[hamradio-commits] [cubicsdr] 01/07: New upstream version 0.2.3+dfsg
Andreas E. Bombe
aeb at moszumanska.debian.org
Thu Jan 25 01:57:48 UTC 2018
This is an automated email from the git hooks/post-receive script.
aeb pushed a commit to annotated tag debian/0.2.3+dfsg-1
in repository cubicsdr.
commit 5c6e0ed7f7b3b49af9d99b7cf0252ef6438ed789
Author: Andreas Bombe <aeb at debian.org>
Date: Wed Jan 24 22:38:47 2018 +0100
New upstream version 0.2.3+dfsg
---
CMakeLists.txt | 12 +-
external/cubicvr2/math/mat3.h | 20 +-
external/cubicvr2/math/mat4.h | 47 +-
external/cubicvr2/math/transform.h | 10 +-
external/cubicvr2/math/vec2.h | 22 +-
external/cubicvr2/math/vec3.h | 18 +-
external/cubicvr2/math/vec4.h | 21 +-
src/AppConfig.cpp | 93 ++++
src/AppConfig.h | 19 +
src/AppFrame.cpp | 623 +++++++++++++++++++----
src/AppFrame.h | 59 ++-
src/BookmarkMgr.cpp | 119 +++--
src/BookmarkMgr.h | 21 +-
src/CubicSDR.cpp | 180 ++++---
src/CubicSDR.h | 36 +-
src/CubicSDRDefs.h | 4 +
src/DemodLabelDialog.cpp | 2 +-
src/DemodLabelDialog.h | 4 +-
src/FrequencyDialog.cpp | 8 +-
src/FrequencyDialog.h | 4 +-
src/IOThread.cpp | 18 +-
src/IOThread.h | 243 ++++-----
src/ModemProperties.cpp | 2 +-
src/ModemProperties.h | 5 +-
src/audio/AudioFile.cpp | 50 ++
src/audio/AudioFile.h | 25 +
src/audio/AudioFileWAV.cpp | 219 ++++++++
src/audio/AudioFileWAV.h | 42 ++
src/audio/AudioSinkFileThread.cpp | 140 +++++
src/audio/AudioSinkFileThread.h | 50 ++
src/audio/AudioSinkThread.cpp | 54 ++
src/audio/AudioSinkThread.h | 25 +
src/audio/AudioThread.cpp | 190 ++++---
src/audio/AudioThread.h | 97 ++--
src/demod/DemodDefs.h | 26 +-
src/demod/DemodulatorInstance.cpp | 211 ++++++--
src/demod/DemodulatorInstance.h | 40 +-
src/demod/DemodulatorMgr.cpp | 190 +++----
src/demod/DemodulatorMgr.h | 54 +-
src/demod/DemodulatorPreThread.cpp | 60 +--
src/demod/DemodulatorPreThread.h | 17 +-
src/demod/DemodulatorThread.cpp | 159 +++---
src/demod/DemodulatorThread.h | 32 +-
src/demod/DemodulatorWorkerThread.cpp | 23 +-
src/demod/DemodulatorWorkerThread.h | 17 +-
src/forms/Bookmark/BookmarkPanel.cpp | 226 ++++----
src/forms/Bookmark/BookmarkPanel.h | 96 ++--
src/forms/Bookmark/BookmarkView.cpp | 110 ++--
src/forms/Bookmark/BookmarkView.h | 12 +-
src/forms/Dialog/AboutDialog.fbp | 415 +++++++++++++++
src/forms/Dialog/AboutDialogBase.cpp | 46 +-
src/forms/Dialog/AboutDialogBase.h | 9 +-
src/forms/Dialog/ActionDialogBase.cpp | 66 +--
src/forms/Dialog/ActionDialogBase.h | 40 +-
src/forms/Dialog/PortSelectorDialogBase.cpp | 112 ++--
src/forms/Dialog/PortSelectorDialogBase.h | 50 +-
src/forms/DigitalConsole/DigitalConsoleFrame.cpp | 104 ++--
src/forms/DigitalConsole/DigitalConsoleFrame.h | 52 +-
src/forms/SDRDevices/SDRDeviceAddForm.cpp | 124 ++---
src/forms/SDRDevices/SDRDeviceAddForm.h | 48 +-
src/forms/SDRDevices/SDRDevices.cpp | 179 +++++--
src/forms/SDRDevices/SDRDevicesForm.cpp | 174 +++----
src/forms/SDRDevices/SDRDevicesForm.h | 76 +--
src/modules/modem/Modem.h | 9 +-
src/modules/modem/analog/ModemAM.cpp | 4 +-
src/modules/modem/analog/ModemDSB.cpp | 2 +-
src/modules/modem/analog/ModemFM.cpp | 2 +-
src/modules/modem/analog/ModemIQ.cpp | 2 +-
src/modules/modem/analog/ModemLSB.cpp | 2 +-
src/modules/modem/analog/ModemNBFM.cpp | 2 +-
src/modules/modem/analog/ModemUSB.cpp | 2 +-
src/panel/MeterPanel.cpp | 2 +-
src/panel/MeterPanel.h | 4 +-
src/panel/ScopePanel.cpp | 14 +-
src/panel/SpectrumPanel.cpp | 4 +-
src/panel/WaterfallPanel.cpp | 2 +-
src/process/FFTDataDistributor.cpp | 15 +-
src/process/FFTVisualDataThread.cpp | 22 +-
src/process/FFTVisualDataThread.h | 6 +-
src/process/ScopeVisualProcessor.cpp | 13 +-
src/process/ScopeVisualProcessor.h | 13 +-
src/process/SpectrumVisualDataThread.cpp | 4 +
src/process/SpectrumVisualDataThread.h | 2 +
src/process/SpectrumVisualProcessor.cpp | 192 ++++---
src/process/SpectrumVisualProcessor.h | 39 +-
src/process/VisualProcessor.h | 167 +++---
src/rig/RigThread.cpp | 4 +-
src/sdr/SDRDeviceInfo.cpp | 94 +++-
src/sdr/SDRDeviceInfo.h | 8 +
src/sdr/SDREnumerator.cpp | 54 +-
src/sdr/SDRPostThread.cpp | 219 ++++----
src/sdr/SDRPostThread.h | 25 +-
src/sdr/SoapySDRThread.cpp | 278 +++++++---
src/sdr/SoapySDRThread.h | 32 +-
src/ui/GLPanel.cpp | 4 +-
src/ui/GLPanel.h | 1 +
src/util/GLFont.cpp | 16 +-
src/util/ThreadBlockingQueue.h | 104 ++--
src/util/ThreadQueue.cpp | 4 -
src/util/ThreadQueue.h | 302 -----------
src/util/Timer.cpp | 120 +++--
src/util/Timer.h | 4 +
src/visual/GainCanvas.cpp | 112 +++-
src/visual/GainCanvas.h | 10 +-
src/visual/InteractiveCanvas.h | 2 +-
src/visual/PrimaryGLContext.cpp | 61 ++-
src/visual/PrimaryGLContext.h | 4 +-
src/visual/ScopeCanvas.cpp | 19 +-
src/visual/ScopeCanvas.h | 5 +-
src/visual/SpectrumCanvas.cpp | 18 +-
src/visual/SpectrumCanvas.h | 5 +-
src/visual/TuningCanvas.cpp | 16 +-
src/visual/WaterfallCanvas.cpp | 70 ++-
src/visual/WaterfallCanvas.h | 7 +-
114 files changed, 4828 insertions(+), 2544 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6a65c6b..d82e51a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,7 +6,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/")
SET(CUBICSDR_VERSION_MAJOR "0")
SET(CUBICSDR_VERSION_MINOR "2")
-SET(CUBICSDR_VERSION_PATCH "2")
+SET(CUBICSDR_VERSION_PATCH "3")
SET(CUBICSDR_VERSION_SUFFIX "")
SET(CUBICSDR_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}${CUBICSDR_VERSION_SUFFIX}")
@@ -347,6 +347,10 @@ SET (cubicsdr_sources
src/modules/modem/analog/ModemLSB.cpp
src/modules/modem/analog/ModemUSB.cpp
src/audio/AudioThread.cpp
+ src/audio/AudioSinkThread.cpp
+ src/audio/AudioSinkFileThread.cpp
+ src/audio/AudioFile.cpp
+ src/audio/AudioFileWAV.cpp
src/util/Gradient.cpp
src/util/Timer.cpp
src/util/MouseTracker.cpp
@@ -451,6 +455,10 @@ SET (cubicsdr_headers
src/modules/modem/analog/ModemLSB.h
src/modules/modem/analog/ModemUSB.h
src/audio/AudioThread.h
+ src/audio/AudioSinkThread.h
+ src/audio/AudioSinkFileThread.h
+ src/audio/AudioFile.h
+ src/audio/AudioFileWAV.h
src/util/Gradient.h
src/util/Timer.h
src/util/ThreadBlockingQueue.h
@@ -995,7 +1003,7 @@ IF (WIN32 AND BUILD_INSTALLER)
IF (MSVC)
install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/external/msvc/${EX_PLATFORM_NAME}/vc_redist.${EX_PLATFORM_NAME}.exe DESTINATION vc_redist)
- set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\vc_redist\\\\vc_redist.${EX_PLATFORM_NAME}.exe\\\" /q:a'")
+ set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\vc_redist\\\\vc_redist.${EX_PLATFORM_NAME}.exe\\\" /passive /norestart'")
ENDIF (MSVC)
diff --git a/external/cubicvr2/math/mat3.h b/external/cubicvr2/math/mat3.h
index 8f5ab22..167d895 100644
--- a/external/cubicvr2/math/mat3.h
+++ b/external/cubicvr2/math/mat3.h
@@ -12,7 +12,7 @@
#include <iostream>
#include "vec3.h"
#include <cstring>
-
+#include <stddef.h>
namespace CubicVR {
#define mat3SG(c,x,y) \
@@ -20,14 +20,20 @@ namespace CubicVR {
c & COMBINE(set,x)(mat3 value) { y = value; return *this; }
struct mat3 {
+
__float a,b,c,d,e,f,g,h,i;
-
- // __float operator [] (unsigned i) const { return ((__float *)this)[i]; }
-#ifndef _WIN32
- __float& operator [] (unsigned i) { return ((__float *)this)[i]; }
-#endif
- operator __float*() const { return (__float *)this; }
+
+ //access as-array:
+ inline __float& operator [] (size_t i) {
+ __float* as_array = (__float*)this;
+ return (as_array[i]);
+ }
+ inline const __float& operator [] (size_t i) const {
+ __float* as_array = (__float*)this;
+ return (as_array[i]);
+ }
+
mat3(__float ai,__float bi,__float ci,__float di,__float ei,__float fi,__float gi,__float hi,__float ii) {
a = ai; b = bi; c = ci; d = di; e = ei; f = fi; g = gi; h = hi; i = ii;
};
diff --git a/external/cubicvr2/math/mat4.h b/external/cubicvr2/math/mat4.h
index 2007535..75d1ec0 100644
--- a/external/cubicvr2/math/mat4.h
+++ b/external/cubicvr2/math/mat4.h
@@ -15,21 +15,35 @@
#include "vec4.h"
#include "mat3.h"
#include <cmath>
+#include <stddef.h>
namespace CubicVR {
using namespace std;
#define mat4SG(c,x,y) \
mat4 COMBINE(get,x)() { return y; } \
c & COMBINE(set,x)(mat4 value) { y = value; return *this; }
+
struct mat4 {
- __float a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p;
+
+ //16 elements
+ __float a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p;
+
+ //access as-array:
+ inline __float& operator [] (size_t i) {
+ __float* as_array = (__float*)this;
+ return (as_array[i]);
+ }
+
+ inline const __float& operator [] (size_t i) const {
+ __float* as_array = (__float*)this;
+ return (as_array[i]);
+ }
- // __float operator [] (unsigned i) const { return ((__float *)this)[i]; }
-#ifndef _WIN32
- __float& operator [] (unsigned i) { return ((__float *)this)[i]; }
-#endif
+ //To be accessed by GL API directly, accessed by pointer.
+ //operator* overloading is way too dangerous, especially in ptr != NULL
+ //tests.
+ inline __float* to_ptr() const { return (__float *)this; }
- operator __float*() const { return (__float *)this; }
mat4(__float ai,__float bi,__float ci,__float di,__float ei,__float fi,__float gi,__float hi,__float ii,__float ji,__float ki,__float li,__float mi,__float ni,__float oi,__float pi) {
a = ai; b = bi; c = ci; d = di; e = ei; f = fi; g = gi; h = hi; i = ii; j = ji; k = ki; l = li; m = mi; n = ni; o = oi; p = pi;
}
@@ -263,22 +277,19 @@ namespace CubicVR {
static mat4 transform(vec3 position, vec3 rotation, vec3 scale) {
mat4 m = mat4::identity();
- if (position!=NULL) {
- m *= mat4::translate(position[0],position[1],position[2]);
- }
- if (rotation!=NULL) {
- if (!(rotation[0] == 0 && rotation[1] == 0 && rotation[2] == 0)) {
- m *= mat4::rotate(rotation[0],rotation[1],rotation[2]);
- }
+ m *= mat4::translate(position[0],position[1],position[2]);
+
+ if (!(rotation[0] == 0 && rotation[1] == 0 && rotation[2] == 0)) {
+ m *= mat4::rotate(rotation[0], rotation[1], rotation[2]);
}
- if (scale!=NULL) {
- if (!(scale[0] == 1 && scale[1] == 1 && scale[2] == 1)) {
- m *= mat4::scale(scale[0],scale[1],scale[2]);
- }
+
+ if (!(scale[0] == 1 && scale[1] == 1 && scale[2] == 1)) {
+ m *= mat4::scale(scale[0],scale[1],scale[2]);
}
return m;
};
+
static vec4 vec4_multiply(vec4 m1, mat4 m2) {
vec4 mOut;
@@ -313,7 +324,7 @@ namespace CubicVR {
};
static vec3 unProject(mat4 pMatrix, mat4 mvMatrix, float width, float height, float winx, float winy, float /* winz */) {
- vec4 p(((winx / width) * 2.0f) - 1.0, -(((winy / height) * 2.0f) - 1.0), 1.0, 1.0);
+ vec4 p(((winx / width) * 2.0f) - 1.0f, -(((winy / height) * 2.0f) - 1.0f), 1.0f, 1.0f);
vec4 invp = mat4::vec4_multiply(mat4::vec4_multiply(p, mat4::inverse(pMatrix)), mat4::inverse(mvMatrix));
diff --git a/external/cubicvr2/math/transform.h b/external/cubicvr2/math/transform.h
index bd6ddde..9037128 100644
--- a/external/cubicvr2/math/transform.h
+++ b/external/cubicvr2/math/transform.h
@@ -73,7 +73,7 @@ namespace CubicVR {
void pushMatrix(mat4 m) {
c_stack++;
- m_stack[c_stack] = (m ? m : getIdentity());
+ m_stack[c_stack] = m;
}
void popMatrix() {
@@ -88,14 +88,10 @@ namespace CubicVR {
m_cache.clear();
c_stack = 0;
valid = 0;
- delete result;
+
result = mat4::identity();
- if (init_mat != NULL) {
- m_stack[0] = init_mat;
- } else {
- setIdentity();
- }
+ m_stack[0] = init_mat;
}
void translate(__float x, __float y, __float z) {
diff --git a/external/cubicvr2/math/vec2.h b/external/cubicvr2/math/vec2.h
index 24011c7..330f09d 100644
--- a/external/cubicvr2/math/vec2.h
+++ b/external/cubicvr2/math/vec2.h
@@ -12,6 +12,7 @@
#include <iostream>
#include <cmath>
#include "cubic_types.h"
+#include <stddef.h>
namespace CubicVR {
#define vec2SG(c,x,y) \
@@ -20,20 +21,27 @@ namespace CubicVR {
struct vec2 {
__float x, y;
+
public:
__float& u() { return x; }
__float& v() { return y; }
-// __float operator [] (unsigned i) const { return ((__float *)this)[i]; }
-#ifndef _WIN32
- __float& operator [] (unsigned i) { return ((__float *)this)[i]; }
-#endif
+ //access as-array:
+ inline __float& operator [] (size_t i) {
+ __float* as_array = (__float*)this;
+ return (as_array[i]);
+ }
+
+ inline const __float& operator [] (size_t i) const {
+ __float* as_array = (__float*)this;
+ return (as_array[i]);
+ }
+
vec2 (__float xi,__float yi) { x = xi; y = yi; }
vec2 () { x = y = 0.0f; }
-
- operator __float*() const { return (__float *)this; }
-
+
vec2 operator*(__float v) { return vec2( x*v, y*v ); }
+
// vec2 operator*(vec2 v) { return vec2::cross(*this,v); }
vec2 operator+(vec2 v) { return vec2::add(*this,v); }
vec2 operator-(vec2 v) { return vec2::subtract(*this,v); }
diff --git a/external/cubicvr2/math/vec3.h b/external/cubicvr2/math/vec3.h
index 220bf60..33bc652 100644
--- a/external/cubicvr2/math/vec3.h
+++ b/external/cubicvr2/math/vec3.h
@@ -12,6 +12,7 @@
#include <iostream>
#include "cubic_types.h"
#include <cmath>
+#include <stddef.h>
namespace CubicVR {
@@ -23,19 +24,24 @@ namespace CubicVR {
struct vec3 {
__float x,y,z;
- operator __float*() const { return (__float *)this; }
-
__float& r() { return x; }
__float& g() { return y; }
__float& b() { return z; }
-#ifndef _WIN32
- __float& operator [] (unsigned i) { return ((__float *)this)[i]; }
-#endif
+ //access as-array:
+ inline __float& operator [] (size_t i) {
+ __float* as_array = (__float*)this;
+ return (as_array[i]);
+ }
+
+ inline const __float& operator [] (size_t i) const {
+ __float* as_array = (__float*)this;
+ return (as_array[i]);
+ }
+
vec3 (__float xi,__float yi,__float zi) { x = xi; y = yi; z = zi; }
vec3 () { x = y = z = 0.0f; }
-
vec3 operator*(__float v) { return vec3(x*v, y*v, z*v); }
vec3 operator*(vec3 v) { return vec3::cross(*this,v); }
vec3 operator+(vec3 v) { return vec3::add(*this,v); }
diff --git a/external/cubicvr2/math/vec4.h b/external/cubicvr2/math/vec4.h
index ad795c1..9983148 100644
--- a/external/cubicvr2/math/vec4.h
+++ b/external/cubicvr2/math/vec4.h
@@ -12,6 +12,7 @@
#include <iostream>
#include "cubic_types.h"
#include <cmath>
+#include <stddef.h>
namespace CubicVR {
@@ -20,23 +21,29 @@ namespace CubicVR {
c & COMBINE(set,x)(vec3 value) { y = value; return *this; }
struct vec4 {
- __float x,y,z,w;
+
+ __float x, y, z, w;
+
public:
__float& r() { return x; }
__float& g() { return y; }
__float& b() { return z; }
__float& a() { return w; }
-// __float operator [] (unsigned i) const { return ((__float *)this)[i]; }
-#ifndef _WIN32
- __float& operator [] (unsigned i) { return ((__float *)this)[i]; }
-#endif
+ //access as-array:
+ inline __float& operator [] (size_t i) {
+ __float* as_array = (__float*)this;
+ return (as_array[i]);
+ }
+
+ inline const __float& operator [] (size_t i) const {
+ __float* as_array = (__float*)this;
+ return (as_array[i]);
+ }
vec4 (__float xi,__float yi,__float zi,__float wi) { x = xi; y = yi; z = zi; w = wi; }
vec4 () { x = y = z = w = 0.0f; }
- operator __float*() const { return (__float *)this; }
-
vec4 operator*(__float v) { return vec4(x*v, y*v, z*v, w*v); }
// vec4 operator*(vec4 v) { return vec4::cross(*this,v); }
// vec4 operator+(vec4 v) { return vec4::add(*this,v); }
diff --git a/src/AppConfig.cpp b/src/AppConfig.cpp
index 176838b..f85ab7c 100644
--- a/src/AppConfig.cpp
+++ b/src/AppConfig.cpp
@@ -4,6 +4,8 @@
#include "AppConfig.h"
#include "CubicSDR.h"
+#include <wx/msgdlg.h>
+
DeviceConfig::DeviceConfig() : deviceId("") {
ppm.store(0);
offset.store(0);
@@ -39,6 +41,14 @@ long DeviceConfig::getSampleRate() {
return sampleRate.load();
}
+void DeviceConfig::setAntennaName(const std::string& name) {
+ antennaName = name;
+}
+
+const std::string& DeviceConfig::getAntennaName() {
+ return antennaName;
+}
+
void DeviceConfig::setAGCMode(bool agcMode) {
this->agcMode.store(agcMode);
}
@@ -81,7 +91,9 @@ std::string DeviceConfig::getDeviceName() {
}
void DeviceConfig::save(DataNode *node) {
+
std::lock_guard < std::mutex > lock(busy_lock);
+
*node->newChild("id") = deviceId;
*node->newChild("name") = deviceName;
*node->newChild("ppm") = ppm.load();
@@ -89,6 +101,10 @@ void DeviceConfig::save(DataNode *node) {
*node->newChild("sample_rate") = sampleRate.load();
*node->newChild("agc_mode") = agcMode.load()?1:0;
+ if (!antennaName.empty()) {
+ *node->newChild("antenna") = antennaName;
+ }
+
if (streamOpts.size()) {
DataNode *streamOptsNode = node->newChild("streamOpts");
for (ConfigSettings::const_iterator opt_i = streamOpts.begin(); opt_i != streamOpts.end(); opt_i++) {
@@ -149,6 +165,12 @@ void DeviceConfig::load(DataNode *node) {
sample_rate_node->element()->get(sampleRateValue);
setSampleRate(sampleRateValue);
}
+ if (node->hasAnother("antenna")) {
+ DataNode *antenna_node = node->getNext("antenna");
+ std::string antennaNameValue;
+ antenna_node->element()->get(antennaNameValue);
+ setAntennaName(antennaNameValue);
+ }
if (node->hasAnother("streamOpts")) {
DataNode *streamOptsNode = node->getNext("streamOpts");
for (int i = 0, iMax = streamOptsNode->numChildren(); i<iMax; i++) {
@@ -485,6 +507,51 @@ bool AppConfig::getBookmarksVisible() {
return bookmarksVisible.load();
}
+void AppConfig::setRecordingPath(std::string recPath) {
+ recordingPath = recPath;
+}
+
+std::string AppConfig::getRecordingPath() {
+ return recordingPath;
+}
+
+bool AppConfig::verifyRecordingPath() {
+ string recPathStr = wxGetApp().getConfig()->getRecordingPath();
+
+ if (recPathStr.empty()) {
+ wxMessageBox( wxT("Recording path is not set. Please use 'Set Recording Path' from the 'File' Menu."), wxT("Recording Path Error"), wxICON_INFORMATION);
+
+ return false;
+ }
+
+ wxFileName recPath(recPathStr);
+
+ if (!recPath.Exists() || !recPath.IsDirWritable()) {
+ wxMessageBox( wxT("Recording path does not exist or is not writable. Please use 'Set Recording Path' from the 'File' Menu."), wxT("Recording Path Error"), wxICON_INFORMATION);
+
+ return false;
+ }
+
+ return true;
+}
+
+
+void AppConfig::setRecordingSquelchOption(int enumChoice) {
+ recordingSquelchOption = enumChoice;
+}
+
+int AppConfig::getRecordingSquelchOption() {
+ return recordingSquelchOption;
+}
+
+void AppConfig::setRecordingFileTimeLimit(int nbSeconds) {
+ recordingFileTimeLimitSeconds = nbSeconds;
+}
+
+int AppConfig::getRecordingFileTimeLimit() {
+ return recordingFileTimeLimitSeconds;
+}
+
void AppConfig::setConfigName(std::string configName) {
this->configName = configName;
@@ -539,6 +606,12 @@ bool AppConfig::save() {
*window_node->newChild("bookmark_visible") = bookmarksVisible.load();
}
+ //Recording settings:
+ DataNode *rec_node = cfg.rootNode()->newChild("recording");
+ *rec_node->newChild("path") = recordingPath;
+ *rec_node->newChild("squelch") = recordingSquelchOption;
+ *rec_node->newChild("file_time_limit") = recordingFileTimeLimitSeconds;
+
DataNode *devices_node = cfg.rootNode()->newChild("devices");
std::map<std::string, DeviceConfig *>::iterator device_config_i;
@@ -721,6 +794,26 @@ bool AppConfig::load() {
}
}
+ //Recording settings:
+ if (cfg.rootNode()->hasAnother("recording")) {
+ DataNode *rec_node = cfg.rootNode()->getNext("recording");
+
+ if (rec_node->hasAnother("path")) {
+ DataNode *rec_path = rec_node->getNext("path");
+ recordingPath = rec_path->element()->toString();
+ }
+
+ if (rec_node->hasAnother("squelch")) {
+ DataNode *rec_squelch = rec_node->getNext("squelch");
+ rec_squelch->element()->get(recordingSquelchOption);
+ }
+
+ if (rec_node->hasAnother("file_time_limit")) {
+ DataNode *rec_file_time_limit = rec_node->getNext("file_time_limit");
+ rec_file_time_limit->element()->get(recordingFileTimeLimitSeconds);
+ }
+ }
+
if (cfg.rootNode()->hasAnother("devices")) {
DataNode *devices_node = cfg.rootNode()->getNext("devices");
diff --git a/src/AppConfig.h b/src/AppConfig.h
index 5d78e78..c1fe392 100644
--- a/src/AppConfig.h
+++ b/src/AppConfig.h
@@ -31,6 +31,9 @@ public:
void setSampleRate(long srate);
long getSampleRate();
+ void setAntennaName(const std::string& name);
+ const std::string& getAntennaName();
+
void setAGCMode(bool agcMode);
bool getAGCMode();
@@ -64,12 +67,14 @@ public:
private:
std::string deviceId;
std::string deviceName;
+
std::mutex busy_lock;
std::atomic_int ppm;
std::atomic_llong offset;
std::atomic_bool agcMode;
std::atomic_long sampleRate;
+ std::string antennaName;
ConfigSettings streamOpts;
ConfigGains gains;
std::map<std::string, std::string> settings;
@@ -133,6 +138,16 @@ public:
void setBookmarksVisible(bool state);
bool getBookmarksVisible();
+ //Recording settings:
+ void setRecordingPath(std::string recPath);
+ std::string getRecordingPath();
+ bool verifyRecordingPath();
+
+ void setRecordingSquelchOption(int enumChoice);
+ int getRecordingSquelchOption();
+
+ void setRecordingFileTimeLimit(int nbSeconds);
+ int getRecordingFileTimeLimit();
#if USE_HAMLIB
int getRigModel();
@@ -180,6 +195,10 @@ private:
std::atomic_int dbOffset;
std::vector<SDRManualDef> manualDevices;
std::atomic_bool bookmarksVisible;
+
+ std::string recordingPath = "";
+ int recordingSquelchOption = 0;
+ int recordingFileTimeLimitSeconds = 0;
#if USE_HAMLIB
std::atomic_int rigModel, rigRate;
std::string rigPort;
diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp
index ca5a8c1..d17a02e 100644
--- a/src/AppFrame.cpp
+++ b/src/AppFrame.cpp
@@ -18,17 +18,21 @@
#include <vector>
#include <algorithm>
-#include "AudioThread.h"
+#include "AudioSinkFileThread.h"
#include "CubicSDR.h"
#include "DataTree.h"
#include "ColorTheme.h"
#include "DemodulatorMgr.h"
#include "ImagePanel.h"
+#include "ActionDialog.h"
#include <thread>
+#include <iostream>
+#include <iomanip>
#include <wx/panel.h>
#include <wx/numformatter.h>
+#include <stddef.h>
#ifdef __linux__
#include "CubicSDR.xpm"
@@ -50,6 +54,22 @@ wxEND_EVENT_TABLE()
#endif
+
+class ActionDialogBookmarkReset : public ActionDialog {
+public:
+ ActionDialogBookmarkReset() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Reset Bookmarks?")) {
+ m_questionText->SetLabelText(wxT("Resetting bookmarks will erase all current bookmarks; are you sure?"));
+ }
+
+ void doClickOK() {
+ wxGetApp().getBookmarkMgr().resetBookmarks();
+ wxGetApp().getBookmarkMgr().updateBookmarks();
+ wxGetApp().getBookmarkMgr().updateActiveList();
+ }
+};
+
+
+
/* split a string by 'seperator' into a vector of string */
std::vector<std::string> str_explode(const std::string &seperator, const std::string &in_str);
@@ -57,7 +77,7 @@ std::vector<std::string> str_explode(const std::string &seperator, const std::st
#define APPFRAME_MODEMPROPS_MAXSIZE 240
AppFrame::AppFrame() :
- wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) {
+ wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(nullptr) {
#ifdef __linux__
SetIcon(wxICON(cubicsdr));
@@ -100,7 +120,7 @@ AppFrame::AppFrame() :
#endif
gainCanvas = new GainCanvas(demodPanel, attribList);
- gainCanvas->setHelpTip("Tuner gains in dB. Click / use Mousewheel to change.");
+ gainCanvas->setHelpTip("Tuner gains, usually in dB. Click / use Mousewheel to change.");
gainSizerItem = demodTray->Add(gainCanvas, 0, wxEXPAND | wxALL, 0);
gainSizerItem->Show(false);
gainSpacerItem = demodTray->AddSpacer(1);
@@ -399,43 +419,13 @@ AppFrame::AppFrame() :
// Make a menubar
menuBar = new wxMenuBar;
- wxMenu *menu = new wxMenu;
-#ifndef __APPLE__
-#ifdef CUBICSDR_ENABLE_ABOUT_DIALOG
- menu->Append(wxID_ABOUT_CUBICSDR, "About " CUBICSDR_INSTALL_NAME);
-#endif
-#endif
- menu->Append(wxID_SDR_DEVICES, "SDR Devices");
- menu->AppendSeparator();
- menu->Append(wxID_SDR_START_STOP, "Stop / Start Device");
- menu->AppendSeparator();
- menu->Append(wxID_OPEN, "&Open Session");
- menu->Append(wxID_SAVE, "&Save Session");
- menu->Append(wxID_SAVEAS, "Save Session &As..");
- menu->AppendSeparator();
- menu->Append(wxID_RESET, "&Reset Session");
-
-#ifndef __APPLE__
- menu->AppendSeparator();
- menu->Append(wxID_CLOSE);
-#else
-#ifdef CUBICSDR_ENABLE_ABOUT_DIALOG
- if ( wxApp::s_macAboutMenuItemId != wxID_NONE ) {
- wxString aboutLabel;
- aboutLabel.Printf(_("About %s"), CUBICSDR_INSTALL_NAME);
- menu->Append( wxApp::s_macAboutMenuItemId, aboutLabel);
- }
-#endif
-#endif
-
- menuBar->Append(menu, wxT("&File"));
+
+ menuBar->Append(makeFileMenu(), wxT("&File"));
settingsMenu = new wxMenu;
menuBar->Append(settingsMenu, wxT("&Settings"));
- menu = new wxMenu;
-
std::vector<RtAudio::DeviceInfo>::iterator devices_i;
std::map<int, RtAudio::DeviceInfo>::iterator mdevices_i;
AudioThread::enumerateDevices(devices);
@@ -469,7 +459,7 @@ AppFrame::AppFrame() :
menuBar->Append(sampleRateMenu, wxT("Sample &Rate"));
// Audio Sample Rates
- menu = new wxMenu;
+ wxMenu *audioSampleRateMenu = new wxMenu;
#define NUM_RATES_DEFAULT 4
unsigned int desired_rates[NUM_RATES_DEFAULT] = { 48000, 44100, 96000, 192000 };
@@ -499,7 +489,7 @@ AppFrame::AppFrame() :
for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) {
int menu_id = wxID_AUDIO_BANDWIDTH_BASE + wxID_AUDIO_DEVICE_MULTIPLIER * mdevices_i->first;
wxMenu *subMenu = new wxMenu;
- menu->AppendSubMenu(subMenu, mdevices_i->second.name, wxT("Description?"));
+ audioSampleRateMenu->AppendSubMenu(subMenu, mdevices_i->second.name, wxT("Description?"));
int j = 0;
for (std::vector<unsigned int>::iterator srate = mdevices_i->second.sampleRates.begin(); srate != mdevices_i->second.sampleRates.end();
@@ -517,7 +507,12 @@ AppFrame::AppFrame() :
}
}
- menuBar->Append(menu, wxT("Audio &Sample Rate"));
+ menuBar->Append(audioSampleRateMenu, wxT("Audio &Sample Rate"));
+
+ //Add a Recording menu
+ menuBar->Append(makeRecordingMenu(), wxT("Recordin&g"));
+ //
+ updateRecordingMenu();
//Add Display menu
displayMenu = new wxMenu;
@@ -526,7 +521,7 @@ AppFrame::AppFrame() :
int fontScale = wxGetApp().getConfig()->getFontScale();
- fontMenu->AppendRadioItem(wxID_DISPLAY_BASE, "Normal")->Check(GLFont::GLFONT_SCALE_NORMAL == fontScale);
+ fontMenu->AppendRadioItem(wxID_DISPLAY_BASE, "Default")->Check(GLFont::GLFONT_SCALE_NORMAL == fontScale);
fontMenu->AppendRadioItem(wxID_DISPLAY_BASE + 1, "1.5x")->Check(GLFont::GLFONT_SCALE_MEDIUM == fontScale);
fontMenu->AppendRadioItem(wxID_DISPLAY_BASE + 2, "2.0x")->Check(GLFont::GLFONT_SCALE_LARGE == fontScale);
@@ -720,6 +715,137 @@ AppFrame::~AppFrame() {
t_FFTData->join();
}
+wxMenu *AppFrame::makeFileMenu() {
+
+ wxMenu *menu = new wxMenu;
+#ifndef __APPLE__
+#ifdef CUBICSDR_ENABLE_ABOUT_DIALOG
+ menu->Append(wxID_ABOUT_CUBICSDR, "About " CUBICSDR_INSTALL_NAME);
+#endif
+#endif
+ menu->Append(wxID_SDR_DEVICES, "SDR Devices");
+ menu->AppendSeparator();
+ menu->Append(wxID_SDR_START_STOP, "Stop / Start Device");
+ menu->AppendSeparator();
+
+ wxMenu *sessionMenu = new wxMenu;
+
+ sessionMenu->Append(wxID_OPEN, "&Open Session");
+ sessionMenu->Append(wxID_SAVE, "&Save Session");
+ sessionMenu->Append(wxID_SAVEAS, "Save Session &As..");
+ sessionMenu->AppendSeparator();
+ sessionMenu->Append(wxID_RESET, "&Reset Session");
+
+ menu->AppendSubMenu(sessionMenu, "Session");
+
+ menu->AppendSeparator();
+
+ wxMenu *bookmarkMenu = new wxMenu;
+
+ bookmarkMenu->Append(wxID_OPEN_BOOKMARKS, "Open Bookmarks");
+ bookmarkMenu->Append(wxID_SAVE_BOOKMARKS, "Save Bookmarks");
+ bookmarkMenu->Append(wxID_SAVEAS_BOOKMARKS, "Save Bookmarks As..");
+ bookmarkMenu->AppendSeparator();
+ bookmarkMenu->Append(wxID_RESET_BOOKMARKS, "Reset Bookmarks");
+
+ menu->AppendSubMenu(bookmarkMenu, "Bookmarks");
+
+#ifndef __APPLE__
+ menu->AppendSeparator();
+ menu->Append(wxID_CLOSE);
+#else
+#ifdef CUBICSDR_ENABLE_ABOUT_DIALOG
+ if (wxApp::s_macAboutMenuItemId != wxID_NONE) {
+ wxString aboutLabel;
+ aboutLabel.Printf(_("About %s"), CUBICSDR_INSTALL_NAME);
+ menu->Append(wxApp::s_macAboutMenuItemId, aboutLabel);
+ }
+#endif
+#endif
+
+ fileMenu = menu;
+
+ return menu;
+}
+
+wxMenu *AppFrame::makeRecordingMenu() {
+
+ recordingMenuItems.clear();
+
+ wxMenu *menu = new wxMenu;
+
+ recordingMenuItems[wxID_RECORDING_PATH] = menu->Append(wxID_RECORDING_PATH, getSettingsLabel("Set Recording Path", "<Not Set>"));
+
+ menu->AppendSeparator();
+
+ //Squelch options as sub-menu:
+ wxMenu *subMenu = new wxMenu;
+ recordingMenuItems[wxID_RECORDING_SQUELCH_BASE] = menu->AppendSubMenu(subMenu, "Squelch");
+
+ recordingMenuItems[wxID_RECORDING_SQUELCH_SILENCE] = subMenu->AppendRadioItem(wxID_RECORDING_SQUELCH_SILENCE, "Record Silence",
+ "Record below squelch-break audio as silence, i.e records as the user may hear.");
+ recordingMenuItems[wxID_RECORDING_SQUELCH_SKIP] = subMenu->AppendRadioItem(wxID_RECORDING_SQUELCH_SKIP, "Skip Silence",
+ "Do not record below squelch-break audio, i.e squelch-break audio parts are packed together.");
+ recordingMenuItems[wxID_RECORDING_SQUELCH_ALWAYS] = subMenu->AppendRadioItem(wxID_RECORDING_SQUELCH_ALWAYS, "Record Always",
+ "Record everything irrespective of the squelch level.");
+
+ recordingMenuItems[wxID_RECORDING_FILE_TIME_LIMIT] = menu->Append(wxID_RECORDING_FILE_TIME_LIMIT, getSettingsLabel("File time limit", "<Not Set>"),
+ "Creates a new file automatically, each time the recording lasts longer than the limit, named according to the current time.");
+
+ recordingMenuItems[wxID_RECORDING_SQUELCH_SILENCE]->Check(true);
+
+ recordingMenu = menu;
+
+ return menu;
+}
+
+void AppFrame::updateRecordingMenu() {
+
+ // Recording path:
+ std::string recPath = wxGetApp().getConfig()->getRecordingPath();
+ if (recPath.length() > 32) {
+ recPath = "..." + recPath.substr(recPath.length() - 32, 32);
+ }
+
+ recordingMenuItems[wxID_RECORDING_PATH]->SetItemLabel(getSettingsLabel("Set Recording Path", recPath.empty() ? "<Not Set>" : recPath));
+
+ //Squelch options:
+ int squelchEnumValue = wxGetApp().getConfig()->getRecordingSquelchOption();
+
+ if (squelchEnumValue == AudioSinkFileThread::SQUELCH_RECORD_SILENCE) {
+
+ recordingMenuItems[wxID_RECORDING_SQUELCH_SILENCE]->Check(true);
+ recordingMenuItems[wxID_RECORDING_SQUELCH_BASE]->SetItemLabel(getSettingsLabel("Squelch", "Record Silence"));
+
+ } else if (squelchEnumValue == AudioSinkFileThread::SQUELCH_SKIP_SILENCE) {
+
+ recordingMenuItems[wxID_RECORDING_SQUELCH_SKIP]->Check(true);
+ recordingMenuItems[wxID_RECORDING_SQUELCH_BASE]->SetItemLabel(getSettingsLabel("Squelch", "Skip Silence"));
+
+ } else if (squelchEnumValue == AudioSinkFileThread::SQUELCH_RECORD_ALWAYS) {
+
+ recordingMenuItems[wxID_RECORDING_SQUELCH_ALWAYS]->Check(true);
+ recordingMenuItems[wxID_RECORDING_SQUELCH_BASE]->SetItemLabel(getSettingsLabel("Squelch", "Record Always"));
+ }
+ else {
+ recordingMenuItems[wxID_RECORDING_SQUELCH_SILENCE]->Check(true);
+ recordingMenuItems[wxID_RECORDING_SQUELCH_BASE]->SetItemLabel(getSettingsLabel("Squelch", "Record Silence"));
+
+ }
+
+ //File time limit:
+ int fileTimeLimitSeconds = wxGetApp().getConfig()->getRecordingFileTimeLimit();
+
+ if (fileTimeLimitSeconds <= 0) {
+
+ recordingMenuItems[wxID_RECORDING_FILE_TIME_LIMIT]->SetItemLabel(getSettingsLabel("File time limit","<Not Set>"));
+ }
+ else {
+ recordingMenuItems[wxID_RECORDING_FILE_TIME_LIMIT]->SetItemLabel(getSettingsLabel("File time limit",
+ std::to_string(fileTimeLimitSeconds), "s"));
+ }
+}
+
void AppFrame::initDeviceParams(SDRDeviceInfo *devInfo) {
this->devInfo = devInfo;
deviceChanged.store(true);
@@ -751,11 +877,13 @@ void AppFrame::updateDeviceParams() {
newSettingsMenu->AppendSeparator();
- newSettingsMenu->Append(wxID_SET_DB_OFFSET, "Power Level Offset");
- newSettingsMenu->Append(wxID_SET_FREQ_OFFSET, "Frequency Offset");
+ settingsMenuItems.clear();
+
+ settingsMenuItems[wxID_SET_DB_OFFSET] = newSettingsMenu->Append(wxID_SET_DB_OFFSET, getSettingsLabel("Power Level Offset", std::to_string(wxGetApp().getConfig()->getDBOffset()), "dB"));
+ settingsMenuItems[wxID_SET_FREQ_OFFSET] = newSettingsMenu->Append(wxID_SET_FREQ_OFFSET, getSettingsLabel("Frequency Offset", std::to_string(wxGetApp().getOffset() / 1000 ) , "KHz"));
if (devInfo->hasCORR(SOAPY_SDR_RX, 0)) {
- newSettingsMenu->Append(wxID_SET_PPM, "Device PPM");
+ settingsMenuItems[wxID_SET_PPM] = newSettingsMenu->Append(wxID_SET_PPM, getSettingsLabel("Device PPM", std::to_string(wxGetApp().getPPM()) , "ppm"));
}
if (devInfo->getDriver() != "rtlsdr") {
@@ -770,47 +898,107 @@ void AppFrame::updateDeviceParams() {
} else if (!wxGetApp().getAGCMode()) {
wxGetApp().setAGCMode(true);
}
-
+
+ //Add an Antenna menu if more than one (RX) antenna, to keep the UI free of useless entries
+ antennaNames.clear();
+ antennaMenuItems.clear();
+ std::vector<std::string> availableAntennas = devInfo->getAntennaNames(SOAPY_SDR_RX, 0);
+
+ if (availableAntennas.size() > 1) {
+
+ newSettingsMenu->AppendSeparator();
+
+ antennaNames = availableAntennas;
+
+ wxMenu *subMenu = new wxMenu;
+
+ int i = 0;
+ std::string antennaChecked;
+ for (std::string currentAntenna : availableAntennas) {
+
+ antennaMenuItems[wxID_ANTENNAS_BASE + i] = subMenu->AppendRadioItem(wxID_ANTENNAS_BASE + i, currentAntenna);
+
+ if (wxGetApp().getAntennaName() == currentAntenna) {
+ antennaMenuItems[wxID_ANTENNAS_BASE + i]->Check(true);
+ antennaChecked = currentAntenna;
+ }
+
+ i++;
+ }
+ antennaMenuItems[wxID_ANTENNA_CURRENT] = newSettingsMenu->AppendSubMenu(subMenu, "Antenna");
+
+ //Change the Antenna label to indicate the current antenna.
+ if (!antennaChecked.empty()) {
+
+ antennaMenuItems[wxID_ANTENNA_CURRENT]->SetItemLabel(getSettingsLabel("Antenna", antennaChecked));
+ }
+ }
+
+ //Add an informative, read-only menu entry to display the current TX selected antenna, if any.
+ if (devInfo->getAntennaNames(SOAPY_SDR_TX, 0).size() > 1) {
+
+ currentTXantennaName = devInfo->getAntennaName(SOAPY_SDR_TX, 0);
+
+ newSettingsMenu->AppendSeparator();
+
+ antennaMenuItems[wxID_ANTENNA_CURRENT_TX] = newSettingsMenu->Append(wxID_ANTENNA_CURRENT_TX, getSettingsLabel("TX Antenna", currentTXantennaName));
+ antennaMenuItems[wxID_ANTENNA_CURRENT_TX]->Enable(false);
+ }
+
+ //Runtime settings part
SoapySDR::ArgInfoList::const_iterator args_i;
settingArgs = soapyDev->getSettingInfo();
if (settingArgs.size()) {
newSettingsMenu->AppendSeparator();
}
-
+ //for each Runtime option of index i:
for (args_i = settingArgs.begin(); args_i != settingArgs.end(); args_i++) {
+
SoapySDR::ArgInfo arg = (*args_i);
+
std::string currentVal = soapyDev->readSetting(arg.key);
- if (arg.type == SoapySDR::ArgInfo::BOOL) {
+
+ if (arg.type == SoapySDR::ArgInfo::BOOL) {
wxMenuItem *item = newSettingsMenu->AppendCheckItem(wxID_SETTINGS_BASE+i, arg.name, arg.description);
item->Check(currentVal=="true");
i++;
} else if (arg.type == SoapySDR::ArgInfo::INT) {
- newSettingsMenu->Append(wxID_SETTINGS_BASE+i, arg.name, arg.description);
+
+ settingsMenuItems[wxID_SETTINGS_BASE + i] = newSettingsMenu->Append(wxID_SETTINGS_BASE + i, getSettingsLabel(arg.name, currentVal, arg.units), arg.description);
i++;
} else if (arg.type == SoapySDR::ArgInfo::FLOAT) {
- newSettingsMenu->Append(wxID_SETTINGS_BASE+i, arg.name, arg.description);
+ settingsMenuItems[wxID_SETTINGS_BASE + i] = newSettingsMenu->Append(wxID_SETTINGS_BASE + i, getSettingsLabel(arg.name, currentVal, arg.units), arg.description);
i++;
} else if (arg.type == SoapySDR::ArgInfo::STRING) {
if (arg.options.size()) {
wxMenu *subMenu = new wxMenu;
int j = 0;
- for (std::vector<std::string>::iterator str_i = arg.options.begin(); str_i != arg.options.end(); str_i++) {
- std::string optName = (*str_i);
+ std::vector<int> subItemsIds;
+ //for each of this options
+ for (std::string optName : arg.options) {
+ //by default the option name is the same as the displayed name.
std::string displayName = optName;
- if (arg.optionNames.size()) {
+
+ if (arg.optionNames.size()) {
displayName = arg.optionNames[j];
}
wxMenuItem *item = subMenu->AppendRadioItem(wxID_SETTINGS_BASE+i, displayName);
- if (currentVal == (*str_i)) {
+ subItemsIds.push_back(wxID_SETTINGS_BASE + i);
+
+ if (currentVal == optName) {
item->Check(true);
}
j++;
i++;
}
- newSettingsMenu->AppendSubMenu(subMenu, arg.name, arg.description);
+ settingsMenuItems[wxID_SETTINGS_BASE + i] = newSettingsMenu->AppendSubMenu(subMenu, getSettingsLabel(arg.name, currentVal, arg.units), arg.description);
+ //map subitems ids to their parent item !
+ for (int currentSubId : subItemsIds) {
+ settingsMenuItems[currentSubId] = settingsMenuItems[wxID_SETTINGS_BASE + i];
+ }
} else {
- newSettingsMenu->Append(wxID_SETTINGS_BASE+i, arg.name, arg.description);
+ settingsMenuItems[wxID_SETTINGS_BASE + i] = newSettingsMenu->Append(wxID_SETTINGS_BASE + i, getSettingsLabel(arg.name, currentVal, arg.units), arg.description);
i++;
}
}
@@ -876,7 +1064,6 @@ void AppFrame::updateDeviceParams() {
newSampleRateMenu->AppendSeparator();
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL_DIALOG] = newSampleRateMenu->Append(wxID_BANDWIDTH_MANUAL_DIALOG, wxT("Manual Entry..."));
-
menuBar->Replace(2, newSampleRateMenu, wxT("Sample &Rate"));
sampleRateMenu = newSampleRateMenu;
@@ -885,9 +1072,6 @@ void AppFrame::updateDeviceParams() {
gainSizerItem->Show(true);
gainSizerItem->SetMinSize(devInfo->getSoapyDevice()->listGains(SOAPY_SDR_RX,0).size()*50,0);
demodTray->Layout();
- gainCanvas->updateGainUI();
- gainCanvas->Refresh();
- gainCanvas->Refresh();
} else {
gainSpacerItem->Show(false);
gainSizerItem->Show(false);
@@ -1060,6 +1244,7 @@ bool AppFrame::actionOnMenuReset(wxCommandEvent& event) {
SetTitle(CUBICSDR_TITLE);
currentSessionFile = "";
+ currentBookmarkFile = "";
bookmarkSplitter->Unsplit(bookmarkView);
bookmarkSplitter->SplitVertically(bookmarkView, mainVisSplitter, wxGetApp().getConfig()->getBookmarkSplit());
hideBookmarksItem->Check(false);
@@ -1098,7 +1283,14 @@ bool AppFrame::actionOnMenuAbout(wxCommandEvent& event) {
bool AppFrame::actionOnMenuSettings(wxCommandEvent& event) {
- if (event.GetId() >= wxID_SETTINGS_BASE && event.GetId() < settingsIdMax) {
+ if (event.GetId() >= wxID_ANTENNAS_BASE && event.GetId() < wxID_ANTENNAS_BASE + antennaNames.size()) {
+
+ wxGetApp().setAntennaName(antennaNames[event.GetId() - wxID_ANTENNAS_BASE]);
+
+ antennaMenuItems[wxID_ANTENNA_CURRENT]->SetItemLabel(getSettingsLabel("Antenna", wxGetApp().getAntennaName()));
+ return true;
+ }
+ else if (event.GetId() >= wxID_SETTINGS_BASE && event.GetId() < settingsIdMax) {
int setIdx = event.GetId() - wxID_SETTINGS_BASE;
int menuIdx = 0;
@@ -1109,6 +1301,9 @@ bool AppFrame::actionOnMenuSettings(wxCommandEvent& event) {
if (arg.type == SoapySDR::ArgInfo::STRING && arg.options.size() && setIdx >= menuIdx && setIdx < menuIdx + (int)arg.options.size()) {
int optIdx = setIdx - menuIdx;
wxGetApp().getSDRThread()->writeSetting(arg.key, arg.options[optIdx]);
+
+ //update parent menu item label to display the current value
+ settingsMenuItems[menuIdx + wxID_SETTINGS_BASE]->SetItemLabel(getSettingsLabel(arg.name, arg.options[optIdx], arg.units));
break;
}
else if (arg.type == SoapySDR::ArgInfo::STRING && arg.options.size()) {
@@ -1121,6 +1316,9 @@ bool AppFrame::actionOnMenuSettings(wxCommandEvent& event) {
}
else if (arg.type == SoapySDR::ArgInfo::STRING) {
wxString stringVal = wxGetTextFromUser(arg.description, arg.name, wxGetApp().getSDRThread()->readSetting(arg.key));
+
+ settingsMenuItems[menuIdx + wxID_SETTINGS_BASE]->SetItemLabel(getSettingsLabel(arg.name, stringVal.ToStdString(), arg.units));
+
if (stringVal.ToStdString() != "") {
wxGetApp().getSDRThread()->writeSetting(arg.key, stringVal.ToStdString());
}
@@ -1135,6 +1333,9 @@ bool AppFrame::actionOnMenuSettings(wxCommandEvent& event) {
currentVal = 0;
}
int intVal = wxGetNumberFromUser(arg.description, arg.units, arg.name, currentVal, arg.range.minimum(), arg.range.maximum(), this);
+
+ settingsMenuItems[menuIdx + wxID_SETTINGS_BASE]->SetItemLabel(getSettingsLabel(arg.name, std::to_string(intVal), arg.units));
+
if (intVal != -1) {
wxGetApp().getSDRThread()->writeSetting(arg.key, std::to_string(intVal));
}
@@ -1148,6 +1349,7 @@ bool AppFrame::actionOnMenuSettings(wxCommandEvent& event) {
catch (std::invalid_argument e) {
// ...
}
+ settingsMenuItems[menuIdx + wxID_SETTINGS_BASE]->SetItemLabel(getSettingsLabel(arg.name, floatVal.ToStdString(), arg.units));
break;
}
else {
@@ -1158,7 +1360,7 @@ bool AppFrame::actionOnMenuSettings(wxCommandEvent& event) {
menuIdx++;
}
} //end for
-
+
return true;
}
@@ -1282,7 +1484,6 @@ bool AppFrame::actionOnMenuAudioSampleRate(wxCommandEvent& event) {
}
i++;
}
-
}
return false;
@@ -1324,9 +1525,153 @@ bool AppFrame::actionOnMenuLoadSave(wxCommandEvent& event) {
return true;
}
+ //save mecanic for bookmark files
+ else if (event.GetId() == wxID_SAVE_BOOKMARKS) {
+
+ if (!currentBookmarkFile.empty()) {
+ wxGetApp().getBookmarkMgr().saveToFile(currentBookmarkFile, false, true);
+ }
+ else {
+ wxFileDialog saveFileDialog(this, _("Save XML Bookmark file"), "", "", "XML files (*.xml)|*.xml", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
+ if (saveFileDialog.ShowModal() == wxID_CANCEL) {
+ return true;
+ }
+
+ // Make sure the file name actually ends in .xml
+ std::string fileName = saveFileDialog.GetPath().ToStdString();
+ std::string lcFileName = fileName;
+
+ std::transform(lcFileName.begin(), lcFileName.end(), lcFileName.begin(), ::tolower);
+
+ if (lcFileName.find_last_of(".xml") != lcFileName.length() - 1) {
+ fileName.append(".xml");
+ }
+
+ wxGetApp().getBookmarkMgr().saveToFile(fileName, false, true);
+ currentBookmarkFile = fileName;
+ }
+
+ return true;
+ }
+ else if (event.GetId() == wxID_OPEN_BOOKMARKS) {
+
+ wxFileDialog openFileDialog(this, _("Open XML Bookmark file"), "", "", "XML files (*.xml)|*.xml", wxFD_OPEN | wxFD_FILE_MUST_EXIST);
+ if (openFileDialog.ShowModal() == wxID_CANCEL) {
+ return true;
+ }
+ if (wxGetApp().getBookmarkMgr().loadFromFile(openFileDialog.GetPath().ToStdString(), false, true)) {
+
+ wxGetApp().getBookmarkMgr().updateBookmarks();
+ wxGetApp().getBookmarkMgr().updateActiveList();
+
+ currentBookmarkFile = openFileDialog.GetPath().ToStdString();
+ }
+ else {
+ //failure at loading.
+ currentBookmarkFile = "";
+ }
+
+ return true;
+ }
+ else if (event.GetId() == wxID_SAVEAS_BOOKMARKS) {
+
+ wxFileDialog saveFileDialog(this, _("Save XML Bookmark file"), "", "", "XML files (*.xml)|*.xml", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
+ if (saveFileDialog.ShowModal() == wxID_CANCEL) {
+ return true;
+ }
+
+ // Make sure the file name actually ends in .xml
+ std::string fileName = saveFileDialog.GetPath().ToStdString();
+ std::string lcFileName = fileName;
+
+ std::transform(lcFileName.begin(), lcFileName.end(), lcFileName.begin(), ::tolower);
+
+ if (lcFileName.find_last_of(".xml") != lcFileName.length() - 1) {
+ fileName.append(".xml");
+ }
+
+ wxGetApp().getBookmarkMgr().saveToFile(fileName, false, true);
+ currentBookmarkFile = fileName;
+
+ return true;
+ }
+ else if (event.GetId() == wxID_RESET_BOOKMARKS) {
+
+ ActionDialog::showDialog(new ActionDialogBookmarkReset());
+
+ return true;
+ }
+
return false;
}
+bool AppFrame::actionOnMenuRecording(wxCommandEvent& event) {
+
+ if (event.GetId() == wxID_RECORDING_PATH) {
+
+ std::string recPath = wxGetApp().getConfig()->getRecordingPath();
+
+ wxDirDialog recPathDialog(this, _("File Path for Recordings"), recPath, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
+ if (recPathDialog.ShowModal() == wxID_CANCEL) {
+ return true;
+ }
+
+ wxGetApp().getConfig()->setRecordingPath(recPathDialog.GetPath().ToStdString());
+
+ updateRecordingMenu();
+ return true;
+
+ }
+ else if (event.GetId() == wxID_RECORDING_SQUELCH_SILENCE) {
+
+ wxGetApp().getConfig()->setRecordingSquelchOption(AudioSinkFileThread::SQUELCH_RECORD_SILENCE);
+
+ updateRecordingMenu();
+ return true;
+ }
+ else if (event.GetId() == wxID_RECORDING_SQUELCH_SKIP) {
+
+ wxGetApp().getConfig()->setRecordingSquelchOption(AudioSinkFileThread::SQUELCH_SKIP_SILENCE);
+
+ updateRecordingMenu();
+ return true;
+ }
+ else if (event.GetId() == wxID_RECORDING_SQUELCH_ALWAYS) {
+
+ wxGetApp().getConfig()->setRecordingSquelchOption(AudioSinkFileThread::SQUELCH_RECORD_ALWAYS);
+
+ updateRecordingMenu();
+ return true;
+ }
+ else if (event.GetId() == wxID_RECORDING_FILE_TIME_LIMIT) {
+
+ int currentFileLimitSeconds = wxGetApp().getConfig()->getRecordingFileTimeLimit();
+
+ long newFileLimit = wxGetNumberFromUser(wxString("\nFile time limit:\n") +
+ "\nCreates a new file automatically, each time the recording lasts longer than the limit, named according to the current time.\n\n " +
+ + "min: 0 s (no limit)"
+ + ", max: 36000 s (10 hours)\n",
+ "Time in seconds",
+ "File Time Limit",
+ //If a manual sample rate has already been input, recall this one.
+ currentFileLimitSeconds > 0 ? currentFileLimitSeconds : 0,
+ 0,
+ 36000,
+ this);
+
+ if (newFileLimit != -1) {
+
+ wxGetApp().getConfig()->setRecordingFileTimeLimit((int)newFileLimit);
+
+ updateRecordingMenu();
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
bool AppFrame::actionOnMenuRig(wxCommandEvent& event) {
bool bManaged = false;
@@ -1498,10 +1843,13 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
wxGetApp().getSDRThread()->setIQSwap(!wxGetApp().getSDRThread()->getIQSwap());
}
else if (event.GetId() == wxID_SET_FREQ_OFFSET) {
- long ofs = wxGetNumberFromUser("Shift the displayed frequency by this amount.\ni.e. -125000000 for -125 MHz", "Frequency (Hz)",
- "Frequency Offset", wxGetApp().getOffset(), -2000000000, 2000000000, this);
+ //enter in KHz to accomodate > 2GHz shifts for down/upconverters on 32 bit platforms.
+ long ofs = wxGetNumberFromUser("Shift the displayed frequency by this amount of KHz.\ni.e. -125000 for -125 MHz", "Frequency (KHz)",
+ "Frequency Offset", (long long)(wxGetApp().getOffset() / 1000.0) , -2000000000, 2000000000, this);
if (ofs != -1) {
- wxGetApp().setOffset(ofs);
+ wxGetApp().setOffset((long long) ofs * 1000);
+
+ settingsMenuItems[wxID_SET_FREQ_OFFSET]->SetItemLabel(getSettingsLabel("Frequency Offset", std::to_string(wxGetApp().getOffset() / 1000), "KHz"));
}
}
else if (event.GetId() == wxID_SET_DB_OFFSET) {
@@ -1509,6 +1857,7 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
"Power Level Offset", wxGetApp().getConfig()->getDBOffset(), -1000, 1000, this);
if (ofs != -1) {
wxGetApp().getConfig()->setDBOffset(ofs);
+ settingsMenuItems[wxID_SET_DB_OFFSET]->SetItemLabel(getSettingsLabel("Power Level Offset", std::to_string(wxGetApp().getConfig()->getDBOffset()), "dB"));
}
}
else if (actionOnMenuAGC(event)) {
@@ -1521,6 +1870,8 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
long ofs = wxGetNumberFromUser("Frequency correction for device in PPM.\ni.e. -51 for -51 PPM\n\nNote: you can adjust PPM interactively\nby holding ALT over the frequency tuning bar.\n", "Parts per million (PPM)",
"Frequency Correction", wxGetApp().getPPM(), -1000, 1000, this);
wxGetApp().setPPM(ofs);
+
+ settingsMenuItems[wxID_SET_PPM]->SetItemLabel(getSettingsLabel("Device PPM", std::to_string(wxGetApp().getPPM()), "ppm"));
}
else if (actionOnMenuLoadSave(event)) {
return;
@@ -1540,9 +1891,12 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
else if (actionOnMenuAudioSampleRate(event)) {
return;
}
- else if (actionOnMenuDisplay(event)) {
+ else if (actionOnMenuRecording(event)) {
return;
}
+ else if (actionOnMenuDisplay(event)) {
+ return;
+ }
//Optional : Rig
else if (actionOnMenuRig(event)) {
return;
@@ -1611,8 +1965,18 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
if (deviceChanged.load()) {
updateDeviceParams();
}
+
+ //Refresh the current TX antenna on, if any:
+ if ((antennaMenuItems.find(wxID_ANTENNA_CURRENT_TX) != antennaMenuItems.end()) && devInfo) {
+ std::string actualTxAntenna = devInfo->getAntennaName(SOAPY_SDR_TX, 0);
+
+ if (currentTXantennaName != actualTxAntenna) {
+ currentTXantennaName = actualTxAntenna;
+ antennaMenuItems[wxID_ANTENNA_CURRENT_TX]->SetItemLabel(getSettingsLabel("TX Antenna", currentTXantennaName));
+ }
+ }
- DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
+ DemodulatorInstancePtr demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
if (demod && demod->isModemInitialized()) {
if (demod->isTracking()) {
@@ -1637,7 +2001,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
wxGetApp().getDemodMgr().setLastBandwidth(demod->getBandwidth());
}
- if (demod != activeDemodulator) {
+ if (demod.get() != activeDemodulator) {
demodSignalMeter->setInputValue(demod->getSquelchLevel());
demodGainMeter->setInputValue(demod->getGain());
wxGetApp().getDemodMgr().setLastGain(demod->getGain());
@@ -1781,7 +2145,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
demod->setGain(demodGainMeter->getInputValue());
demodGainMeter->setLevel(demodGainMeter->getInputValue());
}
- activeDemodulator = demod;
+ activeDemodulator = demod.get();
} else if (demod) {
// Wait state for current demodulator modem to activate..
} else {
@@ -2010,13 +2374,15 @@ void AppFrame::OnAboutDialogClose(wxCommandEvent& event) {
}
void AppFrame::saveSession(std::string fileName) {
+
DataTree s("cubicsdr_session");
DataNode *header = s.rootNode()->newChild("header");
//save as wstring to prevent problems
header->newChild("version")->element()->set(wxString(CUBICSDR_VERSION).ToStdWstring());
*header->newChild("center_freq") = wxGetApp().getFrequency();
- *header->newChild("sample_rate") = wxGetApp().getSampleRate();
+ *header->newChild("sample_rate") = wxGetApp().getSampleRate();
+ *header->newChild("solo_mode") = wxGetApp().getSoloMode()?1:0;
if (waterfallCanvas->getViewState()) {
DataNode *viewState = header->newChild("view_state");
@@ -2027,11 +2393,12 @@ void AppFrame::saveSession(std::string fileName) {
DataNode *demods = s.rootNode()->newChild("demodulators");
- std::vector<DemodulatorInstance *> &instances = wxGetApp().getDemodMgr().getDemodulators();
+ //make a local copy snapshot of the list
+ std::vector<DemodulatorInstancePtr> instances = wxGetApp().getDemodMgr().getDemodulators();
- for (auto instance_i : instances) {
+ for (auto instance : instances) {
DataNode *demod = demods->newChild("demodulator");
- wxGetApp().getDemodMgr().saveInstance(demod, instance_i);
+ wxGetApp().getDemodMgr().saveInstance(demod, instance);
} //end for demodulators
// Make sure the file name actually ends in .xml
@@ -2051,11 +2418,17 @@ void AppFrame::saveSession(std::string fileName) {
}
bool AppFrame::loadSession(std::string fileName) {
+
DataTree l;
if (!l.LoadFromFileXML(fileName)) {
return false;
}
+ //Check if it is a session file, read the root node.
+ if (l.rootNode()->getName() != "cubicsdr_session") {
+ return false;
+ }
+
wxGetApp().getDemodMgr().setActiveDemodulator(nullptr, false);
wxGetApp().getDemodMgr().terminateAll();
@@ -2134,14 +2507,24 @@ bool AppFrame::loadSession(std::string fileName) {
}
}
- DemodulatorInstance *loadedActiveDemod = nullptr;
- DemodulatorInstance *newDemod = nullptr;
+ if (header->hasAnother("solo_mode")) {
+
+ int solo_mode_activated = *header->getNext("solo_mode");
+
+ wxGetApp().setSoloMode((solo_mode_activated > 0) ? true : false);
+ }
+ else {
+ wxGetApp().setSoloMode(false);
+ }
+
+ DemodulatorInstancePtr loadedActiveDemod = nullptr;
+ DemodulatorInstancePtr newDemod = nullptr;
if (l.rootNode()->hasAnother("demodulators")) {
DataNode *demodulators = l.rootNode()->getNext("demodulators");
- std::vector<DemodulatorInstance *> demodsLoaded;
+ std::vector<DemodulatorInstancePtr> demodsLoaded;
while (demodulators->hasAnother("demodulator")) {
DataNode *demod = demodulators->getNext("demodulator");
@@ -2162,7 +2545,7 @@ bool AppFrame::loadSession(std::string fileName) {
}
if (demodsLoaded.size()) {
- wxGetApp().bindDemodulators(&demodsLoaded);
+ wxGetApp().notifyDemodulatorsChanged();
}
} // if l.rootNode()->hasAnother("demodulators")
@@ -2291,14 +2674,14 @@ FrequencyDialog::FrequencyDialogTarget AppFrame::getFrequencyDialogTarget() {
return target;
}
-void AppFrame::gkNudgeLeft(DemodulatorInstance *demod, int snap) {
+void AppFrame::gkNudgeLeft(DemodulatorInstancePtr demod, int snap) {
if (demod) {
demod->setFrequency(demod->getFrequency()-snap);
demod->updateLabel(demod->getFrequency());
}
}
-void AppFrame::gkNudgeRight(DemodulatorInstance *demod, int snap) {
+void AppFrame::gkNudgeRight(DemodulatorInstancePtr demod, int snap) {
if (demod) {
demod->setFrequency(demod->getFrequency()+snap);
demod->updateLabel(demod->getFrequency());
@@ -2324,7 +2707,10 @@ int AppFrame::OnGlobalKeyDown(wxKeyEvent &event) {
return -1;
}
- DemodulatorInstance *demod = nullptr, *lastDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
+ DemodulatorInstancePtr demod = nullptr;
+
+ DemodulatorInstancePtr lastDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
+
int snap = wxGetApp().getFrequencySnap();
if (event.ControlDown()) {
@@ -2375,6 +2761,7 @@ int AppFrame::OnGlobalKeyDown(wxKeyEvent &event) {
case 'S':
case 'P':
case 'M':
+ case 'R':
return 1;
case '0':
case '1':
@@ -2449,8 +2836,8 @@ int AppFrame::OnGlobalKeyUp(wxKeyEvent &event) {
return 1;
}
- DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator();
- DemodulatorInstance *lastDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
+ DemodulatorInstancePtr activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator();
+ DemodulatorInstancePtr lastDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
#ifdef wxHAS_RAW_KEY_CODES
switch (event.GetRawKeyCode()) {
@@ -2515,6 +2902,13 @@ int AppFrame::OnGlobalKeyUp(wxKeyEvent &event) {
wxGetApp().setSoloMode(!wxGetApp().getSoloMode());
return 1;
break;
+ case 'R':
+ if (event.ShiftDown()) {
+ toggleAllActiveDemodRecording();
+ } else {
+ toggleActiveDemodRecording();
+ }
+ break;
case 'P':
wxGetApp().getSpectrumProcessor()->setPeakHold(!wxGetApp().getSpectrumProcessor()->getPeakHold());
if (wxGetApp().getDemodSpectrumProcessor()) {
@@ -2559,6 +2953,43 @@ int AppFrame::OnGlobalKeyUp(wxKeyEvent &event) {
return 1;
}
+void AppFrame::toggleActiveDemodRecording() {
+ if (!wxGetApp().getConfig()->verifyRecordingPath()) {
+ return;
+ }
+
+ DemodulatorInstancePtr activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator();
+
+ if (activeDemod) {
+ activeDemod->setRecording(!activeDemod->isRecording());
+ wxGetApp().getBookmarkMgr().updateActiveList();
+ }
+}
+
+void AppFrame::toggleAllActiveDemodRecording() {
+ if (!wxGetApp().getConfig()->verifyRecordingPath()) {
+ return;
+ }
+
+ auto activeDemods = wxGetApp().getDemodMgr().getDemodulators();
+
+ bool stateToSet = true;
+
+ for (auto i : activeDemods) {
+ if (i->isActive() && i->isRecording()) {
+ stateToSet = false;
+ break;
+ }
+ }
+
+ for (auto i : activeDemods) {
+ if (i->isActive() && i->isRecording() != stateToSet) {
+ i->setRecording(stateToSet);
+ }
+ }
+}
+
+
void AppFrame::setWaterfallLinesPerSecond(int lps) {
waterfallSpeedMeter->setUserInputValue(sqrt(lps));
@@ -2573,7 +3004,7 @@ void AppFrame::setViewState(long long center_freq, int bandwidth) {
waterfallCanvas->setView(center_freq, bandwidth);
}
-void AppFrame::setViewState(long long center_freq) {
+void AppFrame::setViewState() {
spectrumCanvas->setCenterFrequency(wxGetApp().getFrequency());
waterfallCanvas->setCenterFrequency(wxGetApp().getFrequency());
spectrumCanvas->disableView();
@@ -2597,17 +3028,21 @@ std::vector<std::string> str_explode(const std::string &seperator, const std::st
{
std::vector<std::string> vect_out;
- int i = 0, j = 0;
- int seperator_len = seperator.length();
- int str_len = in_str.length();
+ size_t i = 0, j = 0;
+ size_t seperator_len = seperator.length();
+ size_t str_len = in_str.length();
while(i < str_len)
{
j = in_str.find_first_of(seperator,i);
- if (j == std::string::npos && i < str_len) j = str_len;
+ if (j == std::string::npos && i < str_len) {
+ j = str_len;
+ }
- if (j == std::string::npos) break;
+ if (j == std::string::npos) {
+ break;
+ }
vect_out.push_back(in_str.substr(i,j-i));
@@ -2640,3 +3075,19 @@ void AppFrame::setStatusText(std::string statusText, int value) {
GetStatusBar()->SetStatusText(
wxString::Format(statusText.c_str(), wxNumberFormatter::ToString((long)value, wxNumberFormatter::Style_WithThousandsSep)));
}
+
+wxString AppFrame::getSettingsLabel(const std::string& settingsName,
+ const std::string& settingsValue,
+ const std::string& settingsSuffix) {
+
+ size_t itemStringSize = 30;
+ int justifValueSize = itemStringSize - settingsName.length() - 1;
+
+ std::stringstream full_label;
+
+ full_label << settingsName + " : ";
+ full_label << std::right << std::setw(justifValueSize);
+ full_label << settingsValue + " " + settingsSuffix;
+
+ return wxString(full_label.str());
+}
diff --git a/src/AppFrame.h b/src/AppFrame.h
index 2f656f3..03344ae 100644
--- a/src/AppFrame.h
+++ b/src/AppFrame.h
@@ -26,7 +26,8 @@
#include "FrequencyDialog.h"
#include "BookmarkView.h"
#include "AboutDialog.h"
-
+#include "DemodulatorInstance.h"
+#include "DemodulatorThread.h"
#include <map>
#define wxID_RT_AUDIO_DEVICE 1000
@@ -42,6 +43,11 @@
#define wxID_SET_DB_OFFSET 2012
#define wxID_ABOUT_CUBICSDR 2013
+#define wxID_OPEN_BOOKMARKS 2020
+#define wxID_SAVE_BOOKMARKS 2021
+#define wxID_SAVEAS_BOOKMARKS 2022
+#define wxID_RESET_BOOKMARKS 2023
+
#define wxID_MAIN_SPLITTER 2050
#define wxID_VIS_SPLITTER 2051
#define wxID_BM_SPLITTER 2052
@@ -64,8 +70,19 @@
#define wxID_SETTINGS_BASE 2300
+#define wxID_ANTENNA_CURRENT 2500
+#define wxID_ANTENNA_CURRENT_TX 2501
+#define wxID_ANTENNAS_BASE 2502
+
#define wxID_DEVICE_ID 3500
+#define wxID_RECORDING_PATH 8500
+#define wxID_RECORDING_SQUELCH_BASE 8501
+#define wxID_RECORDING_SQUELCH_SILENCE 8502
+#define wxID_RECORDING_SQUELCH_SKIP 8503
+#define wxID_RECORDING_SQUELCH_ALWAYS 8504
+#define wxID_RECORDING_FILE_TIME_LIMIT 8505
+
#define wxID_AUDIO_BANDWIDTH_BASE 9000
#define wxID_AUDIO_DEVICE_MULTIPLIER 50
@@ -91,6 +108,11 @@ public:
AppFrame();
~AppFrame();
+ wxMenu *makeFileMenu();
+
+ wxMenu *makeRecordingMenu();
+ void updateRecordingMenu();
+
void initDeviceParams(SDRDeviceInfo *devInfo);
void updateDeviceParams();
@@ -103,19 +125,22 @@ public:
void setMainWaterfallFFTSize(int fftSize);
void setScopeDeviceName(std::string deviceName);
- void gkNudgeLeft(DemodulatorInstance *demod, int snap);
- void gkNudgeRight(DemodulatorInstance *demod, int snap);
+ void gkNudgeLeft(DemodulatorInstancePtr demod, int snap);
+ void gkNudgeRight(DemodulatorInstancePtr demod, int snap);
int OnGlobalKeyDown(wxKeyEvent &event);
int OnGlobalKeyUp(wxKeyEvent &event);
+ void toggleActiveDemodRecording();
+ void toggleAllActiveDemodRecording();
+
void setWaterfallLinesPerSecond(int lps);
void setSpectrumAvgSpeed(double avg);
FrequencyDialog::FrequencyDialogTarget getFrequencyDialogTarget();
void refreshGainUI();
void setViewState(long long center_freq, int bandwidth);
- void setViewState(long long center_freq);
+ void setViewState();
long long getViewCenterFreq();
int getViewBandwidth();
@@ -161,8 +186,13 @@ private:
bool actionOnMenuAudioSampleRate(wxCommandEvent& event);
bool actionOnMenuDisplay(wxCommandEvent& event);
bool actionOnMenuLoadSave(wxCommandEvent& event);
+ bool actionOnMenuRecording(wxCommandEvent& event);
bool actionOnMenuRig(wxCommandEvent& event);
+ wxString getSettingsLabel(const std::string& settingsName,
+ const std::string& settingsValue,
+ const std::string& settingsSuffix = "");
+
ScopeCanvas *scopeCanvas;
SpectrumCanvas *spectrumCanvas;
WaterfallCanvas *waterfallCanvas;
@@ -185,14 +215,26 @@ private:
wxBoxSizer *demodTray;
BookmarkView *bookmarkView;
- DemodulatorInstance *activeDemodulator;
+ //Use a raw pointer here to prevent a dangling reference
+ DemodulatorInstance* activeDemodulator;
std::vector<RtAudio::DeviceInfo> devices;
std::map<int,RtAudio::DeviceInfo> inputDevices;
std::map<int,RtAudio::DeviceInfo> outputDevices;
+
std::map<int, wxMenuItem *> outputDeviceMenuItems;
std::map<int, wxMenuItem *> sampleRateMenuItems;
+ std::map<int, wxMenuItem *> antennaMenuItems;
+
+ //depending on context, maps the item id to wxMenuItem*,
+ //OR the submenu item id to its parent wxMenuItem*.
+ std::map<int, wxMenuItem *> settingsMenuItems;
+
std::map<int, wxMenuItem *> audioSampleRateMenuItems;
+
+ //
+ std::map<int, wxMenuItem *> recordingMenuItems;
+
std::map<int, wxMenuItem *> directSamplingMenuItems;
wxMenuBar *menuBar;
@@ -201,14 +243,21 @@ private:
wxMenuItem *agcMenuItem = nullptr;
wxMenuItem *iqSwapMenuItem = nullptr;
wxMenuItem *lowPerfMenuItem = nullptr;
+ wxMenu *fileMenu = nullptr;
wxMenu *settingsMenu = nullptr;
+ wxMenu *recordingMenu = nullptr;
SoapySDR::ArgInfoList settingArgs;
int settingsIdMax;
std::vector<long> sampleRates;
long manualSampleRate = -1;
+
+ std::vector<std::string> antennaNames;
+
+ std::string currentTXantennaName;
std::string currentSessionFile;
+ std::string currentBookmarkFile;
FFTVisualDataThread *waterfallDataThread;
diff --git a/src/BookmarkMgr.cpp b/src/BookmarkMgr.cpp
index 7f0770e..85c4f86 100644
--- a/src/BookmarkMgr.cpp
+++ b/src/BookmarkMgr.cpp
@@ -4,6 +4,7 @@
#include "BookmarkMgr.h"
#include "CubicSDR.h"
#include "DataTree.h"
+#include <wx/string.h>
#define BOOKMARK_RECENTS_MAX 25
@@ -18,7 +19,8 @@ BookmarkMgr::BookmarkMgr() {
//represents an empty BookMarkList that is returned by reference by some functions.
const BookmarkList BookmarkMgr::emptyResults;
-void BookmarkMgr::saveToFile(std::string bookmarkFn, bool backup) {
+void BookmarkMgr::saveToFile(std::string bookmarkFn, bool backup, bool useFullpath) {
+
DataTree s("cubicsdr_bookmarks");
DataNode *header = s.rootNode()->newChild("header");
header->newChild("version")->element()->set(wxString(CUBICSDR_VERSION).ToStdWstring());
@@ -48,7 +50,21 @@ void BookmarkMgr::saveToFile(std::string bookmarkFn, bool backup) {
*group->newChild("@expanded") = (getExpandState(bmd_i.first)?std::string("true"):std::string("false"));
for (auto &bm_i : bmd_i.second ) {
- group->newChildCloneFrom("modem", bm_i->node);
+
+ //if a matching demodulator exists, use its data instead to be be saved, because output_device could have been
+ //modified by the user. So, save that "live" version instead.
+ auto matchingDemod = wxGetApp().getDemodMgr().getLastDemodulatorWith(bm_i->type,
+ bm_i->label,
+ bm_i->frequency,
+ bm_i->bandwidth);
+
+ if (matchingDemod != nullptr) {
+
+ wxGetApp().getDemodMgr().saveInstance(group->newChild("modem"), matchingDemod);
+ }
+ else {
+ group->newChildCloneFrom("modem", bm_i->node);
+ }
}
}
@@ -62,9 +78,18 @@ void BookmarkMgr::saveToFile(std::string bookmarkFn, bool backup) {
recent_modems->newChildCloneFrom("modem", r_i->node);
}
- wxFileName saveFile(wxGetApp().getConfig()->getConfigDir(), bookmarkFn);
- wxFileName saveFileBackup(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".backup");
-
+ wxFileName saveFile;
+ wxFileName saveFileBackup;
+
+ if (useFullpath) {
+ saveFile.Assign(bookmarkFn);
+ saveFileBackup.Assign(bookmarkFn + ".backup");
+ }
+ else {
+ saveFile.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn);
+ saveFileBackup.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".backup");
+ }
+
if (saveFile.IsDirWritable()) {
// Hopefully leave at least a readable backup in case of failure..
if (backup && saveFile.FileExists() && (!saveFileBackup.FileExists() || saveFileBackup.IsFileWritable())) {
@@ -74,20 +99,28 @@ void BookmarkMgr::saveToFile(std::string bookmarkFn, bool backup) {
}
}
-bool BookmarkMgr::loadFromFile(std::string bookmarkFn, bool backup) {
- wxFileName loadFile(wxGetApp().getConfig()->getConfigDir(), bookmarkFn);
- wxFileName failFile(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".failedload");
- wxFileName lastLoaded(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".lastloaded");
- wxFileName backupFile(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".backup");
+bool BookmarkMgr::loadFromFile(std::string bookmarkFn, bool backup, bool useFullpath) {
+
+ wxFileName loadFile;
+ wxFileName failFile;
+ wxFileName lastLoaded;
+ wxFileName backupFile;
+
+ if (useFullpath) {
+ loadFile.Assign(bookmarkFn);
+ failFile.Assign(bookmarkFn + ".failedload");
+ lastLoaded.Assign(bookmarkFn + ".lastloaded");
+ backupFile.Assign(bookmarkFn + ".backup");
+ }
+ else {
+ loadFile.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn);
+ failFile.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".failedload");
+ lastLoaded.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".lastloaded");
+ backupFile.Assign(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".backup");
+ }
DataTree s;
bool loadStatusOk = true;
-
- // Clear any active data
- bmData.clear();
- clearRecents();
- clearRanges();
- bmDataSorted.clear();
// File exists but is not readable
if (loadFile.FileExists() && !loadFile.IsFileReadable()) {
@@ -104,6 +137,17 @@ bool BookmarkMgr::loadFromFile(std::string bookmarkFn, bool backup) {
if (!s.LoadFromFileXML(loadFile.GetFullPath(wxPATH_NATIVE).ToStdString())) {
return false;
}
+
+ //Check if it is a bookmark file, read the root node.
+ if (s.rootNode()->getName() != "cubicsdr_bookmarks") {
+ return false;
+ }
+
+ // Clear any active data
+ bmData.clear();
+ clearRecents();
+ clearRanges();
+ bmDataSorted.clear();
if (s.rootNode()->hasAnother("branches")) {
DataNode *branches = s.rootNode()->getNext("branches");
@@ -149,7 +193,7 @@ bool BookmarkMgr::loadFromFile(std::string bookmarkFn, bool backup) {
setExpandState(groupName, (expandState == "true"));
while (group->hasAnother("modem")) {
DataNode *modem = group->getNext("modem");
- BookmarkEntryPtr be = nodeToBookmark("modem", modem);
+ BookmarkEntryPtr be = nodeToBookmark(modem);
if (be) {
addBookmark(groupName.c_str(), be);
} else {
@@ -165,7 +209,7 @@ bool BookmarkMgr::loadFromFile(std::string bookmarkFn, bool backup) {
while (recent_modems->hasAnother("modem")) {
DataNode *modem = recent_modems->getNext("modem");
- BookmarkEntryPtr be = nodeToBookmark("modem", modem);
+ BookmarkEntryPtr be = nodeToBookmark(modem);
if (be) {
addRecent(be);
} else {
@@ -194,6 +238,17 @@ bool BookmarkMgr::loadFromFile(std::string bookmarkFn, bool backup) {
return loadStatusOk;
}
+void BookmarkMgr::resetBookmarks() {
+
+ // Clear any active data
+ bmData.clear();
+ clearRecents();
+ clearRanges();
+ bmDataSorted.clear();
+
+ wxGetApp().getAppFrame()->getBookmarkView()->loadDefaultRanges();
+
+}
bool BookmarkMgr::hasLastLoad(std::string bookmarkFn) {
wxFileName lastLoaded(wxGetApp().getConfig()->getConfigDir(), bookmarkFn + ".lastloaded");
@@ -205,7 +260,7 @@ bool BookmarkMgr::hasBackup(std::string bookmarkFn) {
return backupFile.FileExists() && backupFile.IsFileReadable();
}
-void BookmarkMgr::addBookmark(std::string group, DemodulatorInstance *demod) {
+void BookmarkMgr::addBookmark(std::string group, DemodulatorInstancePtr demod) {
std::lock_guard < std::recursive_mutex > lock(busy_lock);
//Create a BookmarkEntry from demod data, saving its
@@ -360,7 +415,11 @@ bool BookmarkMgr::getExpandState(std::string groupName) {
void BookmarkMgr::updateActiveList() {
- std::lock_guard < std::recursive_mutex > lockData(busy_lock);
+ std::lock_guard < std::recursive_mutex > lockData(busy_lock);
+
+ if (wxGetApp().isShuttingDown()) {
+ return;
+ }
BookmarkView *bmv = wxGetApp().getAppFrame()->getBookmarkView();
@@ -392,7 +451,7 @@ void BookmarkMgr::updateBookmarks(std::string group) {
}
-void BookmarkMgr::addRecent(DemodulatorInstance *demod) {
+void BookmarkMgr::addRecent(DemodulatorInstancePtr demod) {
std::lock_guard < std::recursive_mutex > lock(busy_lock);
recents.push_back(demodToBookmarkEntry(demod));
@@ -482,7 +541,7 @@ void BookmarkMgr::clearRanges() {
}
-BookmarkEntryPtr BookmarkMgr::demodToBookmarkEntry(DemodulatorInstance *demod) {
+BookmarkEntryPtr BookmarkMgr::demodToBookmarkEntry(DemodulatorInstancePtr demod) {
BookmarkEntryPtr be(new BookmarkEntry);
@@ -499,7 +558,7 @@ BookmarkEntryPtr BookmarkMgr::demodToBookmarkEntry(DemodulatorInstance *demod) {
return be;
}
-BookmarkEntryPtr BookmarkMgr::nodeToBookmark(const char *name_in, DataNode *node) {
+BookmarkEntryPtr BookmarkMgr::nodeToBookmark(DataNode *node) {
if (!node->hasAnother("frequency") || !node->hasAnother("type") || !node->hasAnother("bandwidth")) {
return nullptr;
}
@@ -528,26 +587,28 @@ BookmarkEntryPtr BookmarkMgr::nodeToBookmark(const char *name_in, DataNode *node
std::wstring BookmarkMgr::getBookmarkEntryDisplayName(BookmarkEntryPtr bmEnt) {
std::wstring dispName = bmEnt->label;
- if (dispName == "") {
+ if (dispName == L"") {
std::string freqStr = frequencyToStr(bmEnt->frequency) + " " + bmEnt->type;
- dispName = wstring(freqStr.begin(),freqStr.end());
+
+ dispName = wxString(freqStr).ToStdWstring();
}
return dispName;
}
-std::wstring BookmarkMgr::getActiveDisplayName(DemodulatorInstance *demod) {
+std::wstring BookmarkMgr::getActiveDisplayName(DemodulatorInstancePtr demod) {
std::wstring activeName = demod->getDemodulatorUserLabel();
- if (activeName == "") {
+ if (activeName == L"") {
std::string wstr = frequencyToStr(demod->getFrequency()) + " " + demod->getDemodulatorType();
- activeName = std::wstring(wstr.begin(),wstr.end());
+
+ activeName = wxString(wstr).ToStdWstring();
}
return activeName;
}
-void BookmarkMgr::removeActive(DemodulatorInstance *demod) {
+void BookmarkMgr::removeActive(DemodulatorInstancePtr demod) {
std::lock_guard < std::recursive_mutex > lock(busy_lock);
diff --git a/src/BookmarkMgr.h b/src/BookmarkMgr.h
index 5f18b69..0560a67 100644
--- a/src/BookmarkMgr.h
+++ b/src/BookmarkMgr.h
@@ -78,14 +78,17 @@ typedef std::map<std::string, bool> BookmarkExpandState;
class BookmarkMgr {
public:
BookmarkMgr();
-
- void saveToFile(std::string bookmarkFn, bool backup = true);
- bool loadFromFile(std::string bookmarkFn, bool backup = true);
+ //if useFullpath = false, use the application config dir.
+ //else assume bookmarkFn is a full path and use it for location.
+ void saveToFile(std::string bookmarkFn, bool backup = true, bool useFullpath = false);
+ bool loadFromFile(std::string bookmarkFn, bool backup = true, bool useFullpath = false);
+
+ void resetBookmarks();
bool hasLastLoad(std::string bookmarkFn);
bool hasBackup(std::string bookmarkFn);
- void addBookmark(std::string group, DemodulatorInstance *demod);
+ void addBookmark(std::string group, DemodulatorInstancePtr demod);
void addBookmark(std::string group, BookmarkEntryPtr be);
void removeBookmark(std::string group, BookmarkEntryPtr be);
void removeBookmark(BookmarkEntryPtr be);
@@ -105,13 +108,13 @@ public:
void updateBookmarks();
void updateBookmarks(std::string group);
- void addRecent(DemodulatorInstance *demod);
+ void addRecent(DemodulatorInstancePtr demod);
void addRecent(BookmarkEntryPtr be);
void removeRecent(BookmarkEntryPtr be);
const BookmarkList& getRecents();
void clearRecents();
- void removeActive(DemodulatorInstance *demod);
+ void removeActive(DemodulatorInstancePtr demod);
void addRange(BookmarkRangeEntryPtr re);
void removeRange(BookmarkRangeEntryPtr re);
@@ -119,14 +122,14 @@ public:
void clearRanges();
static std::wstring getBookmarkEntryDisplayName(BookmarkEntryPtr bmEnt);
- static std::wstring getActiveDisplayName(DemodulatorInstance *demod);
+ static std::wstring getActiveDisplayName(DemodulatorInstancePtr demod);
protected:
void trimRecents();
- BookmarkEntryPtr demodToBookmarkEntry(DemodulatorInstance *demod);
- BookmarkEntryPtr nodeToBookmark(const char *name_in, DataNode *node);
+ BookmarkEntryPtr demodToBookmarkEntry(DemodulatorInstancePtr demod);
+ BookmarkEntryPtr nodeToBookmark(DataNode *node);
BookmarkMap bmData;
BookmarkMapSorted bmDataSorted;
diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp
index 4e4931c..e6b63b1 100644
--- a/src/CubicSDR.cpp
+++ b/src/CubicSDR.cpp
@@ -32,6 +32,8 @@ IMPLEMENT_APP(CubicSDR)
#include "ActionDialog.h"
+#include <memory>
+
//#ifdef ENABLE_DIGITAL_LAB
//// console output buffer for windows
@@ -201,6 +203,7 @@ CubicSDR::CubicSDR() : frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFA
sampleRateInitialized.store(false);
agcMode.store(true);
soloMode.store(false);
+ shuttingDown.store(false);
fdlgTarget = FrequencyDialog::FDIALOG_TARGET_DEFAULT;
stoppedDev = nullptr;
}
@@ -290,17 +293,17 @@ bool CubicSDR::OnInit() {
// Visual Data
spectrumVisualThread = new SpectrumVisualDataThread();
- pipeIQVisualData = new DemodulatorThreadInputQueue();
+ pipeIQVisualData = std::make_shared<DemodulatorThreadInputQueue>();
pipeIQVisualData->set_max_num_items(1);
- pipeWaterfallIQVisualData = new DemodulatorThreadInputQueue();
+ pipeWaterfallIQVisualData = std::make_shared<DemodulatorThreadInputQueue>();
pipeWaterfallIQVisualData->set_max_num_items(128);
getSpectrumProcessor()->setInput(pipeIQVisualData);
getSpectrumProcessor()->setHideDC(true);
// I/Q Data
- pipeSDRIQData = new SDRThreadIQDataQueue();
+ pipeSDRIQData = std::make_shared<SDRThreadIQDataQueue>();
pipeSDRIQData->set_max_num_items(100);
sdrThread = new SDRThread();
@@ -313,7 +316,7 @@ bool CubicSDR::OnInit() {
sdrPostThread->setOutputQueue("IQDataOutput", pipeWaterfallIQVisualData);
#if CUBICSDR_ENABLE_VIEW_SCOPE
- pipeAudioVisualData = new DemodulatorThreadOutputQueue();
+ pipeAudioVisualData = std::make_shared<DemodulatorThreadOutputQueue>();
pipeAudioVisualData->set_max_num_items(1);
scopeProcessor.setInput(pipeAudioVisualData);
@@ -323,7 +326,7 @@ bool CubicSDR::OnInit() {
#if CUBICSDR_ENABLE_VIEW_DEMOD
demodVisualThread = new SpectrumVisualDataThread();
- pipeDemodIQVisualData = new DemodulatorThreadInputQueue();
+ pipeDemodIQVisualData = std::make_shared<DemodulatorThreadInputQueue>();
pipeDemodIQVisualData->set_max_num_items(1);
if (getDemodSpectrumProcessor()) {
@@ -382,49 +385,81 @@ bool CubicSDR::OnInit() {
}
int CubicSDR::OnExit() {
+ shuttingDown.store(true);
+
#if USE_HAMLIB
if (rigIsActive()) {
- std::cout << "Terminating Rig thread.." << std::endl;
+ std::cout << "Terminating Rig thread.." << std::endl << std::flush;
stopRig();
}
#endif
+ bool terminationSequenceOK = true;
+
//The thread feeding them all should be terminated first, so:
- std::cout << "Terminating SDR thread.." << std::endl;
+ std::cout << "Terminating SDR thread.." << std::endl << std::flush ;
sdrThread->terminate();
- sdrThread->isTerminated(3000);
-
- if (t_SDR) {
- t_SDR->join();
- delete t_SDR;
- t_SDR = nullptr;
+ terminationSequenceOK = terminationSequenceOK && sdrThread->isTerminated(3000);
+
+ //in case termination sequence goes wrong, kill App brutally now because it can get stuck.
+ if (!terminationSequenceOK) {
+ //no trace here because it could occur if the device is not started.
+ ::exit(11);
}
- std::cout << "Terminating SDR post-processing thread.." << std::endl;
+ std::cout << "Terminating SDR post-processing thread.." << std::endl << std::flush;
sdrPostThread->terminate();
- std::cout << "Terminating All Demodulators.." << std::endl;
+ //Wait for termination for sdrPostThread second:: since it is doing
+ //mostly blocking push() to the other threads, they must stay alive
+ //so that sdrPostThread can complete a processing loop and die.
+ terminationSequenceOK = terminationSequenceOK && sdrPostThread->isTerminated(3000);
+
+ //in case termination sequence goes wrong, kill App brutally now because it can get stuck.
+ if (!terminationSequenceOK) {
+ std::cout << "Cannot terminate application properly, calling exit() now." << std::endl << std::flush;
+ ::exit(12);
+ }
+
+ std::cout << "Terminating All Demodulators.." << std::endl << std::flush;
demodMgr.terminateAll();
-
- std::cout << "Terminating Visual Processor threads.." << std::endl;
+
+ std::cout << "Terminating Visual Processor threads.." << std::endl << std::flush;
spectrumVisualThread->terminate();
if (demodVisualThread) {
demodVisualThread->terminate();
}
//Wait nicely
- sdrPostThread->isTerminated(1000);
- spectrumVisualThread->isTerminated(1000);
+ terminationSequenceOK = terminationSequenceOK && spectrumVisualThread->isTerminated(1000);
+
if (demodVisualThread) {
- demodVisualThread->isTerminated(1000);
+ terminationSequenceOK = terminationSequenceOK && demodVisualThread->isTerminated(1000);
+ }
+
+ //in case termination sequence goes wrong, kill App brutally because it can get stuck.
+ if (!terminationSequenceOK) {
+ std::cout << "Cannot terminate application properly, calling exit() now." << std::endl << std::flush;
+ ::exit(13);
+ }
+
+ //Then join the thread themselves:
+ if (t_SDR) {
+ t_SDR->join();
}
- //Then join the thread themselves
t_PostSDR->join();
- if (t_DemodVisual) t_DemodVisual->join();
+
+ if (t_DemodVisual) {
+ t_DemodVisual->join();
+ }
+
t_SpectrumVisual->join();
- //Now only we can delete
+ //Now only we can delete:
+ delete t_SDR;
+ t_SDR = nullptr;
+
delete sdrThread;
sdrThread = nullptr;
@@ -445,19 +480,12 @@ int CubicSDR::OnExit() {
delete demodVisualThread;
demodVisualThread = nullptr;
-
- delete pipeIQVisualData;
- pipeIQVisualData = nullptr;
-
- delete pipeAudioVisualData;
- pipeAudioVisualData = nullptr;
-
- delete pipeSDRIQData;
- pipeSDRIQData = nullptr;
delete m_glContext;
m_glContext = nullptr;
+ std::cout << "Application termination complete." << std::endl << std::flush;
+
#ifdef __APPLE__
AudioThread::deviceCleanup();
#endif
@@ -601,9 +629,22 @@ long long CubicSDR::getOffset() {
void CubicSDR::setOffset(long long ofs) {
offset = ofs;
- sdrThread->setOffset(offset);
- SDRDeviceInfo *dev = getDevice();
- config.getDevice(dev->getDeviceId())->setOffset(ofs);
+
+ if (sdrThread && !sdrThread->isTerminated()) {
+ sdrThread->setOffset(offset);
+ }
+}
+
+void CubicSDR::setAntennaName(const std::string& name) {
+ antennaName = name;
+
+ if (sdrThread && !sdrThread->isTerminated()) {
+ sdrThread->setAntenna(antennaName);
+ }
+}
+
+const std::string& CubicSDR::getAntennaName() {
+ return antennaName;
}
long long CubicSDR::getFrequency() {
@@ -626,12 +667,18 @@ bool CubicSDR::isFrequencyLocked() {
void CubicSDR::unlockFrequency() {
frequency_locked.store(false);
- sdrThread->unlockFrequency();
+ if (sdrThread && !sdrThread->isTerminated()) {
+ sdrThread->unlockFrequency();
+ }
}
void CubicSDR::setSampleRate(long long rate_in) {
sampleRate = rate_in;
- sdrThread->setSampleRate(sampleRate);
+
+ if (sdrThread && !sdrThread->isTerminated()) {
+ sdrThread->setSampleRate(sampleRate);
+ }
+
setFrequency(frequency);
if (rate_in <= CHANNELIZER_RATE_MAX / 8) {
@@ -722,13 +769,8 @@ void CubicSDR::setDevice(SDRDeviceInfo *dev, int waitMsForTermination) {
setPPM(devConfig->getPPM());
setOffset(devConfig->getOffset());
-
-
- if (devConfig->getAGCMode()) {
- setAGCMode(true);
- } else {
- setAGCMode(false);
- }
+ setAGCMode(devConfig->getAGCMode());
+ setAntennaName(devConfig->getAntennaName());
t_SDR = new std::thread(&SDRThread::threadMain, sdrThread);
}
@@ -760,15 +802,15 @@ SpectrumVisualProcessor *CubicSDR::getDemodSpectrumProcessor() {
}
}
-DemodulatorThreadOutputQueue* CubicSDR::getAudioVisualQueue() {
+DemodulatorThreadOutputQueuePtr CubicSDR::getAudioVisualQueue() {
return pipeAudioVisualData;
}
-DemodulatorThreadInputQueue* CubicSDR::getIQVisualQueue() {
+DemodulatorThreadInputQueuePtr CubicSDR::getIQVisualQueue() {
return pipeIQVisualData;
}
-DemodulatorThreadInputQueue* CubicSDR::getWaterfallVisualQueue() {
+DemodulatorThreadInputQueuePtr CubicSDR::getWaterfallVisualQueue() {
return pipeWaterfallIQVisualData;
}
@@ -789,30 +831,21 @@ SDRThread *CubicSDR::getSDRThread() {
}
-void CubicSDR::bindDemodulator(DemodulatorInstance *demod) {
- if (!demod) {
- return;
- }
- sdrPostThread->bindDemodulator(demod);
-}
-
-void CubicSDR::bindDemodulators(std::vector<DemodulatorInstance *> *demods) {
- if (!demods) {
- return;
- }
- sdrPostThread->bindDemodulators(demods);
+void CubicSDR::notifyDemodulatorsChanged() {
+
+ sdrPostThread->notifyDemodulatorsChanged();
}
long long CubicSDR::getSampleRate() {
return sampleRate;
}
-void CubicSDR::removeDemodulator(DemodulatorInstance *demod) {
+void CubicSDR::removeDemodulator(DemodulatorInstancePtr demod) {
if (!demod) {
return;
}
demod->setActive(false);
- sdrPostThread->removeDemodulator(demod);
+ sdrPostThread->notifyDemodulatorsChanged();
wxGetApp().getAppFrame()->notifyUpdateModemProperties();
}
@@ -831,11 +864,8 @@ void CubicSDR::saveConfig() {
void CubicSDR::setPPM(int ppm_in) {
ppm = ppm_in;
- sdrThread->setPPM(ppm);
-
- SDRDeviceInfo *dev = getDevice();
- if (dev) {
- config.getDevice(dev->getDeviceId())->setPPM(ppm_in);
+ if (sdrThread && !sdrThread->isTerminated()) {
+ sdrThread->setPPM(ppm);
}
}
@@ -887,7 +917,7 @@ void CubicSDR::showFrequencyInput(FrequencyDialog::FrequencyDialogTarget targetM
void CubicSDR::showLabelInput() {
- DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator();
+ DemodulatorInstancePtr activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator();
if (activeDemod != nullptr) {
@@ -917,8 +947,12 @@ bool CubicSDR::areDevicesReady() {
return devicesReady.load();
}
-void CubicSDR::notifyMainUIOfDeviceChange() {
+void CubicSDR::notifyMainUIOfDeviceChange(bool forceRefreshOfGains) {
appframe->notifyDeviceChanged();
+
+ if (forceRefreshOfGains) {
+ appframe->refreshGainUI();
+ }
}
bool CubicSDR::areDevicesEnumerating() {
@@ -947,7 +981,10 @@ bool CubicSDR::isDeviceSelectorOpen() {
void CubicSDR::setAGCMode(bool mode) {
agcMode.store(mode);
- sdrThread->setAGCMode(mode);
+
+ if (sdrThread && !sdrThread->isTerminated()) {
+ sdrThread->setAGCMode(mode);
+ }
}
bool CubicSDR::getAGCMode() {
@@ -995,6 +1032,11 @@ bool CubicSDR::getSoloMode() {
return soloMode.load();
}
+bool CubicSDR::isShuttingDown()
+{
+ return shuttingDown.load();
+}
+
int CubicSDR::FilterEvent(wxEvent& event) {
if (!appframe) {
return -1;
diff --git a/src/CubicSDR.h b/src/CubicSDR.h
index 3dbbbc4..d097829 100644
--- a/src/CubicSDR.h
+++ b/src/CubicSDR.h
@@ -91,6 +91,9 @@ public:
void setOffset(long long ofs);
long long getOffset();
+
+ void setAntennaName(const std::string& name);
+ const std::string& getAntennaName();
void setDBOffset(int ofs);
int getDBOffset();
@@ -98,6 +101,7 @@ public:
void setSampleRate(long long rate_in);
long long getSampleRate();
+
std::vector<SDRDeviceInfo *> *getDevices();
void setDevice(SDRDeviceInfo *dev, int waitMsForTermination);
void stopDevice(bool store, int waitMsForTermination);
@@ -107,19 +111,19 @@ public:
SpectrumVisualProcessor *getSpectrumProcessor();
SpectrumVisualProcessor *getDemodSpectrumProcessor();
- DemodulatorThreadOutputQueue* getAudioVisualQueue();
- DemodulatorThreadInputQueue* getIQVisualQueue();
- DemodulatorThreadInputQueue* getWaterfallVisualQueue();
- DemodulatorThreadInputQueue* getActiveDemodVisualQueue();
+ DemodulatorThreadOutputQueuePtr getAudioVisualQueue();
+ DemodulatorThreadInputQueuePtr getIQVisualQueue();
+ DemodulatorThreadInputQueuePtr getWaterfallVisualQueue();
+ DemodulatorThreadInputQueuePtr getActiveDemodVisualQueue();
DemodulatorMgr &getDemodMgr();
BookmarkMgr &getBookmarkMgr();
SDRPostThread *getSDRPostThread();
SDRThread *getSDRThread();
- void bindDemodulator(DemodulatorInstance *demod);
- void bindDemodulators(std::vector<DemodulatorInstance *> *demods);
- void removeDemodulator(DemodulatorInstance *demod);
+ void notifyDemodulatorsChanged();
+
+ void removeDemodulator(DemodulatorInstancePtr demod);
void setFrequencySnap(int snap);
int getFrequencySnap();
@@ -139,7 +143,7 @@ public:
bool areModulesMissing();
std::string getNotification();
- void notifyMainUIOfDeviceChange();
+ void notifyMainUIOfDeviceChange(bool forceRefreshOfGains = false);
void addRemote(std::string remoteAddr);
void removeRemote(std::string remoteAddr);
@@ -166,6 +170,8 @@ public:
void setSoloMode(bool solo);
bool getSoloMode();
+
+ bool isShuttingDown();
#ifdef USE_HAMLIB
RigThread *getRigThread();
@@ -189,7 +195,9 @@ private:
std::atomic_llong offset;
std::atomic_int ppm, snap;
std::atomic_llong sampleRate;
+ std::string antennaName;
std::atomic_bool agcMode;
+ std::atomic_bool shuttingDown;
SDRThread *sdrThread = nullptr;
SDREnumerator *sdrEnum = nullptr;
@@ -197,12 +205,12 @@ private:
SpectrumVisualDataThread *spectrumVisualThread = nullptr;
SpectrumVisualDataThread *demodVisualThread = nullptr;
- SDRThreadIQDataQueue* pipeSDRIQData = nullptr;
- DemodulatorThreadInputQueue* pipeIQVisualData = nullptr;
- DemodulatorThreadOutputQueue* pipeAudioVisualData = nullptr;
- DemodulatorThreadInputQueue* pipeDemodIQVisualData = nullptr;
- DemodulatorThreadInputQueue* pipeWaterfallIQVisualData = nullptr;
- DemodulatorThreadInputQueue* pipeActiveDemodIQVisualData = nullptr;
+ SDRThreadIQDataQueuePtr pipeSDRIQData;
+ DemodulatorThreadInputQueuePtr pipeIQVisualData;
+ DemodulatorThreadOutputQueuePtr pipeAudioVisualData;
+ DemodulatorThreadInputQueuePtr pipeDemodIQVisualData;
+ DemodulatorThreadInputQueuePtr pipeWaterfallIQVisualData;
+ DemodulatorThreadInputQueuePtr pipeActiveDemodIQVisualData;
ScopeVisualProcessor scopeProcessor;
diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h
index e5cc4ca..3beb96b 100644
--- a/src/CubicSDRDefs.h
+++ b/src/CubicSDRDefs.h
@@ -59,3 +59,7 @@ const char filePathSeparator =
//Represents the amount of time to process in the FFT distributor.
#define FFT_DISTRIBUTOR_BUFFER_IN_SECONDS 0.250
+
+//The maximum number of listed sample rates for a device, to be able to handle
+//devices returning an insane amount because they have quasi-continuous ranges (UHD...)
+#define DEVICE_SAMPLE_RATES_MAX_NB 25
\ No newline at end of file
diff --git a/src/DemodLabelDialog.cpp b/src/DemodLabelDialog.cpp
index 251c356..139793f 100644
--- a/src/DemodLabelDialog.cpp
+++ b/src/DemodLabelDialog.cpp
@@ -13,7 +13,7 @@ EVT_SHOW(DemodLabelDialog::OnShow)
wxEND_EVENT_TABLE()
DemodLabelDialog::DemodLabelDialog(wxWindow * parent, wxWindowID id, const wxString & title,
- DemodulatorInstance *demod, const wxPoint & position,
+ DemodulatorInstancePtr demod, const wxPoint & position,
const wxSize & size, long style) :
wxDialog(parent, id, title, position, size, style) {
diff --git a/src/DemodLabelDialog.h b/src/DemodLabelDialog.h
index cee9cff..ab13aa9 100644
--- a/src/DemodLabelDialog.h
+++ b/src/DemodLabelDialog.h
@@ -16,7 +16,7 @@ class DemodLabelDialog : public wxDialog
public:
DemodLabelDialog( wxWindow * parent, wxWindowID id, const wxString & title,
- DemodulatorInstance *demod = NULL,
+ DemodulatorInstancePtr demod = nullptr,
const wxPoint & pos = wxDefaultPosition,
const wxSize & size = wxDefaultSize,
long style = wxDEFAULT_DIALOG_STYLE);
@@ -24,7 +24,7 @@ public:
wxTextCtrl * dialogText;
private:
- DemodulatorInstance *activeDemod = nullptr;
+ DemodulatorInstancePtr activeDemod = nullptr;
void OnEnter ( wxCommandEvent &event );
void OnChar ( wxKeyEvent &event );
void OnShow(wxShowEvent &event);
diff --git a/src/FrequencyDialog.cpp b/src/FrequencyDialog.cpp
index f16ad3b..a272d4c 100644
--- a/src/FrequencyDialog.cpp
+++ b/src/FrequencyDialog.cpp
@@ -12,11 +12,13 @@ EVT_CHAR_HOOK(FrequencyDialog::OnChar)
EVT_SHOW(FrequencyDialog::OnShow)
wxEND_EVENT_TABLE()
-FrequencyDialog::FrequencyDialog(wxWindow * parent, wxWindowID id, const wxString & title, DemodulatorInstance *demod, const wxPoint & position,
+FrequencyDialog::FrequencyDialog(wxWindow * parent, wxWindowID id, const wxString & title, DemodulatorInstancePtr demod, const wxPoint & position,
const wxSize & size, long style, FrequencyDialogTarget targetMode, wxString initString) :
wxDialog(parent, id, title, position, size, style) {
wxString freqStr;
+
activeDemod = demod;
+
this->targetMode = targetMode;
this->initialString = initString;
@@ -53,7 +55,7 @@ FrequencyDialog::FrequencyDialog(wxWindow * parent, wxWindowID id, const wxStrin
dialogText = new wxTextCtrl(this, wxID_FREQ_INPUT, freqStr, wxPoint(6, 1), wxSize(size.GetWidth() - 20, size.GetHeight() - 70),
wxTE_PROCESS_ENTER);
- dialogText->SetFont(wxFont(20, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
+ dialogText->SetFont(wxFont(15, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
Centre();
@@ -122,7 +124,7 @@ void FrequencyDialog::OnChar(wxKeyEvent& event) {
}
if (freq == freq2) {
wxGetApp().setFrequency(freq_ctr);
- wxGetApp().getAppFrame()->setViewState(freq_ctr);
+ wxGetApp().getAppFrame()->setViewState();
} else {
if (wxGetApp().getSampleRate()/4 > range_bw) {
wxGetApp().setFrequency(freq_ctr + wxGetApp().getSampleRate()/4);
diff --git a/src/FrequencyDialog.h b/src/FrequencyDialog.h
index 259de20..6d260b9 100644
--- a/src/FrequencyDialog.h
+++ b/src/FrequencyDialog.h
@@ -24,7 +24,7 @@ public:
FDIALOG_TARGET_GAIN
} FrequencyDialogTarget;
FrequencyDialog ( wxWindow * parent, wxWindowID id, const wxString & title,
- DemodulatorInstance *demod = NULL,
+ DemodulatorInstancePtr demod = nullptr,
const wxPoint & pos = wxDefaultPosition,
const wxSize & size = wxDefaultSize,
long style = wxDEFAULT_DIALOG_STYLE,
@@ -34,7 +34,7 @@ public:
wxTextCtrl * dialogText;
private:
- DemodulatorInstance *activeDemod;
+ DemodulatorInstancePtr activeDemod;
void OnEnter ( wxCommandEvent &event );
void OnChar ( wxKeyEvent &event );
void OnShow(wxShowEvent &event);
diff --git a/src/IOThread.cpp b/src/IOThread.cpp
index 029fe73..5d78b64 100644
--- a/src/IOThread.cpp
+++ b/src/IOThread.cpp
@@ -3,9 +3,7 @@
#include "IOThread.h"
#include <typeinfo>
-
-std::mutex ReBufferGC::g_mutex;
-std::set<ReferenceCounter *> ReBufferGC::garbage;
+#include <memory>
#define SPIN_WAIT_SLEEP_MS 5
@@ -71,32 +69,32 @@ void IOThread::terminate() {
stopping.store(true);
};
-void IOThread::onBindOutput(std::string /* name */, ThreadQueueBase* /* threadQueue */) {
+void IOThread::onBindOutput(std::string /* name */, ThreadQueueBasePtr /* threadQueue */) {
};
-void IOThread::onBindInput(std::string /* name */, ThreadQueueBase* /* threadQueue */) {
+void IOThread::onBindInput(std::string /* name */, ThreadQueueBasePtr /* threadQueue */) {
};
-void IOThread::setInputQueue(std::string qname, ThreadQueueBase *threadQueue) {
+void IOThread::setInputQueue(std::string qname, ThreadQueueBasePtr threadQueue) {
std::lock_guard < std::mutex > lock(m_queue_bindings_mutex);
input_queues[qname] = threadQueue;
this->onBindInput(qname, threadQueue);
};
-ThreadQueueBase *IOThread::getInputQueue(std::string qname) {
+ThreadQueueBasePtr IOThread::getInputQueue(std::string qname) {
std::lock_guard < std::mutex > lock(m_queue_bindings_mutex);
return input_queues[qname];
};
-void IOThread::setOutputQueue(std::string qname, ThreadQueueBase *threadQueue) {
+void IOThread::setOutputQueue(std::string qname, ThreadQueueBasePtr threadQueue) {
std::lock_guard < std::mutex > lock(m_queue_bindings_mutex);
output_queues[qname] = threadQueue;
this->onBindOutput(qname, threadQueue);
};
-ThreadQueueBase *IOThread::getOutputQueue(std::string qname) {
+ThreadQueueBasePtr IOThread::getOutputQueue(std::string qname) {
std::lock_guard < std::mutex > lock(m_queue_bindings_mutex);
return output_queues[qname];
};
@@ -130,7 +128,7 @@ bool IOThread::isTerminated(int waitMs) {
}
}
- std::cout << "ERROR: thread '" << typeid(*this).name() << "' has not terminated in time ! (> " << waitMs << " ms)" << std::endl;
+ std::cout << "ERROR: thread '" << typeid(*this).name() << "' has not terminated in time ! (> " << waitMs << " ms)" << std::endl << std::flush;
return terminated.load();
}
diff --git a/src/IOThread.h b/src/IOThread.h
index 46ce897..20455f3 100644
--- a/src/IOThread.h
+++ b/src/IOThread.h
@@ -11,7 +11,8 @@
#include <string>
#include <iostream>
#include <thread>
-
+#include <memory>
+#include <climits>
#include "ThreadBlockingQueue.h"
#include "Timer.h"
@@ -23,163 +24,135 @@ struct map_string_less : public std::binary_function<std::string,std::string,boo
}
};
-
-class ReferenceCounter {
-
+template <typename PtrType>
+class ReBufferAge {
public:
- //default constructor, initialized with refcont 1, sounds very natural
- ReferenceCounter() {
- refCount = 1;
- }
-
-// void setIndex(int idx) {
-// std::lock_guard < std::recursive_mutex > lock(m_mutex);
-// index = idx;
-// }
-
-// int getIndex() {
-// std::lock_guard < std::recursive_mutex > lock(m_mutex);
-// return index;
-// }
-
- void setRefCount(int rc) {
- std::lock_guard < std::recursive_mutex > lock(m_mutex);
- refCount = rc;
- }
-
- void decRefCount() {
- std::lock_guard < std::recursive_mutex > lock(m_mutex);
- refCount--;
- }
-
- int getRefCount() {
- std::lock_guard < std::recursive_mutex > lock(m_mutex);
- return refCount;
- }
-
- // Access to the own mutex protecting the ReferenceCounter, i.e the monitor of the class
- std::recursive_mutex& getMonitor() const {
- return m_mutex;
+ ReBufferAge(PtrType p, int a) {
+ ptr = p;
+ age = a;
}
-protected:
- //this is a basic mutex for all ReferenceCounter derivatives operations INCLUDING the counter itself for consistency !
- mutable std::recursive_mutex m_mutex;
+ PtrType ptr;
+ int age;
-private:
- int refCount;
-// int index;
+ virtual ~ReBufferAge() {};
};
-
#define REBUFFER_GC_LIMIT 100
+#define REBUFFER_WARNING_THRESHOLD 150
-class ReBufferGC {
-public:
- static void garbageCollect() {
- std::lock_guard < std::mutex > lock(g_mutex);
-
- std::deque<ReferenceCounter *> garbageRemoval;
- for (typename std::set<ReferenceCounter *>::iterator i = garbage.begin(); i != garbage.end(); i++) {
- if ((*i)->getRefCount() <= 0) {
- garbageRemoval.push_back(*i);
- }
- else {
-// std::cout << "Garbage in queue buffer idx #" << (*i)->getIndex() << ", " << (*i)->getRefCount() << " usage(s)" << std::endl;
- std::cout << "Garbage in queue buffer with " << (*i)->getRefCount() << " usage(s)" << std::endl;
- }
- }
- if ( garbageRemoval.size() ) {
- std::cout << "Garbage collecting " << garbageRemoval.size() << " ReBuffer(s)" << std::endl;
- while (!garbageRemoval.empty()) {
- ReferenceCounter *ref = garbageRemoval.back();
- garbageRemoval.pop_back();
- garbage.erase(ref);
- delete ref;
- }
- }
- }
-
- static void addGarbage(ReferenceCounter *ref) {
- std::lock_guard < std::mutex > lock(g_mutex);
- garbage.insert(ref);
- }
-
-private:
- static std::mutex g_mutex;
- static std::set<ReferenceCounter *> garbage;
-};
-
-
-template<class BufferType = ReferenceCounter>
+template<typename BufferType>
class ReBuffer {
+ typedef typename std::shared_ptr<BufferType> ReBufferPtr;
+
public:
+
+ //Virtual destructor to assure correct freeing of all descendants.
+ virtual ~ReBuffer() {
+ //nothing
+ }
+
+ //constructor
ReBuffer(std::string bufferId) : bufferId(bufferId) {
-// indexCounter.store(0);
+ //nothing
}
- BufferType *getBuffer() {
+ /// Return a new ReBuffer_ptr usable by the application.
+ ReBufferPtr getBuffer() {
+
std::lock_guard < std::mutex > lock(m_mutex);
- BufferType* buf = nullptr;
- for (outputBuffersI = outputBuffers.begin(); outputBuffersI != outputBuffers.end(); outputBuffersI++) {
- if (buf == nullptr && (*outputBuffersI)->getRefCount() <= 0) {
- buf = (*outputBuffersI);
- buf->setRefCount(1);
- } else if ((*outputBuffersI)->getRefCount() <= 0) {
- (*outputBuffersI)->decRefCount();
+ // iterate the ReBufferAge list: if the std::shared_ptr count == 1, it means
+ //it is only referenced in outputBuffers itself, so available for re-use.
+ //else if the std::shared_ptr count <= 1, make it age.
+ //else the ReBufferPtr is in use, don't use it.
+
+ ReBufferPtr buf = nullptr;
+
+ outputBuffersI it = outputBuffers.begin();
+
+ while (it != outputBuffers.end()) {
+
+ //careful here: take care of reading the use_count directly
+ //through the iterator, else it's value is wrong if a temp variable
+ //is used.
+ long use = it->ptr.use_count();
+
+ //1. If we encounter a ReBufferPtr with a use count of 0, this
+ //is a bug since it is supposed to be at least 1, because it is referenced here.
+ //in this case, purge it from here and trace.
+ if (use == 0) {
+ std::cout << "Warning: in ReBuffer '" << bufferId << "' count '" << outputBuffers.size() << "', found 1 dangling buffer !" << std::endl << std::flush;
+ it = outputBuffers.erase(it);
+ }
+ else if (use == 1) {
+ if (buf == nullptr) {
+ it->age = 1; //select this one.
+ buf = it->ptr;
+ // std::cout << "**" << std::flush;
+ it++;
+ }
+ else {
+ //make the other unused buffers age
+ it->age--;
+ it++;
+ }
}
- }
-
- if (buf != nullptr) {
- if (outputBuffers.back()->getRefCount() < -REBUFFER_GC_LIMIT) {
- BufferType *ref = outputBuffers.back();
+ else {
+ it++;
+ }
+ } //end while
+
+ //2.1 Garbage collect the oldest (last element) if it aged too much, and return the buffer
+ if (buf != nullptr) {
+
+ if (outputBuffers.back().age < -REBUFFER_GC_LIMIT) {
+ //by the nature of the shared_ptr, memory will ne deallocated automatically.
outputBuffers.pop_back();
- delete ref;
+ //std::cout << "--" << std::flush;
}
-// buf->setIndex(indexCounter++);
return buf;
}
-
-#define REBUFFER_WARNING_THRESHOLD 100
+
if (outputBuffers.size() > REBUFFER_WARNING_THRESHOLD) {
- std::cout << "Warning: ReBuffer '" << bufferId << "' count '" << outputBuffers.size() << "' exceeds threshold of '" << REBUFFER_WARNING_THRESHOLD << "'" << std::endl;
+ std::cout << "Warning: ReBuffer '" << bufferId << "' count '" << outputBuffers.size() << "' exceeds threshold of '" << REBUFFER_WARNING_THRESHOLD << "'" << std::endl << std::flush;
}
-
- //by default created with refcount = 1
- buf = new BufferType();
-// buf->setIndex(indexCounter++);
- outputBuffers.push_back(buf);
- return buf;
+ //3.We need to allocate a new buffer.
+ ReBufferAge < ReBufferPtr > newBuffer(std::make_shared<BufferType>(), 1);
+
+ outputBuffers.push_back(newBuffer);
+
+ // std::cout << "++" << std::flush;
+ return newBuffer.ptr;
}
+ /// Purge the cache.
void purge() {
std::lock_guard < std::mutex > lock(m_mutex);
-// if (bufferId == "DemodulatorThreadBuffers") {
-// std::cout << "'" << bufferId << "' purging.. total indexes: " << indexCounter.load() << std::endl;
-// }
- while (!outputBuffers.empty()) {
- BufferType *ref = outputBuffers.front();
- outputBuffers.pop_front();
- if (ref->getRefCount() <= 0) {
- delete ref;
- } else {
- // Something isn't done with it yet; throw it on the pile.. keep this as a bug indicator for now..
- std::cout << "'" << bufferId << "' pushed garbage.." << std::endl;
- ReBufferGC::addGarbage(ref);
- }
- }
+
+ // since outputBuffers are full std::shared_ptr,
+ //purging if will effectively loose the local reference,
+ // so the std::shared_ptr will naturally be deallocated
+ //when their time comes.
+ outputBuffers.clear();
}
- private:
+private:
+
+ //name of the buffer cache kind
std::string bufferId;
- std::deque<BufferType*> outputBuffers;
- typename std::deque<BufferType*>::iterator outputBuffersI;
- mutable std::mutex m_mutex;
-// std::atomic_int indexCounter;
+
+ //the ReBuffer cache: use a std:deque to also release
+ //memory when ReBufferPtr are GCed.
+ std::deque< ReBufferAge < ReBufferPtr > > outputBuffers;
+
+ typedef typename std::deque< ReBufferAge < ReBufferPtr > >::iterator outputBuffersI;
+
+ //mutex protecting access to outputBuffers.
+ std::mutex m_mutex;
};
@@ -210,20 +183,20 @@ public:
//If wait < 0, the wait in infinite until the thread dies.
bool isTerminated(int waitMs = 0);
- virtual void onBindOutput(std::string name, ThreadQueueBase* threadQueue);
- virtual void onBindInput(std::string name, ThreadQueueBase* threadQueue);
+ virtual void onBindOutput(std::string name, ThreadQueueBasePtr threadQueue);
+ virtual void onBindInput(std::string name, ThreadQueueBasePtr threadQueue);
- void setInputQueue(std::string qname, ThreadQueueBase *threadQueue);
- ThreadQueueBase *getInputQueue(std::string qname);
- void setOutputQueue(std::string qname, ThreadQueueBase *threadQueue);
- ThreadQueueBase *getOutputQueue(std::string qname);
+ void setInputQueue(std::string qname, ThreadQueueBasePtr threadQueue);
+ ThreadQueueBasePtr getInputQueue(std::string qname);
+ void setOutputQueue(std::string qname, ThreadQueueBasePtr threadQueue);
+ ThreadQueueBasePtr getOutputQueue(std::string qname);
protected:
- std::map<std::string, ThreadQueueBase *, map_string_less> input_queues;
- std::map<std::string, ThreadQueueBase *, map_string_less> output_queues;
+ std::map<std::string, ThreadQueueBasePtr, map_string_less> input_queues;
+ std::map<std::string, ThreadQueueBasePtr, map_string_less> output_queues;
//this protects against concurrent changes in input/output bindings: get/set/Input/OutPutQueue
- mutable std::mutex m_queue_bindings_mutex;
+ std::mutex m_queue_bindings_mutex;
//true when a termination is ordered
std::atomic_bool stopping;
diff --git a/src/ModemProperties.cpp b/src/ModemProperties.cpp
index 9984bdc..4fa02df 100644
--- a/src/ModemProperties.cpp
+++ b/src/ModemProperties.cpp
@@ -101,7 +101,7 @@ void ModemProperties::initDefaultProperties() {
defaultProps["._audio_output"] = addArgInfoProperty(m_propertyGrid, outputArg);
}
-void ModemProperties::initProperties(ModemArgInfoList newArgs, DemodulatorInstance *demodInstance) {
+void ModemProperties::initProperties(ModemArgInfoList newArgs, DemodulatorInstancePtr demodInstance) {
args = newArgs;
demodContext = demodInstance;
diff --git a/src/ModemProperties.h b/src/ModemProperties.h
index 268e366..c708f61 100644
--- a/src/ModemProperties.h
+++ b/src/ModemProperties.h
@@ -24,7 +24,7 @@ public:
~ModemProperties();
void initDefaultProperties();
- void initProperties(ModemArgInfoList newArgs, DemodulatorInstance *demodInstance);
+ void initProperties(ModemArgInfoList newArgs, DemodulatorInstancePtr demodInstance);
bool isMouseInView();
void setCollapsed(bool state);
bool isCollapsed();
@@ -46,7 +46,8 @@ private:
wxBoxSizer* bSizer;
wxPropertyGrid* m_propertyGrid;
ModemArgInfoList args;
- DemodulatorInstance *demodContext;
+ DemodulatorInstancePtr demodContext;
+
std::map<std::string, wxPGProperty *> props;
bool mouseInView, collapsed;
diff --git a/src/audio/AudioFile.cpp b/src/audio/AudioFile.cpp
new file mode 100644
index 0000000..6b15db7
--- /dev/null
+++ b/src/audio/AudioFile.cpp
@@ -0,0 +1,50 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
+#include "AudioFile.h"
+#include "CubicSDR.h"
+#include <sstream>
+
+AudioFile::AudioFile() {
+
+}
+
+AudioFile::~AudioFile() {
+
+}
+
+void AudioFile::setOutputFileName(std::string filename) {
+ filenameBase = filename;
+}
+
+std::string AudioFile::getOutputFileName() {
+
+ std::string recPath = wxGetApp().getConfig()->getRecordingPath();
+
+ // Strip any invalid characters from the name
+ std::string stripChars("<>:\"/\\|?*");
+ std::string filenameBaseSafe = filenameBase;
+
+ for (size_t i = 0, iMax = filenameBaseSafe.length(); i < iMax; i++) {
+ if (stripChars.find(filenameBaseSafe[i]) != std::string::npos) {
+ filenameBaseSafe.replace(i,1,"_");
+ }
+ }
+
+ // Create output file name
+ std::stringstream outputFileName;
+ outputFileName << recPath << filePathSeparator << filenameBaseSafe;
+
+ int idx = 0;
+
+ // If the file exists; then find the next non-existing file in sequence.
+ std::string fileNameCandidate = outputFileName.str();
+
+ while (FILE *file = fopen((fileNameCandidate + "." + getExtension()).c_str(), "r")) {
+ fclose(file);
+ fileNameCandidate = outputFileName.str() + "-" + std::to_string(++idx);
+ }
+
+ return fileNameCandidate + "." + getExtension();
+}
+
diff --git a/src/audio/AudioFile.h b/src/audio/AudioFile.h
new file mode 100644
index 0000000..c8636b8
--- /dev/null
+++ b/src/audio/AudioFile.h
@@ -0,0 +1,25 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
+#pragma once
+
+#include "AudioThread.h"
+
+class AudioFile
+{
+
+public:
+ AudioFile();
+ virtual ~AudioFile();
+
+ virtual void setOutputFileName(std::string filename);
+ virtual std::string getExtension() = 0;
+ virtual std::string getOutputFileName();
+
+ virtual bool writeToFile(AudioThreadInputPtr input) = 0;
+ virtual bool closeFile() = 0;
+
+protected:
+ std::string filenameBase;
+
+};
\ No newline at end of file
diff --git a/src/audio/AudioFileWAV.cpp b/src/audio/AudioFileWAV.cpp
new file mode 100644
index 0000000..d0629ab
--- /dev/null
+++ b/src/audio/AudioFileWAV.cpp
@@ -0,0 +1,219 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
+#include "AudioFileWAV.h"
+#include "CubicSDR.h"
+#include <iomanip>
+
+//limit file size to 2GB (- margin) for maximum compatibility.
+#define MAX_WAV_FILE_SIZE (0x7FFFFFFF - 1024)
+
+// Simple endian io read/write handling from
+// http://www.cplusplus.com/forum/beginner/31584/#msg171056
+namespace little_endian_io
+{
+ template <typename Word>
+ std::ostream& write_word(std::ostream& outs, Word value, unsigned size = sizeof(Word)) {
+ for (; size; --size, value >>= 8) {
+ outs.put(static_cast <char> (value & 0xFF));
+ }
+ return outs;
+ }
+
+ template <typename Word>
+ std::istream& read_word(std::istream& ins, Word& value, unsigned size = sizeof(Word)) {
+ for (unsigned n = 0, value = 0; n < size; ++n) {
+ value |= ins.get() << (8 * n);
+ }
+ return ins;
+ }
+}
+
+namespace big_endian_io
+{
+ template <typename Word>
+ std::ostream& write_word(std::ostream& outs, Word value, unsigned size = sizeof(Word)) {
+ while (size) {
+ outs.put(static_cast <char> ((value >> (8 * --size)) & 0xFF));
+ }
+ return outs;
+ }
+
+ template <typename Word>
+ std::istream& read_word(std::istream& ins, Word& value, unsigned size = sizeof(Word)) {
+ for (value = 0; size; --size) {
+ value = (value << 8) | ins.get();
+ }
+ return ins;
+ }
+}
+
+using namespace little_endian_io;
+
+AudioFileWAV::AudioFileWAV() : AudioFile() {
+}
+
+AudioFileWAV::~AudioFileWAV() {
+}
+
+
+std::string AudioFileWAV::getExtension()
+{
+ return "wav";
+}
+
+bool AudioFileWAV::writeToFile(AudioThreadInputPtr input)
+{
+ if (!outputFileStream.is_open()) {
+
+ std::string ofName = getOutputFileName();
+
+ outputFileStream.open(ofName.c_str(), std::ios::binary);
+ currentFileSize = 0;
+
+ writeHeaderToFileStream(input);
+ }
+
+ size_t maxRoomInCurrentFileInSamples = getMaxWritableNumberOfSamples(input);
+
+ if (maxRoomInCurrentFileInSamples >= input->data.size()) {
+ writePayloadToFileStream(input, 0, input->data.size());
+ }
+ else {
+ //we complete the current file and open another:
+ writePayloadToFileStream(input, 0, maxRoomInCurrentFileInSamples);
+
+ closeFile();
+
+ // Open a new file with the next sequence number, and dump the rest of samples in it.
+ currentSequenceNumber++;
+ currentFileSize = 0;
+
+ std::string ofName = getOutputFileName();
+ outputFileStream.open(ofName.c_str(), std::ios::binary);
+
+ writeHeaderToFileStream(input);
+ writePayloadToFileStream(input, maxRoomInCurrentFileInSamples, input->data.size());
+ }
+
+ return true;
+}
+
+bool AudioFileWAV::closeFile()
+{
+ if (outputFileStream.is_open()) {
+ size_t file_length = outputFileStream.tellp();
+
+ // Fix the data chunk header to contain the data size
+ outputFileStream.seekp(dataChunkPos + 4);
+ write_word(outputFileStream, file_length - dataChunkPos + 8);
+
+ // Fix the file header to contain the proper RIFF chunk size, which is (file size - 8) bytes
+ outputFileStream.seekp(0 + 4);
+ write_word(outputFileStream, file_length - 8, 4);
+
+ outputFileStream.close();
+ currentFileSize = 0;
+ }
+
+ return true;
+}
+
+void AudioFileWAV::writeHeaderToFileStream(AudioThreadInputPtr input) {
+
+ // Based on simple wav file output code from
+ // http://www.cplusplus.com/forum/beginner/166954/
+
+ // Write the wav file headers
+ outputFileStream << "RIFF----WAVEfmt "; // (chunk size to be filled in later)
+ write_word(outputFileStream, 16, 4); // no extension data
+ write_word(outputFileStream, 1, 2); // PCM - integer samples
+ write_word(outputFileStream, input->channels, 2); // channels
+ write_word(outputFileStream, input->sampleRate, 4); // samples per second (Hz)
+ write_word(outputFileStream, (input->sampleRate * 16 * input->channels) / 8, 4); // (Sample Rate * BitsPerSample * Channels) / 8
+ write_word(outputFileStream, input->channels * 2, 2); // data block size (size of integer samples, one for each channel, in bytes)
+ write_word(outputFileStream, 16, 2); // number of bits per sample (use a multiple of 8)
+
+ // Write the data chunk header
+ dataChunkPos = outputFileStream.tellp();
+ currentFileSize = dataChunkPos;
+ outputFileStream << "data----"; // (chunk size to be filled in later)
+}
+
+void AudioFileWAV::writePayloadToFileStream(AudioThreadInputPtr input, size_t startInputPosition, size_t endInputPosition) {
+
+ // Prevent clipping
+ float intScale = (input->peak < 1.0) ? 32767.0f : (32767.0f / input->peak);
+
+ if (input->channels == 1) {
+ for (size_t i = startInputPosition, iMax = endInputPosition; i < iMax; i++) {
+
+ write_word(outputFileStream, int(input->data[i] * intScale), 2);
+
+ currentFileSize += 2;
+ }
+ }
+ else if (input->channels == 2) {
+ for (size_t i = startInputPosition, iMax = endInputPosition / 2; i < iMax; i++) {
+
+ write_word(outputFileStream, int(input->data[i * 2] * intScale), 2);
+ write_word(outputFileStream, int(input->data[i * 2 + 1] * intScale), 2);
+
+ currentFileSize += 4;
+ }
+ }
+}
+
+size_t AudioFileWAV::getMaxWritableNumberOfSamples(AudioThreadInputPtr input) {
+
+ long long remainingBytesInFile = (long long)(MAX_WAV_FILE_SIZE) - currentFileSize;
+
+ return (size_t)(remainingBytesInFile / (input->channels * 2));
+
+}
+
+void AudioFileWAV::setOutputFileName(std::string filename) {
+
+ if (filename != filenameBase) {
+
+ currentSequenceNumber = 0;
+ }
+
+ AudioFile::setOutputFileName(filename);
+}
+
+std::string AudioFileWAV::getOutputFileName() {
+
+ std::string recPath = wxGetApp().getConfig()->getRecordingPath();
+
+ // Strip any invalid characters from the name
+ std::string stripChars("<>:\"/\\|?*");
+ std::string filenameBaseSafe = filenameBase;
+
+ for (size_t i = 0, iMax = filenameBaseSafe.length(); i < iMax; i++) {
+ if (stripChars.find(filenameBaseSafe[i]) != std::string::npos) {
+ filenameBaseSafe.replace(i, 1, "_");
+ }
+ }
+
+ // Create output file name
+ std::stringstream outputFileName;
+ outputFileName << recPath << filePathSeparator << filenameBaseSafe;
+
+ //customized part: append a sequence number.
+ if (currentSequenceNumber > 0) {
+ outputFileName << "_" << std::setfill('0') << std::setw(3) << currentSequenceNumber;
+ }
+
+ int idx = 0;
+
+ // If the file exists; then find the next non-existing file in sequence.
+ std::string fileNameCandidate = outputFileName.str();
+
+ while (FILE *file = fopen((fileNameCandidate + "." + getExtension()).c_str(), "r")) {
+ fclose(file);
+ fileNameCandidate = outputFileName.str() + "-" + std::to_string(++idx);
+ }
+
+ return fileNameCandidate + "." + getExtension();
+}
\ No newline at end of file
diff --git a/src/audio/AudioFileWAV.h b/src/audio/AudioFileWAV.h
new file mode 100644
index 0000000..1dc9672
--- /dev/null
+++ b/src/audio/AudioFileWAV.h
@@ -0,0 +1,42 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
+#pragma once
+
+#include "AudioFile.h"
+
+#include <fstream>
+
+class AudioFileWAV : public AudioFile {
+
+public:
+ AudioFileWAV();
+ ~AudioFileWAV();
+
+ //override to manage name change with multi-part WAV.
+ virtual void setOutputFileName(std::string filename);
+
+ //override of the base method to generate multi-part
+ //WAV to overcome the WAV format size limit.
+ virtual std::string getOutputFileName();
+
+ virtual std::string getExtension();
+
+ virtual bool writeToFile(AudioThreadInputPtr input);
+ virtual bool closeFile();
+
+protected:
+ std::ofstream outputFileStream;
+ size_t dataChunkPos;
+ long long currentFileSize = 0;
+ int currentSequenceNumber = 0;
+
+private:
+
+ size_t getMaxWritableNumberOfSamples(AudioThreadInputPtr input);
+
+ void writeHeaderToFileStream(AudioThreadInputPtr input);
+
+ //write [startInputPosition; endInputPosition[ samples from input into the file.
+ void writePayloadToFileStream(AudioThreadInputPtr input, size_t startInputPosition, size_t endInputPosition);
+};
\ No newline at end of file
diff --git a/src/audio/AudioSinkFileThread.cpp b/src/audio/AudioSinkFileThread.cpp
new file mode 100644
index 0000000..e49b57b
--- /dev/null
+++ b/src/audio/AudioSinkFileThread.cpp
@@ -0,0 +1,140 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
+#include "AudioSinkFileThread.h"
+#include <ctime>
+
+#define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000)
+
+AudioSinkFileThread::AudioSinkFileThread() : AudioSinkThread() {
+
+}
+
+AudioSinkFileThread::~AudioSinkFileThread() {
+ if (audioFileHandler != nullptr) {
+ audioFileHandler->closeFile();
+ }
+}
+
+void AudioSinkFileThread::sink(AudioThreadInputPtr input) {
+ if (!audioFileHandler) {
+ return;
+ }
+
+ //by default, always write something
+ bool isSomethingToWrite = true;
+
+ if (input->is_squelch_active) {
+
+ if (squelchOption == SQUELCH_RECORD_SILENCE) {
+
+ //patch with "silence"
+ input->data.assign(input->data.size(), 0.0f);
+ input->peak = 0.0f;
+ }
+ else if (squelchOption == SQUELCH_SKIP_SILENCE) {
+ isSomethingToWrite = false;
+ }
+ }
+
+ //else, nothing to do record as if squelch was not enabled.
+
+ if (!isSomethingToWrite) {
+ return;
+ }
+
+ if (fileTimeLimit > 0) {
+ durationMeasurement.update();
+
+ //duration exeeded, close this file and create another
+ //with "now" as timestamp.
+ if (durationMeasurement.getSeconds() > fileTimeLimit) {
+
+ audioFileHandler->closeFile();
+
+ //initialize the filename of the AudioFile with the current time
+ time_t t = std::time(nullptr);
+ tm ltm = *std::localtime(&t);
+
+ // GCC 5+
+ // fileName << "_" << std::put_time(<m, "%d-%m-%Y_%H-%M-%S");
+
+ char timeStr[512];
+ //International format: Year.Month.Day, also lexicographically sortable
+ strftime(timeStr, sizeof(timeStr), "%Y-%m-%d_%H-%M-%S", <m);
+
+ audioFileHandler->setOutputFileName(fileNameBase + std::string("_") + timeStr);
+
+ //reset duration counter
+ durationMeasurement.start();
+ //the following writeToFile will take care of creating another file.
+ }
+ }
+
+ // forward to output file handler
+ audioFileHandler->writeToFile(input);
+}
+
+void AudioSinkFileThread::inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps) {
+ // close, set new parameters, adjust file name sequence and re-open?
+ if (!audioFileHandler) {
+ return;
+ }
+
+ audioFileHandler->closeFile();
+
+ //reset duration counter
+ durationMeasurement.start();
+}
+
+void AudioSinkFileThread::setAudioFileNameBase(const std::string& baseName) {
+
+ fileNameBase = baseName;
+}
+
+void AudioSinkFileThread::setAudioFileHandler(AudioFile * output) {
+ audioFileHandler = output;
+
+ //initialize the filename of the AudioFile with the current time
+ time_t t = std::time(nullptr);
+ tm ltm = *std::localtime(&t);
+
+ // GCC 5+
+ // fileName << "_" << std::put_time(<m, "%d-%m-%Y_%H-%M-%S");
+
+ char timeStr[512];
+ //International format: Year.Month.Day, also lexicographically sortable
+ strftime(timeStr, sizeof(timeStr), "%Y-%m-%d_%H-%M-%S", <m);
+
+ audioFileHandler->setOutputFileName(fileNameBase + std::string("_") + timeStr);
+
+ // reset Timer
+ durationMeasurement.start();
+}
+
+void AudioSinkFileThread::setSquelchOption(int squelchOptEnumValue) {
+
+ if (squelchOptEnumValue == AudioSinkFileThread::SQUELCH_RECORD_SILENCE) {
+ squelchOption = AudioSinkFileThread::SQUELCH_RECORD_SILENCE;
+ }
+ else if (squelchOptEnumValue == AudioSinkFileThread::SQUELCH_SKIP_SILENCE) {
+ squelchOption = AudioSinkFileThread::SQUELCH_SKIP_SILENCE;
+ }
+ else if (squelchOptEnumValue == AudioSinkFileThread::SQUELCH_RECORD_ALWAYS) {
+ squelchOption = AudioSinkFileThread::SQUELCH_RECORD_ALWAYS;
+ }
+ else {
+ squelchOption = AudioSinkFileThread::SQUELCH_RECORD_SILENCE;
+ }
+}
+
+// Time limit
+void AudioSinkFileThread::setFileTimeLimit(int nbSeconds) {
+
+ if (nbSeconds > 0) {
+ fileTimeLimit = nbSeconds;
+ }
+ else {
+ fileTimeLimit = 0;
+ }
+}
diff --git a/src/audio/AudioSinkFileThread.h b/src/audio/AudioSinkFileThread.h
new file mode 100644
index 0000000..f0bdb73
--- /dev/null
+++ b/src/audio/AudioSinkFileThread.h
@@ -0,0 +1,50 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
+#pragma once
+
+#include "AudioSinkThread.h"
+#include "AudioFile.h"
+#include "Timer.h"
+
+class AudioSinkFileThread : public AudioSinkThread {
+
+public:
+ AudioSinkFileThread();
+ ~AudioSinkFileThread();
+
+ enum SquelchOption {
+ SQUELCH_RECORD_SILENCE = 0, // default value, record as a user would hear it.
+ SQUELCH_SKIP_SILENCE = 1, // skip below-squelch level.
+ SQUELCH_RECORD_ALWAYS = 2, // record irrespective of the squelch level.
+ SQUELCH_RECORD_MAX
+ };
+
+ virtual void sink(AudioThreadInputPtr input);
+ virtual void inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps);
+
+ void setAudioFileHandler(AudioFile *output);
+
+ void setAudioFileNameBase(const std::string& baseName);
+
+ //Squelch
+ void setSquelchOption(int squelchOptEnumValue);
+
+ // Time limit
+ void setFileTimeLimit(int nbSeconds);
+
+protected:
+
+ std::string fileNameBase;
+
+ AudioFile *audioFileHandler = nullptr;
+
+ SquelchOption squelchOption = SQUELCH_RECORD_SILENCE;
+ int fileTimeLimit = 0;
+
+ int fileTimeDurationSeconds = -1;
+
+ Timer durationMeasurement;
+
+};
+
diff --git a/src/audio/AudioSinkThread.cpp b/src/audio/AudioSinkThread.cpp
new file mode 100644
index 0000000..edf3152
--- /dev/null
+++ b/src/audio/AudioSinkThread.cpp
@@ -0,0 +1,54 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
+#include "AudioSinkThread.h"
+
+#define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000)
+
+AudioSinkThread::AudioSinkThread() {
+ inputQueuePtr = std::make_shared<AudioThreadInputQueue>();
+ inputQueuePtr->set_max_num_items(1000);
+ setInputQueue("input", inputQueuePtr);
+}
+
+AudioSinkThread::~AudioSinkThread() {
+
+}
+
+void AudioSinkThread::run() {
+#ifdef __APPLE__
+ pthread_t tID = pthread_self(); // ID of this thread
+ int priority = sched_get_priority_max(SCHED_RR) - 1;
+ sched_param prio = { priority }; // scheduling priority of thread
+ pthread_setschedparam(tID, SCHED_RR, &prio);
+#endif
+
+ AudioThreadInputPtr inp;
+ AudioThreadInput inputRef;
+
+ while (!stopping) {
+ if (!inputQueuePtr->pop(inp, HEARTBEAT_CHECK_PERIOD_MICROS)) {
+ continue;
+ }
+
+ if (inputRef.channels != inp->channels ||
+ inputRef.frequency != inp->frequency ||
+ inputRef.inputRate != inp->inputRate ||
+ inputRef.sampleRate != inp->sampleRate) {
+
+ inputChanged(inputRef, inp);
+
+ inputRef.channels = inp->channels;
+ inputRef.frequency = inp->frequency;
+ inputRef.inputRate = inp->inputRate;
+ inputRef.sampleRate = inp->sampleRate;
+ }
+
+ sink(inp);
+ }
+}
+
+void AudioSinkThread::terminate() {
+ IOThread::terminate();
+ inputQueuePtr->flush();
+}
diff --git a/src/audio/AudioSinkThread.h b/src/audio/AudioSinkThread.h
new file mode 100644
index 0000000..754c75d
--- /dev/null
+++ b/src/audio/AudioSinkThread.h
@@ -0,0 +1,25 @@
+// Copyright (c) Charles J. Cliffe
+// SPDX-License-Identifier: GPL-2.0+
+
+#pragma once
+
+#include "AudioThread.h"
+
+class AudioSinkThread : public IOThread {
+
+public:
+
+ AudioSinkThread();
+ virtual ~AudioSinkThread();
+
+ virtual void run();
+ virtual void terminate();
+
+ virtual void sink(AudioThreadInputPtr input) = 0;
+ virtual void inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps) = 0;
+
+protected:
+ std::recursive_mutex m_mutex;
+ AudioThreadInputQueuePtr inputQueuePtr;
+
+};
diff --git a/src/audio/AudioThread.cpp b/src/audio/AudioThread.cpp
index e0d4ad1..e5db98b 100644
--- a/src/audio/AudioThread.cpp
+++ b/src/audio/AudioThread.cpp
@@ -11,23 +11,26 @@
#include <memory.h>
#include <mutex>
+//50 ms
+#define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000)
std::map<int, AudioThread *> AudioThread::deviceController;
std::map<int, int> AudioThread::deviceSampleRate;
std::map<int, std::thread *> AudioThread::deviceThread;
-AudioThread::AudioThread() : IOThread(),
- currentInput(nullptr), inputQueue(nullptr), nBufferFrames(1024), sampleRate(0) {
+std::recursive_mutex AudioThread::m_device_mutex;
- audioQueuePtr.store(0);
- underflowCount.store(0);
+AudioThread::AudioThread() : IOThread(), nBufferFrames(1024), sampleRate(0) {
+
+ audioQueuePtr = 0;
+ underflowCount = 0;
active.store(false);
outputDevice.store(-1);
gain = 1.0;
}
AudioThread::~AudioThread() {
-
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
}
std::recursive_mutex & AudioThread::getMutex()
@@ -46,10 +49,10 @@ void AudioThread::bindThread(AudioThread *other) {
void AudioThread::removeThread(AudioThread *other) {
- std::lock_guard<std::recursive_mutex> lock(m_mutex);
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ auto i = std::find(boundThreads.begin(), boundThreads.end(), other);
- std::vector<AudioThread *>::iterator i;
- i = std::find(boundThreads.begin(), boundThreads.end(), other);
if (i != boundThreads.end()) {
boundThreads.erase(i);
}
@@ -57,9 +60,9 @@ void AudioThread::removeThread(AudioThread *other) {
void AudioThread::deviceCleanup() {
- std::map<int, AudioThread *>::iterator i;
+ std::lock_guard<std::recursive_mutex> lock(m_device_mutex);
- for (i = deviceController.begin(); i != deviceController.end(); i++) {
+ for (auto i = deviceController.begin(); i != deviceController.end(); i++) {
i->second->terminate();
}
}
@@ -71,7 +74,7 @@ static int audioCallback(void *outputBuffer, void * /* inputBuffer */, unsigned
//Zero output buffer in all cases: this allow to mute audio if no AudioThread data is
//actually active.
- memset(out, 0, nBufferFrames * 2 * sizeof(float));
+ ::memset(out, 0, nBufferFrames * 2 * sizeof(float));
AudioThread *src = (AudioThread *) userData;
@@ -82,7 +85,7 @@ static int audioCallback(void *outputBuffer, void * /* inputBuffer */, unsigned
}
if (status) {
- std::cout << "Audio buffer underflow.." << (src->underflowCount++) << std::endl;
+ std::cout << "Audio buffer underflow.." << (src->underflowCount++) << std::endl << std::flush;
}
if (src->boundThreads.empty()) {
@@ -123,7 +126,7 @@ static int audioCallback(void *outputBuffer, void * /* inputBuffer */, unsigned
if (srcmix->currentInput->sampleRate == src->getSampleRate()) {
break;
}
- srcmix->currentInput->decRefCount();
+
}
srcmix->currentInput = nullptr;
} //end while
@@ -140,7 +143,7 @@ static int audioCallback(void *outputBuffer, void * /* inputBuffer */, unsigned
if (!srcmix->inputQueue->empty()) {
srcmix->audioQueuePtr = 0;
if (srcmix->currentInput) {
- srcmix->currentInput->decRefCount();
+
srcmix->currentInput = nullptr;
}
@@ -160,7 +163,7 @@ static int audioCallback(void *outputBuffer, void * /* inputBuffer */, unsigned
if (srcmix->audioQueuePtr >= srcmix->currentInput->data.size()) {
srcmix->audioQueuePtr = 0;
if (srcmix->currentInput) {
- srcmix->currentInput->decRefCount();
+
srcmix->currentInput = nullptr;
}
@@ -187,7 +190,7 @@ static int audioCallback(void *outputBuffer, void * /* inputBuffer */, unsigned
if (srcmix->audioQueuePtr >= srcmix->currentInput->data.size()) {
srcmix->audioQueuePtr = 0;
if (srcmix->currentInput) {
- srcmix->currentInput->decRefCount();
+
srcmix->currentInput = nullptr;
}
@@ -277,23 +280,47 @@ void AudioThread::enumerateDevices(std::vector<RtAudio::DeviceInfo> &devs) {
void AudioThread::setDeviceSampleRate(int deviceId, int sampleRate) {
+ AudioThread* matchingAudioThread = nullptr;
- if (deviceController.find(deviceId) != deviceController.end()) {
- AudioThreadCommand refreshDevice;
- refreshDevice.cmd = AudioThreadCommand::AUDIO_THREAD_CMD_SET_SAMPLE_RATE;
- refreshDevice.int_value = sampleRate;
- //VSO : blocking push !
- deviceController[deviceId]->getCommandQueue()->push(refreshDevice);
- }
+ //scope lock here to minimize the common unique static lock contention
+ {
+ std::lock_guard<std::recursive_mutex> lock(m_device_mutex);
+
+ if (deviceController.find(deviceId) != deviceController.end()) {
+
+ matchingAudioThread = deviceController[deviceId];
+ }
+ }
+
+ //out-of-lock test
+ if (matchingAudioThread != nullptr) {
+
+ AudioThreadCommand refreshDevice;
+ refreshDevice.cmd = AudioThreadCommand::AUDIO_THREAD_CMD_SET_SAMPLE_RATE;
+ refreshDevice.int_value = sampleRate;
+ //VSO : blocking push !
+ matchingAudioThread->getCommandQueue()->push(refreshDevice);
+ }
}
void AudioThread::setSampleRate(int sampleRate) {
- std::lock_guard<std::recursive_mutex> lock(m_mutex);
+ bool outputIsThis = false;
+
+ //scope lock here to minimize the common unique static lock contention
+ {
+ std::lock_guard<std::recursive_mutex> lock(m_device_mutex);
- if (deviceController[outputDevice.load()] == this) {
- deviceSampleRate[outputDevice.load()] = sampleRate;
+ if (deviceController[outputDevice.load()] == this) {
+ outputIsThis = true;
+ deviceSampleRate[outputDevice.load()] = sampleRate;
+ }
+ }
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ if (outputIsThis) {
+
dac.stopStream();
dac.closeStream();
@@ -302,14 +329,12 @@ void AudioThread::setSampleRate(int sampleRate) {
srcmix->setSampleRate(sampleRate);
}
- std::vector<DemodulatorInstance *>::iterator demod_i;
- std::vector<DemodulatorInstance *> *demodulators;
+ //make a local copy, snapshot of the list of demodulators
+ std::vector<DemodulatorInstancePtr> demodulators = wxGetApp().getDemodMgr().getDemodulators();
- demodulators = &wxGetApp().getDemodMgr().getDemodulators();
-
- for (demod_i = demodulators->begin(); demod_i != demodulators->end(); demod_i++) {
- if ((*demod_i)->getOutputDevice() == outputDevice.load()) {
- (*demod_i)->setAudioSampleRate(sampleRate);
+ for (auto demod : demodulators) {
+ if (demod->getOutputDevice() == outputDevice.load()) {
+ demod->setAudioSampleRate(sampleRate);
}
}
@@ -328,7 +353,8 @@ int AudioThread::getSampleRate() {
void AudioThread::setupDevice(int deviceId) {
- std::lock_guard<std::recursive_mutex> lock(m_mutex);
+ //global lock to setup the device...
+ std::lock_guard<std::recursive_mutex> lock(m_device_mutex);
parameters.deviceId = deviceId;
parameters.nChannels = 2;
@@ -381,6 +407,7 @@ void AudioThread::setupDevice(int deviceId) {
}
int AudioThread::getOutputDevice() {
+
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (outputDevice == -1) {
@@ -391,7 +418,8 @@ int AudioThread::getOutputDevice() {
void AudioThread::setInitOutputDevice(int deviceId, int sampleRate) {
- std::lock_guard<std::recursive_mutex> lock(m_mutex);
+ //global lock
+ std::lock_guard<std::recursive_mutex> lock(m_device_mutex);
outputDevice = deviceId;
if (sampleRate == -1) {
@@ -422,14 +450,16 @@ void AudioThread::run() {
setupDevice((outputDevice.load() == -1) ? (dac.getDefaultOutputDevice()) : outputDevice.load());
// std::cout << "Audio thread started." << std::endl;
-
- inputQueue = static_cast<AudioThreadInputQueue *>(getInputQueue("AudioDataInput"));
+
+ inputQueue = std::static_pointer_cast<AudioThreadInputQueue>(getInputQueue("AudioDataInput"));
//Infinite loop, witing for commands or for termination
while (!stopping) {
AudioThreadCommand command;
- cmdQueue.pop(command);
+ if (!cmdQueue.pop(command, HEARTBEAT_CHECK_PERIOD_MICROS)) {
+ continue;
+ }
if (command.cmd == AudioThreadCommand::AUDIO_THREAD_CMD_SET_DEVICE) {
setupDevice(command.int_value);
@@ -438,27 +468,22 @@ void AudioThread::run() {
setSampleRate(command.int_value);
}
}
-
- //Thread termination, prevent fancy things to happen, lock the whole thing:
- //This way audioThreadCallback is rightly protected from thread termination
- std::lock_guard<std::recursive_mutex> lock(m_mutex);
-
+
// Drain any remaining inputs, with a non-blocking pop
- AudioThreadInput *ref;
- while (inputQueue && inputQueue->try_pop(ref)) {
-
- if (ref) {
- ref->decRefCount();
- }
- } //end while
-
- //Nullify currentInput...
- if (currentInput) {
- currentInput->setRefCount(0);
- currentInput = nullptr;
+ if (inputQueue != nullptr) {
+ inputQueue->flush();
}
- //Stop
+ //Thread termination, prevent fancy things to happen, lock the whole thing:
+ //This way audioThreadCallback is rightly protected from thread termination
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ //Nullify currentInput...
+ currentInput = nullptr;
+
+ //Stop : this affects the device list , so must be protected globally.
+ std::lock_guard<std::recursive_mutex> global_lock(m_device_mutex);
+
if (deviceController[parameters.deviceId] != this) {
deviceController[parameters.deviceId]->removeThread(this);
} else {
@@ -479,9 +504,6 @@ void AudioThread::run() {
void AudioThread::terminate() {
IOThread::terminate();
- AudioThreadCommand endCond; // push an empty input to bump the queue
- //VSO: blocking push
- cmdQueue.push(endCond);
}
bool AudioThread::isActive() {
@@ -491,25 +513,34 @@ bool AudioThread::isActive() {
}
void AudioThread::setActive(bool state) {
-
+
+ AudioThread* matchingAudioThread = nullptr;
+
+ //scope lock here to minimize the common unique static lock contention
+ {
+ std::lock_guard<std::recursive_mutex> lock(m_device_mutex);
+
+ if (deviceController.find(parameters.deviceId) != deviceController.end()) {
+
+ matchingAudioThread = deviceController[parameters.deviceId];
+ }
+ }
+
std::lock_guard<std::recursive_mutex> lock(m_mutex);
- AudioThreadInput *dummy;
+ if (matchingAudioThread == nullptr) {
+ return;
+ }
+
if (state && !active && inputQueue) {
- deviceController[parameters.deviceId]->bindThread(this);
+ matchingAudioThread->bindThread(this);
} else if (!state && active) {
- deviceController[parameters.deviceId]->removeThread(this);
+ matchingAudioThread->removeThread(this);
}
// Activity state changing, clear any inputs
if(inputQueue) {
-
- while (inputQueue->try_pop(dummy)) { // flush queue, non-blocking pop
-
- if (dummy) {
- dummy->decRefCount();
- }
- }
+ inputQueue->flush();
}
active = state;
}
@@ -519,21 +550,12 @@ AudioThreadCommandQueue *AudioThread::getCommandQueue() {
}
void AudioThread::setGain(float gain_in) {
-
- std::lock_guard<std::recursive_mutex> lock(m_mutex);
-
- if (gain < 0.0) {
- gain = 0.0;
+
+ if (gain_in < 0.0) {
+ gain_in = 0.0;
}
- if (gain > 2.0) {
- gain = 2.0;
+ if (gain_in > 2.0) {
+ gain_in = 2.0;
}
gain = gain_in;
}
-
-float AudioThread::getGain() {
-
- std::lock_guard<std::recursive_mutex> lock(m_mutex);
-
- return gain;
-}
diff --git a/src/audio/AudioThread.h b/src/audio/AudioThread.h
index ce349c0..7585faa 100644
--- a/src/audio/AudioThread.h
+++ b/src/audio/AudioThread.h
@@ -8,12 +8,12 @@
#include <map>
#include <string>
#include <atomic>
-
+#include <memory>
#include "ThreadBlockingQueue.h"
#include "RtAudio.h"
#include "DemodDefs.h"
-class AudioThreadInput: public ReferenceCounter {
+class AudioThreadInput {
public:
long long frequency;
int inputRate;
@@ -21,18 +21,43 @@ public:
int channels;
float peak;
int type;
+ bool is_squelch_active;
+
std::vector<float> data;
AudioThreadInput() :
- frequency(0), sampleRate(0), channels(0), peak(0) {
+ frequency(0), inputRate(0), sampleRate(0), channels(0), peak(0), type(0), is_squelch_active(false) {
}
- ~AudioThreadInput() {
- std::lock_guard < std::recursive_mutex > lock(m_mutex);
+
+ AudioThreadInput(AudioThreadInput *copyFrom) {
+ copy(copyFrom);
+ }
+
+ void copy(AudioThreadInput *copyFrom) {
+ frequency = copyFrom->frequency;
+ inputRate = copyFrom->inputRate;
+ sampleRate = copyFrom->sampleRate;
+ channels = copyFrom->channels;
+ peak = copyFrom->peak;
+ type = copyFrom->type;
+ is_squelch_active = copyFrom->is_squelch_active;
+ data.assign(copyFrom->data.begin(), copyFrom->data.end());
+ }
+
+
+ virtual ~AudioThreadInput() {
+
}
};
+typedef std::shared_ptr<AudioThreadInput> AudioThreadInputPtr;
+
+typedef ThreadBlockingQueue<AudioThreadInputPtr> DemodulatorThreadOutputQueue;
+
+typedef std::shared_ptr<DemodulatorThreadOutputQueue> DemodulatorThreadOutputQueuePtr;
+
class AudioThreadCommand {
public:
enum AudioThreadCommandEnum {
@@ -47,30 +72,26 @@ public:
int int_value;
};
-typedef ThreadBlockingQueue<AudioThreadInput *> AudioThreadInputQueue;
+typedef ThreadBlockingQueue<AudioThreadInputPtr> AudioThreadInputQueue;
typedef ThreadBlockingQueue<AudioThreadCommand> AudioThreadCommandQueue;
+typedef std::shared_ptr<AudioThreadInputQueue> AudioThreadInputQueuePtr;
+typedef std::shared_ptr<AudioThreadCommandQueue> AudioThreadCommandQueuePtr;
+
class AudioThread : public IOThread {
-public:
- AudioThreadInput *currentInput;
- AudioThreadInputQueue *inputQueue;
- std::atomic_uint audioQueuePtr;
- std::atomic_uint underflowCount;
- std::atomic_bool initialized;
- std::atomic_bool active;
- std::atomic_int outputDevice;
- float gain;
+public:
+
AudioThread();
- ~AudioThread();
+ virtual ~AudioThread();
static void enumerateDevices(std::vector<RtAudio::DeviceInfo> &devs);
- void setupDevice(int deviceId);
void setInitOutputDevice(int deviceId, int sampleRate=-1);
int getOutputDevice();
- void setSampleRate(int sampleRate);
+
int getSampleRate();
+
virtual void run();
virtual void terminate();
@@ -78,11 +99,31 @@ public:
void setActive(bool state);
void setGain(float gain_in);
- float getGain();
+
+ static std::map<int, int> deviceSampleRate;
AudioThreadCommandQueue *getCommandQueue();
+ //give access to the this AudioThread lock
+ std::recursive_mutex& getMutex();
+
+ static void deviceCleanup();
+ static void setDeviceSampleRate(int deviceId, int sampleRate);
+
+ //fields below, only to be used by other AudioThreads !
+ size_t underflowCount;
+ //protected by m_mutex
+ std::vector<AudioThread *> boundThreads;
+ AudioThreadInputQueuePtr inputQueue;
+ AudioThreadInputPtr currentInput;
+ size_t audioQueuePtr;
+ float gain;
+
private:
+
+ std::atomic_bool active;
+ std::atomic_int outputDevice;
+
RtAudio dac;
unsigned int nBufferFrames;
RtAudio::StreamOptions opts;
@@ -93,20 +134,16 @@ private:
//The own m_mutex protecting this AudioThread, in particular boundThreads
std::recursive_mutex m_mutex;
-public:
- //give access to the this AudioThread lock
- std::recursive_mutex& getMutex();
+ void setupDevice(int deviceId);
+ void setSampleRate(int sampleRate);
void bindThread(AudioThread *other);
void removeThread(AudioThread *other);
- static std::map<int,AudioThread *> deviceController;
- static std::map<int,int> deviceSampleRate;
- static std::map<int,std::thread *> deviceThread;
- static void deviceCleanup();
- static void setDeviceSampleRate(int deviceId, int sampleRate);
-
- //protected by m_mutex
- std::vector<AudioThread *> boundThreads;
+ static std::map<int, AudioThread *> deviceController;
+ static std::map<int, std::thread *> deviceThread;
+
+ //The mutex protecting static deviceController, deviceThread and deviceSampleRate access.
+ static std::recursive_mutex m_device_mutex;
};
diff --git a/src/demod/DemodDefs.h b/src/demod/DemodDefs.h
index d4a922a..bb0a63c 100644
--- a/src/demod/DemodDefs.h
+++ b/src/demod/DemodDefs.h
@@ -9,12 +9,12 @@
#include <vector>
#include <atomic>
#include <mutex>
+#include <memory>
#include "IOThread.h"
class DemodulatorThread;
-
class DemodulatorThreadControlCommand {
public:
enum DemodulatorThreadControlCommandEnum {
@@ -29,7 +29,7 @@ public:
std::string demodType;
};
-class DemodulatorThreadIQData: public ReferenceCounter {
+class DemodulatorThreadIQData {
public:
long long frequency;
long long sampleRate;
@@ -48,7 +48,7 @@ public:
return *this;
}
- ~DemodulatorThreadIQData() {
+ virtual ~DemodulatorThreadIQData() {
}
};
@@ -56,7 +56,7 @@ public:
class Modem;
class ModemKit;
-class DemodulatorThreadPostIQData: public ReferenceCounter {
+class DemodulatorThreadPostIQData {
public:
std::vector<liquid_float_complex> data;
@@ -71,13 +71,13 @@ public:
}
- ~DemodulatorThreadPostIQData() {
- std::lock_guard < std::recursive_mutex > lock(m_mutex);
+ virtual ~DemodulatorThreadPostIQData() {
+
}
};
-class DemodulatorThreadAudioData: public ReferenceCounter {
+class DemodulatorThreadAudioData {
public:
long long frequency;
unsigned int sampleRate;
@@ -95,11 +95,17 @@ public:
}
- ~DemodulatorThreadAudioData() {
+ virtual ~DemodulatorThreadAudioData() {
}
};
+typedef std::shared_ptr<DemodulatorThreadIQData> DemodulatorThreadIQDataPtr;
+typedef std::shared_ptr<DemodulatorThreadPostIQData> DemodulatorThreadPostIQDataPtr;
-typedef ThreadBlockingQueue<DemodulatorThreadIQData *> DemodulatorThreadInputQueue;
-typedef ThreadBlockingQueue<DemodulatorThreadPostIQData *> DemodulatorThreadPostInputQueue;
+typedef ThreadBlockingQueue<DemodulatorThreadIQDataPtr> DemodulatorThreadInputQueue;
+typedef ThreadBlockingQueue<DemodulatorThreadPostIQDataPtr> DemodulatorThreadPostInputQueue;
typedef ThreadBlockingQueue<DemodulatorThreadControlCommand> DemodulatorThreadControlCommandQueue;
+
+typedef std::shared_ptr<DemodulatorThreadInputQueue> DemodulatorThreadInputQueuePtr;
+typedef std::shared_ptr<DemodulatorThreadPostInputQueue> DemodulatorThreadPostInputQueuePtr;
+typedef std::shared_ptr<DemodulatorThreadControlCommandQueue> DemodulatorThreadControlCommandQueuePtr;
diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp
index 355daa6..39e9014 100644
--- a/src/demod/DemodulatorInstance.cpp
+++ b/src/demod/DemodulatorInstance.cpp
@@ -1,9 +1,17 @@
// Copyright (c) Charles J. Cliffe
// SPDX-License-Identifier: GPL-2.0+
+#include <memory>
+#include <iomanip>
+
#include "DemodulatorInstance.h"
#include "CubicSDR.h"
+#include "DemodulatorThread.h"
+#include "DemodulatorPreThread.h"
+#include "AudioSinkFileThread.h"
+#include "AudioFileWAV.h"
+
#if USE_HAMLIB
#include "RigThread.h"
#endif
@@ -42,6 +50,7 @@ DemodulatorInstance::DemodulatorInstance() {
active.store(false);
squelch.store(false);
muted.store(false);
+ recording.store(false);
deltaLock.store(false);
deltaLockOfs.store(0);
currentOutputDevice.store(-1);
@@ -52,9 +61,9 @@ DemodulatorInstance::DemodulatorInstance() {
label.store(new std::string("Unnamed"));
user_label.store(new std::wstring());
- pipeIQInputData = new DemodulatorThreadInputQueue;
+ pipeIQInputData = std::make_shared<DemodulatorThreadInputQueue>();
pipeIQInputData->set_max_num_items(100);
- pipeIQDemodData = new DemodulatorThreadPostInputQueue;
+ pipeIQDemodData = std::make_shared< DemodulatorThreadPostInputQueue>();
pipeIQInputData->set_max_num_items(100);
audioThread = new AudioThread();
@@ -63,10 +72,10 @@ DemodulatorInstance::DemodulatorInstance() {
demodulatorPreThread->setInputQueue("IQDataInput",pipeIQInputData);
demodulatorPreThread->setOutputQueue("IQDataOutput",pipeIQDemodData);
- pipeAudioData = new AudioThreadInputQueue;
+ pipeAudioData = std::make_shared<AudioThreadInputQueue>();
pipeAudioData->set_max_num_items(10);
- threadQueueControl = new DemodulatorThreadControlCommandQueue;
+ threadQueueControl = std::make_shared<DemodulatorThreadControlCommandQueue>();
threadQueueControl->set_max_num_items(2);
demodulatorThread = new DemodulatorThread(this);
@@ -78,25 +87,48 @@ DemodulatorInstance::DemodulatorInstance() {
}
DemodulatorInstance::~DemodulatorInstance() {
-#if ENABLE_DIGITAL_LAB
- delete activeOutput;
-#endif
- delete audioThread;
- delete demodulatorThread;
- delete demodulatorPreThread;
- delete pipeIQInputData;
- delete pipeIQDemodData;
- delete threadQueueControl;
- delete pipeAudioData;
- wxGetApp().getBookmarkMgr().updateActiveList();
+ std::lock_guard < std::recursive_mutex > lockData(m_thread_control_mutex);
+
+ //now that DemodulatorInstance are managed through shared_ptr, we
+ //should enter here ONLY when it is no longer used by any piece of code, anywhere.
+ //so active wait on IsTerminated(), then die.
+#define TERMINATION_SPIN_WAIT_MS (20)
+#define MAX_WAIT_FOR_TERMINATION_MS (3000.0)
+ //this is a stupid busy plus sleep loop
+ int nbCyclesToWait = (MAX_WAIT_FOR_TERMINATION_MS / TERMINATION_SPIN_WAIT_MS) + 1;
+ int currentCycle = 0;
+
+ while (currentCycle < nbCyclesToWait) {
+
+ if (isTerminated()) {
+ std::cout << "Garbage collected demodulator instance '" << getLabel() << "'... " << std::endl << std::flush;
+
+#if ENABLE_DIGITAL_LAB
+ delete activeOutput;
+#endif
+ delete demodulatorPreThread;
+ delete demodulatorThread;
+ delete audioThread;
+ delete audioSinkThread;
+
+ break;
+ }
+ else {
+ std::this_thread::sleep_for(std::chrono::milliseconds(TERMINATION_SPIN_WAIT_MS));
+ }
+ currentCycle++;
+ } //end while
}
-void DemodulatorInstance::setVisualOutputQueue(DemodulatorThreadOutputQueue *tQueue) {
+void DemodulatorInstance::setVisualOutputQueue(DemodulatorThreadOutputQueuePtr tQueue) {
demodulatorThread->setOutputQueue("AudioVisualOutput", tQueue);
}
void DemodulatorInstance::run() {
+
+ std::lock_guard < std::recursive_mutex > lockData(m_thread_control_mutex);
+
if (active) {
return;
}
@@ -127,8 +159,6 @@ void DemodulatorInstance::run() {
#endif
active = true;
-
- wxGetApp().getBookmarkMgr().updateActiveList();
}
void DemodulatorInstance::updateLabel(long long freq) {
@@ -149,10 +179,22 @@ void DemodulatorInstance::terminate() {
// std::cout << "Terminating demodulator audio thread.." << std::endl;
audioThread->terminate();
+
// std::cout << "Terminating demodulator thread.." << std::endl;
demodulatorThread->terminate();
+
// std::cout << "Terminating demodulator preprocessor thread.." << std::endl;
demodulatorPreThread->terminate();
+
+ if (audioSinkThread != nullptr) {
+ stopRecording();
+ }
+
+ //that will actually unblock the currently blocked push().
+ pipeIQInputData->flush();
+ pipeAudioData->flush();
+ pipeIQDemodData->flush();
+ threadQueueControl->flush();
}
std::string DemodulatorInstance::getLabel() {
@@ -166,20 +208,30 @@ void DemodulatorInstance::setLabel(std::string labelStr) {
bool DemodulatorInstance::isTerminated() {
- //
+ std::lock_guard < std::recursive_mutex > lockData(m_thread_control_mutex);
+
bool audioTerminated = audioThread->isTerminated();
bool demodTerminated = demodulatorThread->isTerminated();
bool preDemodTerminated = demodulatorPreThread->isTerminated();
+ bool audioSinkTerminated = (audioSinkThread == nullptr) || audioSinkThread->isTerminated();
- //Cleanup the worker threads, if the threads are indeed terminated
- if (audioTerminated) {
+ //Cleanup the worker threads, if the threads are indeed terminated.
+ // threads are linked as t_PreDemod ==> t_Demod ==> t_Audio
+ //so terminate in the same order to starve the following threads in succession.
+ //i.e waiting on timed-pop so able to se their stopping flag.
- if (t_Audio) {
- t_Audio->join();
+ if (preDemodTerminated) {
+
+ if (t_PreDemod) {
- delete t_Audio;
- t_Audio = nullptr;
- }
+#ifdef __APPLE__
+ pthread_join(t_PreDemod, NULL);
+#else
+ t_PreDemod->join();
+ delete t_PreDemod;
+#endif
+ t_PreDemod = nullptr;
+ }
}
if (demodTerminated) {
@@ -195,21 +247,30 @@ bool DemodulatorInstance::isTerminated() {
}
}
- if (preDemodTerminated) {
-
- if (t_PreDemod) {
+ if (audioTerminated) {
+ if (t_Audio) {
#ifdef __APPLE__
pthread_join(t_PreDemod, NULL);
#else
- t_PreDemod->join();
- delete t_PreDemod;
+ t_Audio->join();
+ delete t_Audio;
#endif
- t_PreDemod = nullptr;
- }
+ t_Audio = nullptr;
+ }
+ }
+
+ if (audioSinkTerminated) {
+
+ if (t_AudioSink != nullptr) {
+ t_AudioSink->join();
+
+ delete t_AudioSink;
+ t_AudioSink = nullptr;
+ }
}
- bool terminated = audioTerminated && demodTerminated && preDemodTerminated;
+ bool terminated = audioTerminated && demodTerminated && preDemodTerminated && audioSinkTerminated;
return terminated;
}
@@ -218,7 +279,7 @@ bool DemodulatorInstance::isActive() {
return active;
}
-void DemodulatorInstance::setActive(bool state) {
+void DemodulatorInstance::setActive(bool state) {
if (active && !state) {
#if ENABLE_DIGITAL_LAB
if (activeOutput) {
@@ -226,7 +287,9 @@ void DemodulatorInstance::setActive(bool state) {
}
#endif
audioThread->setActive(state);
+
DemodulatorThread::releaseSquelchLock(this);
+
} else if (!active && state) {
#if ENABLE_DIGITAL_LAB
if (activeOutput && getModemType() == "digital") {
@@ -406,7 +469,7 @@ void DemodulatorInstance::setFrequency(long long freq) {
}
#endif
#if USE_HAMLIB
- if (wxGetApp().rigIsActive() && wxGetApp().getRigThread()->getFollowModem() && wxGetApp().getDemodMgr().getLastActiveDemodulator() == this) {
+ if (wxGetApp().rigIsActive() && wxGetApp().getRigThread()->getFollowModem() && wxGetApp().getDemodMgr().getLastActiveDemodulator().get() == this) {
wxGetApp().getRigThread()->setFrequency(freq,true);
}
#endif
@@ -483,11 +546,26 @@ void DemodulatorInstance::setMuted(bool muted) {
wxGetApp().getDemodMgr().setLastMuted(muted);
}
+bool DemodulatorInstance::isRecording()
+{
+ return recording.load();
+}
+
+void DemodulatorInstance::setRecording(bool recording_in)
+{
+ if (!recording.load() && recording_in) {
+ startRecording();
+ }
+ else if (recording.load() && !recording_in) {
+ stopRecording();
+ }
+}
+
DemodVisualCue *DemodulatorInstance::getVisualCue() {
return &visualCue;
}
-DemodulatorThreadInputQueue *DemodulatorInstance::getIQInputDataPipe() {
+DemodulatorThreadInputQueuePtr DemodulatorInstance::getIQInputDataPipe() {
return pipeIQInputData;
}
@@ -540,6 +618,65 @@ ModemSettings DemodulatorInstance::getLastModemSettings(std::string demodType) {
}
}
+
+void DemodulatorInstance::startRecording() {
+ if (recording.load()) {
+ return;
+ }
+
+ AudioSinkFileThread *newSinkThread = new AudioSinkFileThread();
+ AudioFileWAV *afHandler = new AudioFileWAV();
+
+ std::stringstream fileName;
+
+ std::wstring userLabel = getDemodulatorUserLabel();
+
+ wxString userLabelForFileName(userLabel);
+ std::string userLabelStr = userLabelForFileName.ToStdString();
+
+ if (!userLabelStr.empty()) {
+ fileName << userLabelStr;
+ } else {
+ fileName << getLabel();
+ }
+
+ newSinkThread->setAudioFileNameBase(fileName.str());
+
+ //attach options:
+ newSinkThread->setSquelchOption(wxGetApp().getConfig()->getRecordingSquelchOption());
+ newSinkThread->setFileTimeLimit(wxGetApp().getConfig()->getRecordingFileTimeLimit());
+
+ newSinkThread->setAudioFileHandler(afHandler);
+
+ audioSinkThread = newSinkThread;
+ t_AudioSink = new std::thread(&AudioSinkThread::threadMain, audioSinkThread);
+
+ demodulatorThread->setOutputQueue("AudioSink", audioSinkThread->getInputQueue("input"));
+
+ recording.store(true);
+}
+
+
+void DemodulatorInstance::stopRecording() {
+ if (!recording.load()) {
+ return;
+ }
+
+ demodulatorThread->setOutputQueue("AudioSink", nullptr);
+ audioSinkThread->terminate();
+
+ t_AudioSink->join();
+
+ delete t_AudioSink;
+ delete audioSinkThread;
+
+ t_AudioSink = nullptr;
+ audioSinkThread = nullptr;
+
+ recording.store(false);
+}
+
+
#if ENABLE_DIGITAL_LAB
ModemDigitalOutput *DemodulatorInstance::getOutput() {
if (activeOutput == nullptr) {
diff --git a/src/demod/DemodulatorInstance.h b/src/demod/DemodulatorInstance.h
index 749c6ec..297df69 100644
--- a/src/demod/DemodulatorInstance.h
+++ b/src/demod/DemodulatorInstance.h
@@ -6,12 +6,12 @@
#include <vector>
#include <map>
#include <thread>
-
-#include "DemodulatorThread.h"
-#include "DemodulatorPreThread.h"
-
+#include <memory>
+#include "DemodDefs.h"
#include "ModemDigital.h"
#include "ModemAnalog.h"
+#include "AudioThread.h"
+#include "AudioSinkThread.h"
#if ENABLE_DIGITAL_LAB
#include "DigitalConsole.h"
@@ -30,6 +30,8 @@ private:
std::atomic_int squelchBreak;
};
+class DemodulatorThread;
+class DemodulatorPreThread;
class DemodulatorInstance {
public:
@@ -48,7 +50,7 @@ public:
DemodulatorInstance();
~DemodulatorInstance();
- void setVisualOutputQueue(DemodulatorThreadOutputQueue *tQueue);
+ void setVisualOutputQueue(DemodulatorThreadOutputQueuePtr tQueue);
void run();
void terminate();
@@ -109,9 +111,12 @@ public:
bool isMuted();
void setMuted(bool muted);
+ bool isRecording();
+ void setRecording(bool recording);
+
DemodVisualCue *getVisualCue();
- DemodulatorThreadInputQueue *getIQInputDataPipe();
+ DemodulatorThreadInputQueuePtr getIQInputDataPipe();
ModemArgInfoList getModemArgs();
std::string readModemSetting(std::string setting);
@@ -131,14 +136,23 @@ public:
#endif
protected:
- DemodulatorThreadInputQueue* pipeIQInputData;
- DemodulatorThreadPostInputQueue* pipeIQDemodData;
- AudioThreadInputQueue *pipeAudioData;
+ void startRecording();
+ void stopRecording();
+
+private:
+ DemodulatorThreadInputQueuePtr pipeIQInputData;
+ DemodulatorThreadPostInputQueuePtr pipeIQDemodData;
+ AudioThreadInputQueuePtr pipeAudioData;
DemodulatorPreThread *demodulatorPreThread;
DemodulatorThread *demodulatorThread;
- DemodulatorThreadControlCommandQueue *threadQueueControl;
+ DemodulatorThreadControlCommandQueuePtr threadQueueControl;
-private:
+ AudioSinkThread *audioSinkThread = nullptr;
+ std::thread *t_AudioSink = nullptr;
+ AudioThreadInputQueuePtr audioSinkInputQueue;
+
+ //protects child thread creation and termination
+ std::recursive_mutex m_thread_control_mutex;
std::atomic<std::string *> label; //
// User editable buffer, 16 bit string.
@@ -148,6 +162,8 @@ private:
std::atomic_bool squelch;
std::atomic_bool muted;
std::atomic_bool deltaLock;
+ std::atomic_bool recording;
+
std::atomic_int deltaLockOfs;
std::atomic_int currentOutputDevice;
@@ -160,3 +176,5 @@ private:
ModemDigitalOutput *activeOutput;
#endif
};
+
+typedef std::shared_ptr<DemodulatorInstance> DemodulatorInstancePtr;
diff --git a/src/demod/DemodulatorMgr.cpp b/src/demod/DemodulatorMgr.cpp
index 4fa9e85..40978b9 100644
--- a/src/demod/DemodulatorMgr.cpp
+++ b/src/demod/DemodulatorMgr.cpp
@@ -17,13 +17,11 @@
#include "DataTree.h"
-bool demodFreqCompare (DemodulatorInstance *i, DemodulatorInstance *j) { return (i->getFrequency()<j->getFrequency()); }
-bool inactiveCompare (DemodulatorInstance *i, DemodulatorInstance *j) { return (i->isActive()<j->isActive()); }
+bool demodFreqCompare (DemodulatorInstancePtr i, DemodulatorInstancePtr j) { return (i->getFrequency() < j->getFrequency()); }
+bool inactiveCompare (DemodulatorInstancePtr i, DemodulatorInstancePtr j) { return (i->isActive() < j->isActive()); }
DemodulatorMgr::DemodulatorMgr() {
- activeDemodulator = NULL;
- lastActiveDemodulator = NULL;
- activeVisualDemodulator = NULL;
+
lastBandwidth = DEFAULT_DEMOD_BW;
lastDemodType = DEFAULT_DEMOD_TYPE;
lastSquelchEnabled = false;
@@ -37,9 +35,11 @@ DemodulatorMgr::~DemodulatorMgr() {
terminateAll();
}
-DemodulatorInstance *DemodulatorMgr::newThread() {
+DemodulatorInstancePtr DemodulatorMgr::newThread() {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
- DemodulatorInstance *newDemod = new DemodulatorInstance;
+
+ //create a new instance of DemodulatorInstance here.
+ DemodulatorInstancePtr newDemod = std::make_shared<DemodulatorInstance>();
std::stringstream label;
label << demods.size();
@@ -51,27 +51,33 @@ DemodulatorInstance *DemodulatorMgr::newThread() {
}
void DemodulatorMgr::terminateAll() {
+
std::lock_guard < std::recursive_mutex > lock(demods_busy);
+
while (demods.size()) {
- DemodulatorInstance *d = demods.back();
+ DemodulatorInstancePtr d = demods.back();
demods.pop_back();
- wxGetApp().removeDemodulator(d);
deleteThread(d);
}
}
-std::vector<DemodulatorInstance *> &DemodulatorMgr::getDemodulators() {
+std::vector<DemodulatorInstancePtr> DemodulatorMgr::getDemodulators() {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
return demods;
}
-std::vector<DemodulatorInstance *> DemodulatorMgr::getOrderedDemodulators(bool actives) {
+std::vector<DemodulatorInstancePtr> DemodulatorMgr::getOrderedDemodulators(bool actives) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
- std::vector<DemodulatorInstance *> demods_ordered = demods;
+
+ auto demods_ordered = demods;
+
if (actives) {
+
std::sort(demods_ordered.begin(), demods_ordered.end(), inactiveCompare);
- std::vector<DemodulatorInstance *>::iterator i;
+
+ std::vector<DemodulatorInstancePtr>::iterator i;
+
for (i = demods_ordered.begin(); i != demods_ordered.end(); i++) {
if ((*i)->isActive()) {
break;
@@ -88,13 +94,14 @@ std::vector<DemodulatorInstance *> DemodulatorMgr::getOrderedDemodulators(bool a
return demods_ordered;
}
-DemodulatorInstance *DemodulatorMgr::getPreviousDemodulator(DemodulatorInstance *demod, bool actives) {
+DemodulatorInstancePtr DemodulatorMgr::getPreviousDemodulator(DemodulatorInstancePtr demod, bool actives) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
if (!getLastActiveDemodulator()) {
return nullptr;
}
- std::vector<DemodulatorInstance *> demods_ordered = getOrderedDemodulators(actives);
- std::vector<DemodulatorInstance *>::iterator p = std::find(demods_ordered.begin(), demods_ordered.end(), demod);
+ auto demods_ordered = getOrderedDemodulators(actives);
+ auto p = std::find(demods_ordered.begin(), demods_ordered.end(), demod);
+
if (p == demods_ordered.end()) {
return nullptr;
}
@@ -104,13 +111,14 @@ DemodulatorInstance *DemodulatorMgr::getPreviousDemodulator(DemodulatorInstance
return *(--p);
}
-DemodulatorInstance *DemodulatorMgr::getNextDemodulator(DemodulatorInstance *demod, bool actives) {
+DemodulatorInstancePtr DemodulatorMgr::getNextDemodulator(DemodulatorInstancePtr demod, bool actives) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
if (!getLastActiveDemodulator()) {
return nullptr;
}
- std::vector<DemodulatorInstance *> demods_ordered = getOrderedDemodulators(actives);
- std::vector<DemodulatorInstance *>::iterator p = std::find(demods_ordered.begin(), demods_ordered.end(), demod);
+ auto demods_ordered = getOrderedDemodulators(actives);
+ auto p = std::find(demods_ordered.begin(), demods_ordered.end(), demod);
+
if (actives) {
}
@@ -123,26 +131,25 @@ DemodulatorInstance *DemodulatorMgr::getNextDemodulator(DemodulatorInstance *dem
return *(++p);
}
-DemodulatorInstance *DemodulatorMgr::getLastDemodulator() {
+DemodulatorInstancePtr DemodulatorMgr::getLastDemodulator() {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
- std::vector<DemodulatorInstance *> demods_ordered = getOrderedDemodulators();
- return *(demods_ordered.end());
+
+ return getOrderedDemodulators().back();
}
-DemodulatorInstance *DemodulatorMgr::getFirstDemodulator() {
+DemodulatorInstancePtr DemodulatorMgr::getFirstDemodulator() {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
- std::vector<DemodulatorInstance *> demods_ordered = getOrderedDemodulators();
- return *(demods_ordered.begin());
+
+ return getOrderedDemodulators().front();
}
-void DemodulatorMgr::deleteThread(DemodulatorInstance *demod) {
+void DemodulatorMgr::deleteThread(DemodulatorInstancePtr demod) {
+
std::lock_guard < std::recursive_mutex > lock(demods_busy);
wxGetApp().getBookmarkMgr().addRecent(demod);
-
- std::vector<DemodulatorInstance *>::iterator i;
-
- i = std::find(demods.begin(), demods.end(), demod);
+
+ auto i = std::find(demods.begin(), demods.end(), demod);
if (activeDemodulator == demod) {
activeDemodulator = nullptr;
@@ -161,18 +168,15 @@ void DemodulatorMgr::deleteThread(DemodulatorInstance *demod) {
//Ask for termination
demod->setActive(false);
demod->terminate();
-
- //Do not cleanup immediatly
- demods_deleted.push_back(demod);
}
-std::vector<DemodulatorInstance *> DemodulatorMgr::getDemodulatorsAt(long long freq, int bandwidth) {
+std::vector<DemodulatorInstancePtr> DemodulatorMgr::getDemodulatorsAt(long long freq, int bandwidth) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
- std::vector<DemodulatorInstance *> foundDemods;
+ std::vector<DemodulatorInstancePtr> foundDemods;
for (int i = 0, iMax = demods.size(); i < iMax; i++) {
- DemodulatorInstance *testDemod = demods[i];
+ DemodulatorInstancePtr testDemod = demods[i];
long long freqTest = testDemod->getFrequency();
long long bandwidthTest = testDemod->getBandwidth();
@@ -191,7 +195,7 @@ std::vector<DemodulatorInstance *> DemodulatorMgr::getDemodulatorsAt(long long f
bool DemodulatorMgr::anyDemodulatorsAt(long long freq, int bandwidth) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
for (int i = 0, iMax = demods.size(); i < iMax; i++) {
- DemodulatorInstance *testDemod = demods[i];
+ DemodulatorInstancePtr testDemod = demods[i];
long long freqTest = testDemod->getFrequency();
long long bandwidthTest = testDemod->getBandwidth();
@@ -209,36 +213,34 @@ bool DemodulatorMgr::anyDemodulatorsAt(long long freq, int bandwidth) {
}
-void DemodulatorMgr::setActiveDemodulator(DemodulatorInstance *demod, bool temporary) {
+void DemodulatorMgr::setActiveDemodulator(DemodulatorInstancePtr demod, bool temporary) {
+
+ std::lock_guard < std::recursive_mutex > lock(demods_busy);
if (!temporary) {
- if (activeDemodulator.load() != nullptr) {
- lastActiveDemodulator = activeDemodulator.load();
+ if (activeDemodulator != nullptr) {
+ lastActiveDemodulator = activeDemodulator;
updateLastState();
} else {
lastActiveDemodulator = demod;
}
updateLastState();
#if USE_HAMLIB
- if (wxGetApp().rigIsActive() && wxGetApp().getRigThread()->getFollowModem() && lastActiveDemodulator.load()) {
- wxGetApp().getRigThread()->setFrequency(lastActiveDemodulator.load()->getFrequency(),true);
+ if (wxGetApp().rigIsActive() && wxGetApp().getRigThread()->getFollowModem() && lastActiveDemodulator) {
+ wxGetApp().getRigThread()->setFrequency(lastActiveDemodulator->getFrequency(),true);
}
#endif
wxGetApp().getBookmarkMgr().updateActiveList();
- } else {
- std::lock_guard < std::recursive_mutex > lock(demods_busy);
- garbageCollect();
- ReBufferGC::garbageCollect();
- }
+ }
- if (activeVisualDemodulator.load()) {
- activeVisualDemodulator.load()->setVisualOutputQueue(nullptr);
+ if (activeVisualDemodulator) {
+ activeVisualDemodulator->setVisualOutputQueue(nullptr);
}
if (demod) {
demod->setVisualOutputQueue(wxGetApp().getAudioVisualQueue());
activeVisualDemodulator = demod;
} else {
- DemodulatorInstance *last = getLastActiveDemodulator();
+ DemodulatorInstancePtr last = getLastActiveDemodulator();
if (last) {
last->setVisualOutputQueue(wxGetApp().getAudioVisualQueue());
}
@@ -249,25 +251,41 @@ void DemodulatorMgr::setActiveDemodulator(DemodulatorInstance *demod, bool tempo
}
-DemodulatorInstance *DemodulatorMgr::getActiveDemodulator() {
- if (activeDemodulator.load() && !activeDemodulator.load()->isActive()) {
+//Dangerous: this is only intended by some internal classes
+void DemodulatorMgr::setActiveDemodulatorByRawPointer(DemodulatorInstance* demod, bool temporary) {
+ std::lock_guard < std::recursive_mutex > lock(demods_busy);
+
+ for (auto existing_demod : demods) {
+
+ if (existing_demod.get() == demod) {
+
+ setActiveDemodulator(existing_demod, temporary);
+ break;
+ }
+ }
+}
+
+DemodulatorInstancePtr DemodulatorMgr::getActiveDemodulator() {
+ std::lock_guard < std::recursive_mutex > lock(demods_busy);
+
+ if (activeDemodulator && !activeDemodulator->isActive()) {
activeDemodulator = getLastActiveDemodulator();
}
return activeDemodulator;
}
-DemodulatorInstance *DemodulatorMgr::getLastActiveDemodulator() {
+DemodulatorInstancePtr DemodulatorMgr::getLastActiveDemodulator() {
return lastActiveDemodulator;
}
-DemodulatorInstance *DemodulatorMgr::getLastDemodulatorWith(const std::string& type,
+DemodulatorInstancePtr DemodulatorMgr::getLastDemodulatorWith(const std::string& type,
const std::wstring& userLabel,
long long frequency,
int bandwidth) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
//backwards search:
- for (std::vector<DemodulatorInstance *>::reverse_iterator it = demods.rbegin(); it != demods.rend(); it++) {
+ for (auto it = demods.rbegin(); it != demods.rend(); it++) {
if ((*it)->getDemodulatorType() == type &&
(*it)->getDemodulatorUserLabel() == userLabel &&
@@ -281,51 +299,31 @@ DemodulatorInstance *DemodulatorMgr::getLastDemodulatorWith(const std::string& t
return nullptr;
}
-//Private internal method, no need to protect it with demods_busy
-void DemodulatorMgr::garbageCollect() {
- if (demods_deleted.size()) {
-
- std::vector<DemodulatorInstance *>::iterator i;
-
- for (i = demods_deleted.begin(); i != demods_deleted.end(); i++) {
- if ((*i)->isTerminated()) {
- DemodulatorInstance *deleted = (*i);
- demods_deleted.erase(i);
-
- std::cout << "Garbage collected demodulator instance " << deleted->getLabel() << std::endl;
-
- delete deleted;
- return;
- }
- }
-
- }
-}
void DemodulatorMgr::updateLastState() {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
if (std::find(demods.begin(), demods.end(), lastActiveDemodulator) == demods.end()) {
- if (activeDemodulator.load() && activeDemodulator.load()->isActive()) {
- lastActiveDemodulator = activeDemodulator.load();
- } else if (activeDemodulator.load() && !activeDemodulator.load()->isActive()){
+ if (activeDemodulator && activeDemodulator->isActive()) {
+ lastActiveDemodulator = activeDemodulator;
+ } else if (activeDemodulator && !activeDemodulator->isActive()){
activeDemodulator = nullptr;
lastActiveDemodulator = nullptr;
}
}
- if (lastActiveDemodulator.load() && !lastActiveDemodulator.load()->isActive()) {
+ if (lastActiveDemodulator && !lastActiveDemodulator->isActive()) {
lastActiveDemodulator = nullptr;
}
- if (lastActiveDemodulator.load()) {
- lastBandwidth = lastActiveDemodulator.load()->getBandwidth();
- lastDemodType = lastActiveDemodulator.load()->getDemodulatorType();
- lastDemodLock = lastActiveDemodulator.load()->getDemodulatorLock()?true:false;
- lastSquelchEnabled = lastActiveDemodulator.load()->isSquelchEnabled();
- lastSquelch = lastActiveDemodulator.load()->getSquelchLevel();
- lastGain = lastActiveDemodulator.load()->getGain();
- lastModemSettings[lastDemodType] = lastActiveDemodulator.load()->readModemSettings();
+ if (lastActiveDemodulator) {
+ lastBandwidth = lastActiveDemodulator->getBandwidth();
+ lastDemodType = lastActiveDemodulator->getDemodulatorType();
+ lastDemodLock = lastActiveDemodulator->getDemodulatorLock()?true:false;
+ lastSquelchEnabled = lastActiveDemodulator->isSquelchEnabled();
+ lastSquelch = lastActiveDemodulator->getSquelchLevel();
+ lastGain = lastActiveDemodulator->getGain();
+ lastModemSettings[lastDemodType] = lastActiveDemodulator->readModemSettings();
}
}
@@ -404,7 +402,8 @@ void DemodulatorMgr::setOutputDevices(std::map<int,RtAudio::DeviceInfo> devs) {
outputDevices = devs;
}
-void DemodulatorMgr::saveInstance(DataNode *node, DemodulatorInstance *inst) {
+void DemodulatorMgr::saveInstance(DataNode *node, DemodulatorInstancePtr inst) {
+
*node->newChild("bandwidth") = inst->getBandwidth();
*node->newChild("frequency") = inst->getFrequency();
*node->newChild("type") = inst->getDemodulatorType();
@@ -431,14 +430,13 @@ void DemodulatorMgr::saveInstance(DataNode *node, DemodulatorInstance *inst) {
*settingsNode->newChild(msi->first.c_str()) = msi->second;
}
}
-
}
-DemodulatorInstance *DemodulatorMgr::loadInstance(DataNode *node) {
+DemodulatorInstancePtr DemodulatorMgr::loadInstance(DataNode *node) {
std::lock_guard < std::recursive_mutex > lock(demods_busy);
- DemodulatorInstance *newDemod = nullptr;
+ DemodulatorInstancePtr newDemod = nullptr;
node->rewindAll();
@@ -526,15 +524,21 @@ DemodulatorInstance *DemodulatorMgr::loadInstance(DataNode *node) {
}
//Attach to sound output:
- bool found_device = false;
std::map<int, RtAudio::DeviceInfo>::iterator i;
+
+ bool matching_device_found = false;
+
for (i = outputDevices.begin(); i != outputDevices.end(); i++) {
if (i->second.name == output_device) {
newDemod->setOutputDevice(i->first);
- found_device = true;
+ matching_device_found = true;
break;
}
}
+ //if no device is found, choose the first of the list anyway.
+ if (!matching_device_found) {
+ newDemod->setOutputDevice(outputDevices.begin()->first);
+ }
return newDemod;
}
diff --git a/src/demod/DemodulatorMgr.h b/src/demod/DemodulatorMgr.h
index ca65c22..b080e5f 100644
--- a/src/demod/DemodulatorMgr.h
+++ b/src/demod/DemodulatorMgr.h
@@ -16,23 +16,32 @@ public:
DemodulatorMgr();
~DemodulatorMgr();
- DemodulatorInstance *newThread();
- std::vector<DemodulatorInstance *> &getDemodulators();
- std::vector<DemodulatorInstance *> getOrderedDemodulators(bool actives = true);
- std::vector<DemodulatorInstance *> getDemodulatorsAt(long long freq, int bandwidth);
- DemodulatorInstance *getPreviousDemodulator(DemodulatorInstance *demod, bool actives = true);
- DemodulatorInstance *getNextDemodulator(DemodulatorInstance *demod, bool actives = true);
- DemodulatorInstance *getLastDemodulator();
- DemodulatorInstance *getFirstDemodulator();
+ DemodulatorInstancePtr newThread();
+
+ //return snapshot-copy of the list purposefully
+ std::vector<DemodulatorInstancePtr> getDemodulators();
+
+ std::vector<DemodulatorInstancePtr> getOrderedDemodulators(bool actives = true);
+ std::vector<DemodulatorInstancePtr> getDemodulatorsAt(long long freq, int bandwidth);
+
+ DemodulatorInstancePtr getPreviousDemodulator(DemodulatorInstancePtr demod, bool actives = true);
+ DemodulatorInstancePtr getNextDemodulator(DemodulatorInstancePtr demod, bool actives = true);
+ DemodulatorInstancePtr getLastDemodulator();
+ DemodulatorInstancePtr getFirstDemodulator();
bool anyDemodulatorsAt(long long freq, int bandwidth);
- void deleteThread(DemodulatorInstance *);
+ void deleteThread(DemodulatorInstancePtr);
void terminateAll();
- void setActiveDemodulator(DemodulatorInstance *demod, bool temporary = true);
- DemodulatorInstance *getActiveDemodulator();
- DemodulatorInstance *getLastActiveDemodulator();
- DemodulatorInstance *getLastDemodulatorWith(const std::string& type,
+ void setActiveDemodulator(DemodulatorInstancePtr demod, bool temporary = true);
+
+ //Dangerous: this is only intended by some internal classes,
+ // and only set a pre-existing demod
+ void setActiveDemodulatorByRawPointer(DemodulatorInstance* demod, bool temporary = true);
+
+ DemodulatorInstancePtr getActiveDemodulator();
+ DemodulatorInstancePtr getLastActiveDemodulator();
+ DemodulatorInstancePtr getLastDemodulatorWith(const std::string& type,
const std::wstring& userLabel,
long long frequency,
int bandwidth);
@@ -64,20 +73,17 @@ public:
void updateLastState();
void setOutputDevices(std::map<int,RtAudio::DeviceInfo> devs);
- void saveInstance(DataNode *node, DemodulatorInstance *inst);
+ void saveInstance(DataNode *node, DemodulatorInstancePtr inst);
- DemodulatorInstance *loadInstance(DataNode *node);
-
+ DemodulatorInstancePtr loadInstance(DataNode *node);
+
private:
-
- void garbageCollect();
- std::vector<DemodulatorInstance *> demods;
- std::vector<DemodulatorInstance *> demods_deleted;
+ std::vector<DemodulatorInstancePtr> demods;
- std::atomic<DemodulatorInstance *> activeDemodulator;
- std::atomic<DemodulatorInstance *> lastActiveDemodulator;
- std::atomic<DemodulatorInstance *> activeVisualDemodulator;
+ DemodulatorInstancePtr activeDemodulator;
+ DemodulatorInstancePtr lastActiveDemodulator;
+ DemodulatorInstancePtr activeVisualDemodulator;
int lastBandwidth;
std::string lastDemodType;
@@ -91,7 +97,7 @@ private:
//protects access to demods lists and such, need to be recursive
//because of the usage of public re-entrant methods
std::recursive_mutex demods_busy;
-
+
std::map<std::string, ModemSettings> lastModemSettings;
std::map<int,RtAudio::DeviceInfo> outputDevices;
};
diff --git a/src/demod/DemodulatorPreThread.cpp b/src/demod/DemodulatorPreThread.cpp
index 574c7fa..049014b 100644
--- a/src/demod/DemodulatorPreThread.cpp
+++ b/src/demod/DemodulatorPreThread.cpp
@@ -12,7 +12,10 @@
#include "CubicSDR.h"
#include "DemodulatorInstance.h"
-DemodulatorPreThread::DemodulatorPreThread(DemodulatorInstance *parent) : IOThread(), iqResampler(NULL), iqResampleRatio(1), cModem(nullptr), cModemKit(nullptr), iqInputQueue(NULL), iqOutputQueue(NULL)
+//50 ms
+#define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000)
+
+DemodulatorPreThread::DemodulatorPreThread(DemodulatorInstance* parent) : IOThread(), iqResampler(NULL), iqResampleRatio(1), cModem(nullptr), cModemKit(nullptr)
{
initialized.store(false);
this->parent = parent;
@@ -20,10 +23,10 @@ DemodulatorPreThread::DemodulatorPreThread(DemodulatorInstance *parent) : IOThre
freqShifter = nco_crcf_create(LIQUID_VCO);
shiftFrequency = 0;
- workerQueue = new DemodulatorThreadWorkerCommandQueue;
+ workerQueue = std::make_shared<DemodulatorThreadWorkerCommandQueue>();
workerQueue->set_max_num_items(2);
- workerResults = new DemodulatorThreadWorkerResultQueue;
+ workerResults = std::make_shared<DemodulatorThreadWorkerResultQueue>();
workerResults->set_max_num_items(100);
workerThread = new DemodulatorWorkerThread();
@@ -62,8 +65,8 @@ void DemodulatorPreThread::run() {
ReBuffer<DemodulatorThreadPostIQData> buffers("DemodulatorPreThreadBuffers");
- iqInputQueue = static_cast<DemodulatorThreadInputQueue*>(getInputQueue("IQDataInput"));
- iqOutputQueue = static_cast<DemodulatorThreadPostInputQueue*>(getOutputQueue("IQDataOutput"));
+ iqInputQueue = std::static_pointer_cast<DemodulatorThreadInputQueue>(getInputQueue("IQDataInput"));
+ iqOutputQueue = std::static_pointer_cast<DemodulatorThreadPostInputQueue>(getOutputQueue("IQDataOutput"));
std::vector<liquid_float_complex> in_buf_data;
std::vector<liquid_float_complex> out_buf_data;
@@ -71,9 +74,11 @@ void DemodulatorPreThread::run() {
t_Worker = new std::thread(&DemodulatorWorkerThread::threadMain, workerThread);
while (!stopping) {
- DemodulatorThreadIQData *inp;
+ DemodulatorThreadIQDataPtr inp;
- iqInputQueue->pop(inp);
+ if (!iqInputQueue->pop(inp, HEARTBEAT_CHECK_PERIOD_MICROS)) {
+ continue;
+ }
if (frequencyChanged.load()) {
currentFrequency.store(newFrequency);
@@ -157,7 +162,7 @@ void DemodulatorPreThread::run() {
}
if (cModem && cModemKit && abs(shiftFrequency) > (int) ((double) (inp->sampleRate / 2) * 1.5)) {
- inp->decRefCount();
+
continue;
}
@@ -192,7 +197,7 @@ void DemodulatorPreThread::run() {
out_buf = temp_buf;
}
- DemodulatorThreadPostIQData *resamp = buffers.getBuffer();
+ DemodulatorThreadPostIQDataPtr resamp = buffers.getBuffer();
size_t out_size = ceil((double) (bufSize) * iqResampleRatio) + 512;
@@ -218,8 +223,6 @@ void DemodulatorPreThread::run() {
iqOutputQueue->push(resamp);
}
- inp->decRefCount();
-
DemodulatorWorkerThreadResult result;
//process all worker results until
while (!stopping && workerResults->try_pop(result)) {
@@ -277,12 +280,9 @@ void DemodulatorPreThread::run() {
}
} //end while stopping
- DemodulatorThreadPostIQData *tmp;
- while (iqOutputQueue->try_pop(tmp)) {
-
- tmp->decRefCount();
- }
- buffers.purge();
+
+ iqOutputQueue->flush();
+ iqInputQueue->flush();
}
void DemodulatorPreThread::setDemodType(std::string demodType) {
@@ -347,18 +347,18 @@ int DemodulatorPreThread::getAudioSampleRate() {
}
void DemodulatorPreThread::terminate() {
+
+ //make non-blocking calls to be sure threads are flagged for termination.
IOThread::terminate();
- DemodulatorThreadIQData *inp = new DemodulatorThreadIQData; // push dummy to nudge queue
-
- //VSO: blocking push :
- iqInputQueue->push(inp);
-
- DemodulatorWorkerThreadCommand command(DemodulatorWorkerThreadCommand::DEMOD_WORKER_THREAD_CMD_NULL);
-
- workerQueue->push(command);
-
workerThread->terminate();
- workerThread->isTerminated(1000);
+
+ //unblock the push()
+ iqOutputQueue->flush();
+ iqInputQueue->flush();
+
+ //wait blocking for termination here, it could be long with lots of modems and we MUST terminate properly,
+ //else better kill the whole application...
+ workerThread->isTerminated(5000);
t_Worker->join();
delete t_Worker;
@@ -366,12 +366,6 @@ void DemodulatorPreThread::terminate() {
delete workerThread;
workerThread = nullptr;
-
- delete workerResults;
- workerResults = nullptr;
-
- delete workerQueue;
- workerQueue = nullptr;
}
Modem *DemodulatorPreThread::getModem() {
diff --git a/src/demod/DemodulatorPreThread.h b/src/demod/DemodulatorPreThread.h
index 55bdfcd..3913d11 100644
--- a/src/demod/DemodulatorPreThread.h
+++ b/src/demod/DemodulatorPreThread.h
@@ -6,6 +6,7 @@
#include <queue>
#include <vector>
#include <atomic>
+#include <memory>
#include "CubicSDRDefs.h"
#include "DemodDefs.h"
@@ -16,8 +17,8 @@ class DemodulatorInstance;
class DemodulatorPreThread : public IOThread {
public:
- DemodulatorPreThread(DemodulatorInstance *parent);
- ~DemodulatorPreThread();
+ DemodulatorPreThread(DemodulatorInstance* parent);
+ virtual ~DemodulatorPreThread();
virtual void run();
@@ -49,7 +50,9 @@ public:
void writeModemSettings(ModemSettings settings);
protected:
- DemodulatorInstance *parent;
+
+ DemodulatorInstance* parent;
+
msresamp_crcf iqResampler;
double iqResampleRatio;
std::vector<liquid_float_complex> resampledData;
@@ -78,9 +81,9 @@ protected:
DemodulatorWorkerThread *workerThread;
std::thread *t_Worker;
- DemodulatorThreadWorkerCommandQueue *workerQueue;
- DemodulatorThreadWorkerResultQueue *workerResults;
+ DemodulatorThreadWorkerCommandQueuePtr workerQueue;
+ DemodulatorThreadWorkerResultQueuePtr workerResults;
- DemodulatorThreadInputQueue* iqInputQueue;
- DemodulatorThreadPostInputQueue* iqOutputQueue;
+ DemodulatorThreadInputQueuePtr iqInputQueue;
+ DemodulatorThreadPostInputQueuePtr iqOutputQueue;
};
diff --git a/src/demod/DemodulatorThread.cpp b/src/demod/DemodulatorThread.cpp
index a186404..d828d26 100644
--- a/src/demod/DemodulatorThread.cpp
+++ b/src/demod/DemodulatorThread.cpp
@@ -12,14 +12,17 @@
#define M_PI 3.14159265358979323846
#endif
+//50 ms
+#define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000)
+
#ifdef __APPLE__
#include <pthread.h>
#endif
-std::atomic<DemodulatorInstance *> DemodulatorThread::squelchLock(nullptr);
+DemodulatorInstance* DemodulatorThread::squelchLock(nullptr);
std::mutex DemodulatorThread::squelchLockMutex;
-DemodulatorThread::DemodulatorThread(DemodulatorInstance *parent)
+DemodulatorThread::DemodulatorThread(DemodulatorInstance* parent)
: IOThread(), outputBuffers("DemodulatorThreadBuffers"), squelchLevel(-100),
signalLevel(-100), signalFloor(-30), signalCeil(30), squelchEnabled(false) {
@@ -32,13 +35,19 @@ DemodulatorThread::~DemodulatorThread() {
releaseSquelchLock(demodInstance);
}
-void DemodulatorThread::onBindOutput(std::string name, ThreadQueueBase *threadQueue) {
+void DemodulatorThread::onBindOutput(std::string name, ThreadQueueBasePtr threadQueue) {
if (name == "AudioVisualOutput") {
//protects because it may be changed at runtime
std::lock_guard < std::mutex > lock(m_mutexAudioVisOutputQueue);
- audioVisOutputQueue = static_cast<DemodulatorThreadOutputQueue*>(threadQueue);
+ audioVisOutputQueue = std::static_pointer_cast<DemodulatorThreadOutputQueue>(threadQueue);
+ }
+
+ if (name == "AudioSink") {
+ std::lock_guard < std::mutex > lock(m_mutexAudioVisOutputQueue);
+
+ audioSinkOutputQueue = std::static_pointer_cast<AudioThreadInputQueue>(threadQueue);
}
}
@@ -72,22 +81,23 @@ void DemodulatorThread::run() {
// std::cout << "Demodulator thread started.." << std::endl;
- iqInputQueue = static_cast<DemodulatorThreadPostInputQueue*>(getInputQueue("IQDataInput"));
- audioOutputQueue = static_cast<AudioThreadInputQueue*>(getOutputQueue("AudioDataOutput"));
- threadQueueControl = static_cast<DemodulatorThreadControlCommandQueue *>(getInputQueue("ControlQueue"));
+ iqInputQueue = std::static_pointer_cast<DemodulatorThreadPostInputQueue>(getInputQueue("IQDataInput"));
+ audioOutputQueue = std::static_pointer_cast<AudioThreadInputQueue>(getOutputQueue("AudioDataOutput"));
+ threadQueueControl = std::static_pointer_cast<DemodulatorThreadControlCommandQueue>(getInputQueue("ControlQueue"));
ModemIQData modemData;
while (!stopping) {
- DemodulatorThreadPostIQData *inp;
-
- iqInputQueue->pop(inp);
- // std::lock_guard < std::mutex > lock(inp->m_mutex);
+ DemodulatorThreadPostIQDataPtr inp;
+ if (!iqInputQueue->pop(inp, HEARTBEAT_CHECK_PERIOD_MICROS)) {
+ continue;
+ }
+
size_t bufSize = inp->data.size();
if (!bufSize) {
- inp->decRefCount();
+
continue;
}
@@ -104,7 +114,7 @@ void DemodulatorThread::run() {
}
if (!cModem || !cModemKit) {
- inp->decRefCount();
+
continue;
}
@@ -115,7 +125,7 @@ void DemodulatorThread::run() {
modemData.sampleRate = inp->sampleRate;
modemData.data.assign(inputData->begin(), inputData->end());
- AudioThreadInput *ati = nullptr;
+ AudioThreadInputPtr ati = nullptr;
ModemAnalog *modemAnalog = (cModem->getType() == "analog")?((ModemAnalog *)cModem):nullptr;
ModemDigital *modemDigital = (cModem->getType() == "digital")?((ModemDigital *)cModem):nullptr;
@@ -133,7 +143,7 @@ void DemodulatorThread::run() {
ati->data.resize(0);
}
- cModem->demodulate(cModemKit, &modemData, ati);
+ cModem->demodulate(cModemKit, &modemData, ati.get());
double currentSignalLevel = 0;
double sampleTime = double(inp->data.size()) / double(inp->sampleRate);
@@ -191,16 +201,16 @@ void DemodulatorThread::run() {
signalLevel = signalLevel + (currentSignalLevel - signalLevel) * 0.05 * sampleTime * 30.0;
}
- bool squelched = (muted.load() || (squelchEnabled && (signalLevel < squelchLevel)));
+ bool squelched = squelchEnabled && (signalLevel < squelchLevel);
if (squelchEnabled) {
if (!squelched && !squelchBreak) {
if (wxGetApp().getSoloMode() && !wxGetApp().getAppFrame()->isUserDemodBusy()) {
std::lock_guard < std::mutex > lock(squelchLockMutex);
- if (squelchLock.load() == nullptr) {
- squelchLock.store(demodInstance);
+ if (squelchLock == nullptr) {
+ squelchLock = demodInstance;
wxGetApp().getDemodMgr().setActiveDemodulator(nullptr);
- wxGetApp().getDemodMgr().setActiveDemodulator(demodInstance, false);
+ wxGetApp().getDemodMgr().setActiveDemodulatorByRawPointer(demodInstance, false);
squelchBreak = true;
demodInstance->getVisualCue()->triggerSquelchBreak(120);
}
@@ -214,31 +224,36 @@ void DemodulatorThread::run() {
squelchBreak = false;
}
}
-
- if (audioOutputQueue != nullptr && ati && ati->data.size() && !squelched) {
- std::vector<float>::iterator data_i;
- ati->peak = 0;
- for (auto data_i : ati->data) {
- float p = fabs(data_i);
- if (p > ati->peak) {
- ati->peak = p;
- }
- }
- } else if (ati) {
- ati->setRefCount(0);
- ati = nullptr;
- }
-
+
+ //compute audio peak:
+ if (audioOutputQueue != nullptr && ati) {
+
+ ati->peak = 0;
+
+ for (auto data_i : ati->data) {
+ float p = fabs(data_i);
+ if (p > ati->peak) {
+ ati->peak = p;
+ }
+ }
+ }
+
+ //attach squelch flag to samples, to be used by audio sink.
+ if (ati) {
+ ati->is_squelch_active = squelched;
+ }
+
//At that point, capture the current state of audioVisOutputQueue in a local
//variable, and works with it with now on until the next while-turn.
- DemodulatorThreadOutputQueue* localAudioVisOutputQueue = nullptr;
+ DemodulatorThreadOutputQueuePtr localAudioVisOutputQueue = nullptr;
{
std::lock_guard < std::mutex > lock(m_mutexAudioVisOutputQueue);
localAudioVisOutputQueue = audioVisOutputQueue;
}
- if ((ati || modemDigital) && localAudioVisOutputQueue != nullptr && localAudioVisOutputQueue->empty()) {
- AudioThreadInput *ati_vis = new AudioThreadInput;
+ if (!squelched && (ati || modemDigital) && localAudioVisOutputQueue != nullptr && localAudioVisOutputQueue->empty()) {
+
+ AudioThreadInputPtr ati_vis = std::make_shared<AudioThreadInput>();
ati_vis->sampleRate = inp->sampleRate;
ati_vis->inputRate = inp->sampleRate;
@@ -246,7 +261,7 @@ void DemodulatorThread::run() {
size_t num_vis = DEMOD_VIS_SIZE;
if (modemDigital) {
if (ati) { // TODO: handle digital modems with audio output
- ati->setRefCount(0);
+
ati = nullptr;
}
ati_vis->data.resize(inputData->size());
@@ -300,25 +315,40 @@ void DemodulatorThread::run() {
if (!localAudioVisOutputQueue->try_push(ati_vis)) {
//non-blocking push needed for audio vis out
- ati_vis->setRefCount(0);
+
std::cout << "DemodulatorThread::run() cannot push ati_vis into localAudioVisOutputQueue, is full !" << std::endl;
std::this_thread::yield();
}
}
- if (ati != nullptr) {
- if (!muted.load() && (!wxGetApp().getSoloMode() || (demodInstance == wxGetApp().getDemodMgr().getLastActiveDemodulator()))) {
+ if (!squelched && ati != nullptr) {
+ if (!muted.load() && (!wxGetApp().getSoloMode() || (demodInstance == wxGetApp().getDemodMgr().getLastActiveDemodulator().get()))) {
//non-blocking push needed for audio out
if (!audioOutputQueue->try_push(ati)) {
- ati->decRefCount();
+
std::cout << "DemodulatorThread::run() cannot push ati into audioOutputQueue, is full !" << std::endl;
std::this_thread::yield();
}
- } else {
- ati->setRefCount(0);
}
}
+
+ // Capture audioSinkOutputQueue state in a local variable
+ DemodulatorThreadOutputQueuePtr localAudioSinkOutputQueue = nullptr;
+ {
+ std::lock_guard < std::mutex > lock(m_mutexAudioVisOutputQueue);
+ localAudioSinkOutputQueue = audioSinkOutputQueue;
+ }
+
+ //Push to audio sink, if any:
+ if (ati && localAudioSinkOutputQueue != nullptr) {
+
+ if (!localAudioSinkOutputQueue->try_push(ati)) {
+ std::cout << "DemodulatorThread::run() cannot push ati into audioSinkOutputQueue, is full !" << std::endl;
+ std::this_thread::yield();
+ }
+ }
+
DemodulatorThreadControlCommand command;
//empty command queue, execute commands
@@ -335,40 +365,23 @@ void DemodulatorThread::run() {
break;
}
}
-
-
- inp->decRefCount();
}
// end while !stopping
// Purge any unused inputs, with a non-blocking pop
- DemodulatorThreadPostIQData *ref;
- while (iqInputQueue->try_pop(ref)) {
-
- if (ref) { // May have other consumers; just decrement
- ref->decRefCount();
- }
- }
-
- AudioThreadInput *ref_audio;
- while (audioOutputQueue->try_pop(ref_audio)) {
-
- if (ref_audio) { // Originated here; set RefCount to 0
- ref_audio->setRefCount(0);
- }
- }
-
- outputBuffers.purge();
+ iqInputQueue->flush();
+ audioOutputQueue->flush();
// std::cout << "Demodulator thread done." << std::endl;
}
void DemodulatorThread::terminate() {
IOThread::terminate();
- DemodulatorThreadPostIQData *inp = new DemodulatorThreadPostIQData; // push dummy to nudge queue
-
- //VSO: blocking push
- iqInputQueue->push(inp);
+
+ //unblock the curretly blocked push()
+ iqInputQueue->flush();
+ audioOutputQueue->flush();
+ threadQueueControl->flush();
}
bool DemodulatorThread::isMuted() {
@@ -406,9 +419,11 @@ bool DemodulatorThread::getSquelchBreak() {
return squelchBreak;
}
-void DemodulatorThread::releaseSquelchLock(DemodulatorInstance *inst) {
+void DemodulatorThread::releaseSquelchLock(DemodulatorInstance* inst) {
+
std::lock_guard < std::mutex > lock(squelchLockMutex);
- if (inst == nullptr || squelchLock.load() == inst) {
- squelchLock.store(nullptr);
+
+ if (inst == nullptr || squelchLock == inst) {
+ squelchLock = nullptr;
}
-}
\ No newline at end of file
+}
diff --git a/src/demod/DemodulatorThread.h b/src/demod/DemodulatorThread.h
index 1df889c..45607bb 100644
--- a/src/demod/DemodulatorThread.h
+++ b/src/demod/DemodulatorThread.h
@@ -5,13 +5,12 @@
#include <queue>
#include <vector>
+#include <memory>
#include "DemodDefs.h"
#include "AudioThread.h"
#include "Modem.h"
-typedef ThreadBlockingQueue<AudioThreadInput *> DemodulatorThreadOutputQueue;
-
#define DEMOD_VIS_SIZE 2048
#define DEMOD_SIGNAL_MIN -30
#define DEMOD_SIGNAL_MAX 30
@@ -21,13 +20,13 @@ class DemodulatorInstance;
class DemodulatorThread : public IOThread {
public:
- DemodulatorThread(DemodulatorInstance *parent);
- ~DemodulatorThread();
+ DemodulatorThread(DemodulatorInstance* parent);
+ virtual ~DemodulatorThread();
- void onBindOutput(std::string name, ThreadQueueBase *threadQueue);
+ void onBindOutput(std::string name, ThreadQueueBasePtr threadQueue);
- void run();
- void terminate();
+ virtual void run();
+ virtual void terminate();
void setMuted(bool state);
bool isMuted();
@@ -40,13 +39,14 @@ public:
bool getSquelchBreak();
- static void releaseSquelchLock(DemodulatorInstance *inst);
+
+ static void releaseSquelchLock(DemodulatorInstance* inst);
protected:
double abMagnitude(float inphase, float quadrature);
double linearToDb(double linear);
- DemodulatorInstance *demodInstance = nullptr;
+ DemodulatorInstance* demodInstance;
ReBuffer<AudioThreadInput> outputBuffers;
std::atomic_bool muted;
@@ -55,18 +55,20 @@ protected:
std::atomic<float> signalLevel, signalFloor, signalCeil;
bool squelchEnabled, squelchBreak;
- static std::atomic<DemodulatorInstance *> squelchLock;
+ static DemodulatorInstance* squelchLock;
static std::mutex squelchLockMutex;
Modem *cModem = nullptr;
ModemKit *cModemKit = nullptr;
- DemodulatorThreadPostInputQueue* iqInputQueue = nullptr;
- AudioThreadInputQueue *audioOutputQueue = nullptr;
- DemodulatorThreadOutputQueue* audioVisOutputQueue = nullptr;
- DemodulatorThreadControlCommandQueue *threadQueueControl = nullptr;
+ DemodulatorThreadPostInputQueuePtr iqInputQueue;
+ AudioThreadInputQueuePtr audioOutputQueue;
+ DemodulatorThreadOutputQueuePtr audioVisOutputQueue;
+ DemodulatorThreadControlCommandQueuePtr threadQueueControl;
+
+ DemodulatorThreadOutputQueuePtr audioSinkOutputQueue = nullptr;
//protects the audioVisOutputQueue dynamic binding change at runtime (in DemodulatorMgr)
- mutable std::mutex m_mutexAudioVisOutputQueue;
+ std::mutex m_mutexAudioVisOutputQueue;
};
diff --git a/src/demod/DemodulatorWorkerThread.cpp b/src/demod/DemodulatorWorkerThread.cpp
index b1304a7..c736368 100644
--- a/src/demod/DemodulatorWorkerThread.cpp
+++ b/src/demod/DemodulatorWorkerThread.cpp
@@ -6,8 +6,11 @@
#include "CubicSDR.h"
#include <vector>
+//50 ms
+#define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000)
+
DemodulatorWorkerThread::DemodulatorWorkerThread() : IOThread(),
- commandQueue(NULL), resultQueue(NULL), cModem(nullptr), cModemKit(nullptr) {
+ cModem(nullptr), cModemKit(nullptr) {
}
DemodulatorWorkerThread::~DemodulatorWorkerThread() {
@@ -17,8 +20,8 @@ void DemodulatorWorkerThread::run() {
// std::cout << "Demodulator worker thread started.." << std::endl;
- commandQueue = static_cast<DemodulatorThreadWorkerCommandQueue *>(getInputQueue("WorkerCommandQueue"));
- resultQueue = static_cast<DemodulatorThreadWorkerResultQueue *>(getOutputQueue("WorkerResultQueue"));
+ commandQueue = std::static_pointer_cast<DemodulatorThreadWorkerCommandQueue>(getInputQueue("WorkerCommandQueue"));
+ resultQueue = std::static_pointer_cast<DemodulatorThreadWorkerResultQueue>(getOutputQueue("WorkerResultQueue"));
while (!stopping) {
bool filterChanged = false;
@@ -30,8 +33,11 @@ void DemodulatorWorkerThread::run() {
//Beware of the subtility here,
//we are waiting for the first command to show up (blocking!)
//then consuming the commands until done.
- while (!done) {
- commandQueue->pop(command);
+ while (!done && !stopping) {
+
+ if (!commandQueue->pop(command, HEARTBEAT_CHECK_PERIOD_MICROS)) {
+ continue;
+ }
switch (command.cmd) {
case DemodulatorWorkerThreadCommand::DEMOD_WORKER_THREAD_CMD_BUILD_FILTERS:
@@ -46,7 +52,7 @@ void DemodulatorWorkerThread::run() {
break;
}
done = commandQueue->empty();
- }
+ } //end while done.
if ((makeDemod || filterChanged) && !stopping) {
DemodulatorWorkerThreadResult result(DemodulatorWorkerThreadResult::DEMOD_WORKER_THREAD_RESULT_FILTERS);
@@ -110,6 +116,7 @@ void DemodulatorWorkerThread::run() {
void DemodulatorWorkerThread::terminate() {
IOThread::terminate();
- DemodulatorWorkerThreadCommand inp; // push dummy to nudge queue
- commandQueue->push(inp);
+ //unblock the push()
+ resultQueue->flush();
+ commandQueue->flush();
}
diff --git a/src/demod/DemodulatorWorkerThread.h b/src/demod/DemodulatorWorkerThread.h
index cb56176..a4c3488 100644
--- a/src/demod/DemodulatorWorkerThread.h
+++ b/src/demod/DemodulatorWorkerThread.h
@@ -5,7 +5,7 @@
#include <queue>
#include <vector>
-
+#include <memory>
#include "liquid/liquid.h"
#include "AudioThread.h"
#include "ThreadBlockingQueue.h"
@@ -32,8 +32,6 @@ public:
msresamp_crcf iqResampler;
double iqResampleRatio;
-
- DemodulatorThread *demodThread;
long long sampleRate;
unsigned int bandwidth;
@@ -72,19 +70,22 @@ public:
typedef ThreadBlockingQueue<DemodulatorWorkerThreadCommand> DemodulatorThreadWorkerCommandQueue;
typedef ThreadBlockingQueue<DemodulatorWorkerThreadResult> DemodulatorThreadWorkerResultQueue;
+typedef std::shared_ptr<DemodulatorThreadWorkerCommandQueue> DemodulatorThreadWorkerCommandQueuePtr;
+typedef std::shared_ptr<DemodulatorThreadWorkerResultQueue> DemodulatorThreadWorkerResultQueuePtr;
+
class DemodulatorWorkerThread : public IOThread {
public:
DemodulatorWorkerThread();
- ~DemodulatorWorkerThread();
+ virtual ~DemodulatorWorkerThread();
virtual void run();
- void setCommandQueue(DemodulatorThreadWorkerCommandQueue *tQueue) {
+ void setCommandQueue(DemodulatorThreadWorkerCommandQueuePtr tQueue) {
commandQueue = tQueue;
}
- void setResultQueue(DemodulatorThreadWorkerResultQueue *tQueue) {
+ void setResultQueue(DemodulatorThreadWorkerResultQueuePtr tQueue) {
resultQueue = tQueue;
}
@@ -92,8 +93,8 @@ public:
protected:
- DemodulatorThreadWorkerCommandQueue *commandQueue;
- DemodulatorThreadWorkerResultQueue *resultQueue;
+ DemodulatorThreadWorkerCommandQueuePtr commandQueue;
+ DemodulatorThreadWorkerResultQueuePtr resultQueue;
Modem *cModem;
ModemKit *cModemKit;
std::string cModemType;
diff --git a/src/forms/Bookmark/BookmarkPanel.cpp b/src/forms/Bookmark/BookmarkPanel.cpp
index cb3efd4..73b1fb2 100644
--- a/src/forms/Bookmark/BookmarkPanel.cpp
+++ b/src/forms/Bookmark/BookmarkPanel.cpp
@@ -1,136 +1,136 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Jun 17 2015)
+// C++ code generated with wxFormBuilder (version Oct 27 2017)
// http://www.wxformbuilder.org/
//
-// PLEASE DO "NOT" EDIT THIS FILE!
+// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "BookmarkPanel.h"
///////////////////////////////////////////////////////////////////////////
-BookmarkPanel::BookmarkPanel( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style )
+BookmarkPanel::BookmarkPanel(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) : wxPanel(parent, id, pos, size, style)
{
wxBoxSizer* bSizer1;
- bSizer1 = new wxBoxSizer( wxVERTICAL );
-
- m_searchText = new wxTextCtrl( this, wxID_ANY, wxT("Search.."), wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
- bSizer1->Add( m_searchText, 0, wxALL|wxEXPAND, 5 );
-
- m_clearSearchButton = new wxButton( this, wxID_ANY, wxT("Clear Search"), wxDefaultPosition, wxDefaultSize, 0 );
- bSizer1->Add( m_clearSearchButton, 0, wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 5 );
-
- m_treeView = new wxTreeCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_DEFAULT_STYLE|wxTR_HAS_VARIABLE_ROW_HEIGHT|wxTR_HIDE_ROOT|wxTR_SINGLE );
- bSizer1->Add( m_treeView, 1, wxEXPAND, 5 );
-
- m_propPanel = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ bSizer1 = new wxBoxSizer(wxVERTICAL);
+
+ m_searchText = new wxTextCtrl(this, wxID_ANY, wxT("Search.."), wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
+ bSizer1->Add(m_searchText, 0, wxALL | wxEXPAND, 5);
+
+ m_clearSearchButton = new wxButton(this, wxID_ANY, wxT("Clear Search"), wxDefaultPosition, wxDefaultSize, 0);
+ bSizer1->Add(m_clearSearchButton, 0, wxBOTTOM | wxEXPAND | wxLEFT | wxRIGHT, 5);
+
+ m_treeView = new wxTreeCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_DEFAULT_STYLE | wxTR_HAS_VARIABLE_ROW_HEIGHT | wxTR_HIDE_ROOT | wxTR_SINGLE);
+ bSizer1->Add(m_treeView, 1, wxEXPAND, 5);
+
+ m_propPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
wxFlexGridSizer* fgPropSizer;
- fgPropSizer = new wxFlexGridSizer( 0, 2, 0, 0 );
- fgPropSizer->AddGrowableCol( 1 );
- fgPropSizer->SetFlexibleDirection( wxBOTH );
- fgPropSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
-
- m_labelLabel = new wxStaticText( m_propPanel, wxID_ANY, wxT("Label"), wxDefaultPosition, wxDefaultSize, 0 );
- m_labelLabel->Wrap( -1 );
- fgPropSizer->Add( m_labelLabel, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 );
-
- m_labelText = new wxTextCtrl( m_propPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
- fgPropSizer->Add( m_labelText, 0, wxALL|wxEXPAND, 5 );
-
- m_frequencyLabel = new wxStaticText( m_propPanel, wxID_ANY, wxT("Freq"), wxDefaultPosition, wxDefaultSize, 0 );
- m_frequencyLabel->Wrap( -1 );
- fgPropSizer->Add( m_frequencyLabel, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 );
-
- m_frequencyVal = new wxStaticText( m_propPanel, wxID_ANY, wxT("FrequencyVal"), wxDefaultPosition, wxDefaultSize, 0 );
- m_frequencyVal->Wrap( -1 );
- fgPropSizer->Add( m_frequencyVal, 0, wxALL, 5 );
-
- m_bandwidthLabel = new wxStaticText( m_propPanel, wxID_ANY, wxT("BW"), wxDefaultPosition, wxDefaultSize, 0 );
- m_bandwidthLabel->Wrap( -1 );
- fgPropSizer->Add( m_bandwidthLabel, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 );
-
- m_bandwidthVal = new wxStaticText( m_propPanel, wxID_ANY, wxT("BandwidthVal"), wxDefaultPosition, wxDefaultSize, 0 );
- m_bandwidthVal->Wrap( -1 );
- fgPropSizer->Add( m_bandwidthVal, 0, wxALL, 5 );
-
- m_modulationLabel = new wxStaticText( m_propPanel, wxID_ANY, wxT("Type"), wxDefaultPosition, wxDefaultSize, 0 );
- m_modulationLabel->Wrap( -1 );
- fgPropSizer->Add( m_modulationLabel, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 );
-
- m_modulationVal = new wxStaticText( m_propPanel, wxID_ANY, wxT("TypeVal"), wxDefaultPosition, wxDefaultSize, 0 );
- m_modulationVal->Wrap( -1 );
- fgPropSizer->Add( m_modulationVal, 0, wxALL, 5 );
-
-
- m_propPanel->SetSizer( fgPropSizer );
+ fgPropSizer = new wxFlexGridSizer(0, 2, 0, 0);
+ fgPropSizer->AddGrowableCol(1);
+ fgPropSizer->SetFlexibleDirection(wxBOTH);
+ fgPropSizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
+
+ m_labelLabel = new wxStaticText(m_propPanel, wxID_ANY, wxT("Label"), wxDefaultPosition, wxDefaultSize, 0);
+ m_labelLabel->Wrap(-1);
+ fgPropSizer->Add(m_labelLabel, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 5);
+
+ m_labelText = new wxTextCtrl(m_propPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
+ fgPropSizer->Add(m_labelText, 0, wxALL | wxEXPAND, 5);
+
+ m_frequencyLabel = new wxStaticText(m_propPanel, wxID_ANY, wxT("Freq"), wxDefaultPosition, wxDefaultSize, 0);
+ m_frequencyLabel->Wrap(-1);
+ fgPropSizer->Add(m_frequencyLabel, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 5);
+
+ m_frequencyVal = new wxStaticText(m_propPanel, wxID_ANY, wxT("FrequencyVal"), wxDefaultPosition, wxDefaultSize, 0);
+ m_frequencyVal->Wrap(-1);
+ fgPropSizer->Add(m_frequencyVal, 0, wxALL, 5);
+
+ m_bandwidthLabel = new wxStaticText(m_propPanel, wxID_ANY, wxT("BW"), wxDefaultPosition, wxDefaultSize, 0);
+ m_bandwidthLabel->Wrap(-1);
+ fgPropSizer->Add(m_bandwidthLabel, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 5);
+
+ m_bandwidthVal = new wxStaticText(m_propPanel, wxID_ANY, wxT("BandwidthVal"), wxDefaultPosition, wxDefaultSize, 0);
+ m_bandwidthVal->Wrap(-1);
+ fgPropSizer->Add(m_bandwidthVal, 0, wxALL, 5);
+
+ m_modulationLabel = new wxStaticText(m_propPanel, wxID_ANY, wxT("Type"), wxDefaultPosition, wxDefaultSize, 0);
+ m_modulationLabel->Wrap(-1);
+ fgPropSizer->Add(m_modulationLabel, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 5);
+
+ m_modulationVal = new wxStaticText(m_propPanel, wxID_ANY, wxT("TypeVal"), wxDefaultPosition, wxDefaultSize, 0);
+ m_modulationVal->Wrap(-1);
+ fgPropSizer->Add(m_modulationVal, 0, wxALL, 5);
+
+
+ m_propPanel->SetSizer(fgPropSizer);
m_propPanel->Layout();
- fgPropSizer->Fit( m_propPanel );
- bSizer1->Add( m_propPanel, 0, wxALL|wxBOTTOM|wxEXPAND|wxTOP, 5 );
-
- m_buttonPanel = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ fgPropSizer->Fit(m_propPanel);
+ bSizer1->Add(m_propPanel, 0, wxALL | wxBOTTOM | wxEXPAND | wxTOP, 5);
+
+ m_buttonPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
wxBoxSizer* m_buttonPanelSizer;
- m_buttonPanelSizer = new wxBoxSizer( wxVERTICAL );
-
-
- m_buttonPanel->SetSizer( m_buttonPanelSizer );
+ m_buttonPanelSizer = new wxBoxSizer(wxVERTICAL);
+
+
+ m_buttonPanel->SetSizer(m_buttonPanelSizer);
m_buttonPanel->Layout();
- m_buttonPanelSizer->Fit( m_buttonPanel );
- bSizer1->Add( m_buttonPanel, 0, wxALL|wxEXPAND, 5 );
-
-
- this->SetSizer( bSizer1 );
+ m_buttonPanelSizer->Fit(m_buttonPanel);
+ bSizer1->Add(m_buttonPanel, 0, wxALL | wxEXPAND, 5);
+
+
+ this->SetSizer(bSizer1);
this->Layout();
- m_updateTimer.SetOwner( this, wxID_ANY );
-
+ m_updateTimer.SetOwner(this, wxID_ANY);
+
// Connect Events
- this->Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( BookmarkPanel::onEnterWindow ) );
- this->Connect( wxEVT_LEAVE_WINDOW, wxMouseEventHandler( BookmarkPanel::onLeaveWindow ) );
- this->Connect( wxEVT_MOTION, wxMouseEventHandler( BookmarkPanel::onMotion ) );
- m_searchText->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( BookmarkPanel::onSearchTextFocus ), NULL, this );
- m_searchText->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( BookmarkPanel::onSearchText ), NULL, this );
- m_clearSearchButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BookmarkPanel::onClearSearch ), NULL, this );
- m_treeView->Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( BookmarkPanel::onEnterWindow ), NULL, this );
- m_treeView->Connect( wxEVT_LEAVE_WINDOW, wxMouseEventHandler( BookmarkPanel::onLeaveWindow ), NULL, this );
- m_treeView->Connect( wxEVT_MOTION, wxMouseEventHandler( BookmarkPanel::onMotion ), NULL, this );
- m_treeView->Connect( wxEVT_COMMAND_TREE_BEGIN_DRAG, wxTreeEventHandler( BookmarkPanel::onTreeBeginDrag ), NULL, this );
- m_treeView->Connect( wxEVT_COMMAND_TREE_END_DRAG, wxTreeEventHandler( BookmarkPanel::onTreeEndDrag ), NULL, this );
- m_treeView->Connect( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, wxTreeEventHandler( BookmarkPanel::onTreeActivate ), NULL, this );
- m_treeView->Connect( wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxTreeEventHandler( BookmarkPanel::onTreeCollapse ), NULL, this );
- m_treeView->Connect( wxEVT_COMMAND_TREE_ITEM_EXPANDED, wxTreeEventHandler( BookmarkPanel::onTreeExpanded ), NULL, this );
- m_treeView->Connect( wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, wxTreeEventHandler( BookmarkPanel::onTreeItemGetTooltip ), NULL, this );
- m_treeView->Connect( wxEVT_COMMAND_TREE_ITEM_MENU, wxTreeEventHandler( BookmarkPanel::onTreeItemMenu ), NULL, this );
- m_treeView->Connect( wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler( BookmarkPanel::onTreeSelect ), NULL, this );
- m_treeView->Connect( wxEVT_COMMAND_TREE_SEL_CHANGING, wxTreeEventHandler( BookmarkPanel::onTreeSelectChanging ), NULL, this );
- m_labelText->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( BookmarkPanel::onLabelText ), NULL, this );
- m_frequencyVal->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( BookmarkPanel::onDoubleClickFreq ), NULL, this );
- m_bandwidthVal->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( BookmarkPanel::onDoubleClickBandwidth ), NULL, this );
- this->Connect( wxID_ANY, wxEVT_TIMER, wxTimerEventHandler( BookmarkPanel::onUpdateTimer ) );
+ this->Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(BookmarkPanel::onEnterWindow));
+ this->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(BookmarkPanel::onLeaveWindow));
+ this->Connect(wxEVT_MOTION, wxMouseEventHandler(BookmarkPanel::onMotion));
+ m_searchText->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(BookmarkPanel::onSearchTextFocus), NULL, this);
+ m_searchText->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(BookmarkPanel::onSearchText), NULL, this);
+ m_clearSearchButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BookmarkPanel::onClearSearch), NULL, this);
+ m_treeView->Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(BookmarkPanel::onEnterWindow), NULL, this);
+ m_treeView->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(BookmarkPanel::onLeaveWindow), NULL, this);
+ m_treeView->Connect(wxEVT_MOTION, wxMouseEventHandler(BookmarkPanel::onMotion), NULL, this);
+ m_treeView->Connect(wxEVT_COMMAND_TREE_BEGIN_DRAG, wxTreeEventHandler(BookmarkPanel::onTreeBeginDrag), NULL, this);
+ m_treeView->Connect(wxEVT_COMMAND_TREE_END_DRAG, wxTreeEventHandler(BookmarkPanel::onTreeEndDrag), NULL, this);
+ m_treeView->Connect(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, wxTreeEventHandler(BookmarkPanel::onTreeActivate), NULL, this);
+ m_treeView->Connect(wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxTreeEventHandler(BookmarkPanel::onTreeCollapse), NULL, this);
+ m_treeView->Connect(wxEVT_COMMAND_TREE_ITEM_EXPANDED, wxTreeEventHandler(BookmarkPanel::onTreeExpanded), NULL, this);
+ m_treeView->Connect(wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, wxTreeEventHandler(BookmarkPanel::onTreeItemGetTooltip), NULL, this);
+ m_treeView->Connect(wxEVT_COMMAND_TREE_ITEM_MENU, wxTreeEventHandler(BookmarkPanel::onTreeItemMenu), NULL, this);
+ m_treeView->Connect(wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler(BookmarkPanel::onTreeSelect), NULL, this);
+ m_treeView->Connect(wxEVT_COMMAND_TREE_SEL_CHANGING, wxTreeEventHandler(BookmarkPanel::onTreeSelectChanging), NULL, this);
+ m_labelText->Connect(wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler(BookmarkPanel::onLabelText), NULL, this);
+ m_frequencyVal->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(BookmarkPanel::onDoubleClickFreq), NULL, this);
+ m_bandwidthVal->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(BookmarkPanel::onDoubleClickBandwidth), NULL, this);
+ this->Connect(wxID_ANY, wxEVT_TIMER, wxTimerEventHandler(BookmarkPanel::onUpdateTimer));
}
BookmarkPanel::~BookmarkPanel()
{
// Disconnect Events
- this->Disconnect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( BookmarkPanel::onEnterWindow ) );
- this->Disconnect( wxEVT_LEAVE_WINDOW, wxMouseEventHandler( BookmarkPanel::onLeaveWindow ) );
- this->Disconnect( wxEVT_MOTION, wxMouseEventHandler( BookmarkPanel::onMotion ) );
- m_searchText->Disconnect( wxEVT_LEFT_DOWN, wxMouseEventHandler( BookmarkPanel::onSearchTextFocus ), NULL, this );
- m_searchText->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( BookmarkPanel::onSearchText ), NULL, this );
- m_clearSearchButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BookmarkPanel::onClearSearch ), NULL, this );
- m_treeView->Disconnect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( BookmarkPanel::onEnterWindow ), NULL, this );
- m_treeView->Disconnect( wxEVT_LEAVE_WINDOW, wxMouseEventHandler( BookmarkPanel::onLeaveWindow ), NULL, this );
- m_treeView->Disconnect( wxEVT_MOTION, wxMouseEventHandler( BookmarkPanel::onMotion ), NULL, this );
- m_treeView->Disconnect( wxEVT_COMMAND_TREE_BEGIN_DRAG, wxTreeEventHandler( BookmarkPanel::onTreeBeginDrag ), NULL, this );
- m_treeView->Disconnect( wxEVT_COMMAND_TREE_END_DRAG, wxTreeEventHandler( BookmarkPanel::onTreeEndDrag ), NULL, this );
- m_treeView->Disconnect( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, wxTreeEventHandler( BookmarkPanel::onTreeActivate ), NULL, this );
- m_treeView->Disconnect( wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxTreeEventHandler( BookmarkPanel::onTreeCollapse ), NULL, this );
- m_treeView->Disconnect( wxEVT_COMMAND_TREE_ITEM_EXPANDED, wxTreeEventHandler( BookmarkPanel::onTreeExpanded ), NULL, this );
- m_treeView->Disconnect( wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, wxTreeEventHandler( BookmarkPanel::onTreeItemGetTooltip ), NULL, this );
- m_treeView->Disconnect( wxEVT_COMMAND_TREE_ITEM_MENU, wxTreeEventHandler( BookmarkPanel::onTreeItemMenu ), NULL, this );
- m_treeView->Disconnect( wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler( BookmarkPanel::onTreeSelect ), NULL, this );
- m_treeView->Disconnect( wxEVT_COMMAND_TREE_SEL_CHANGING, wxTreeEventHandler( BookmarkPanel::onTreeSelectChanging ), NULL, this );
- m_labelText->Disconnect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( BookmarkPanel::onLabelText ), NULL, this );
- m_frequencyVal->Disconnect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( BookmarkPanel::onDoubleClickFreq ), NULL, this );
- m_bandwidthVal->Disconnect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( BookmarkPanel::onDoubleClickBandwidth ), NULL, this );
- this->Disconnect( wxID_ANY, wxEVT_TIMER, wxTimerEventHandler( BookmarkPanel::onUpdateTimer ) );
-
+ this->Disconnect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(BookmarkPanel::onEnterWindow));
+ this->Disconnect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(BookmarkPanel::onLeaveWindow));
+ this->Disconnect(wxEVT_MOTION, wxMouseEventHandler(BookmarkPanel::onMotion));
+ m_searchText->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(BookmarkPanel::onSearchTextFocus), NULL, this);
+ m_searchText->Disconnect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(BookmarkPanel::onSearchText), NULL, this);
+ m_clearSearchButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BookmarkPanel::onClearSearch), NULL, this);
+ m_treeView->Disconnect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(BookmarkPanel::onEnterWindow), NULL, this);
+ m_treeView->Disconnect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(BookmarkPanel::onLeaveWindow), NULL, this);
+ m_treeView->Disconnect(wxEVT_MOTION, wxMouseEventHandler(BookmarkPanel::onMotion), NULL, this);
+ m_treeView->Disconnect(wxEVT_COMMAND_TREE_BEGIN_DRAG, wxTreeEventHandler(BookmarkPanel::onTreeBeginDrag), NULL, this);
+ m_treeView->Disconnect(wxEVT_COMMAND_TREE_END_DRAG, wxTreeEventHandler(BookmarkPanel::onTreeEndDrag), NULL, this);
+ m_treeView->Disconnect(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, wxTreeEventHandler(BookmarkPanel::onTreeActivate), NULL, this);
+ m_treeView->Disconnect(wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxTreeEventHandler(BookmarkPanel::onTreeCollapse), NULL, this);
+ m_treeView->Disconnect(wxEVT_COMMAND_TREE_ITEM_EXPANDED, wxTreeEventHandler(BookmarkPanel::onTreeExpanded), NULL, this);
+ m_treeView->Disconnect(wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, wxTreeEventHandler(BookmarkPanel::onTreeItemGetTooltip), NULL, this);
+ m_treeView->Disconnect(wxEVT_COMMAND_TREE_ITEM_MENU, wxTreeEventHandler(BookmarkPanel::onTreeItemMenu), NULL, this);
+ m_treeView->Disconnect(wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler(BookmarkPanel::onTreeSelect), NULL, this);
+ m_treeView->Disconnect(wxEVT_COMMAND_TREE_SEL_CHANGING, wxTreeEventHandler(BookmarkPanel::onTreeSelectChanging), NULL, this);
+ m_labelText->Disconnect(wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler(BookmarkPanel::onLabelText), NULL, this);
+ m_frequencyVal->Disconnect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(BookmarkPanel::onDoubleClickFreq), NULL, this);
+ m_bandwidthVal->Disconnect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(BookmarkPanel::onDoubleClickBandwidth), NULL, this);
+ this->Disconnect(wxID_ANY, wxEVT_TIMER, wxTimerEventHandler(BookmarkPanel::onUpdateTimer));
+
}
diff --git a/src/forms/Bookmark/BookmarkPanel.h b/src/forms/Bookmark/BookmarkPanel.h
index f6fd49f..2e1a4fb 100644
--- a/src/forms/Bookmark/BookmarkPanel.h
+++ b/src/forms/Bookmark/BookmarkPanel.h
@@ -1,8 +1,8 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Jun 17 2015)
+// C++ code generated with wxFormBuilder (version Oct 27 2017)
// http://www.wxformbuilder.org/
//
-// PLEASE DO "NOT" EDIT THIS FILE!
+// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#ifndef __BOOKMARKPANEL_H__
@@ -29,53 +29,53 @@
///////////////////////////////////////////////////////////////////////////////
/// Class BookmarkPanel
///////////////////////////////////////////////////////////////////////////////
-class BookmarkPanel : public wxPanel
+class BookmarkPanel : public wxPanel
{
- private:
-
- protected:
- wxTextCtrl* m_searchText;
- wxButton* m_clearSearchButton;
- wxTreeCtrl* m_treeView;
- wxPanel* m_propPanel;
- wxStaticText* m_labelLabel;
- wxTextCtrl* m_labelText;
- wxStaticText* m_frequencyLabel;
- wxStaticText* m_frequencyVal;
- wxStaticText* m_bandwidthLabel;
- wxStaticText* m_bandwidthVal;
- wxStaticText* m_modulationLabel;
- wxStaticText* m_modulationVal;
- wxPanel* m_buttonPanel;
- wxTimer m_updateTimer;
-
- // Virtual event handlers, overide them in your derived class
- virtual void onEnterWindow( wxMouseEvent& event ) { event.Skip(); }
- virtual void onLeaveWindow( wxMouseEvent& event ) { event.Skip(); }
- virtual void onMotion( wxMouseEvent& event ) { event.Skip(); }
- virtual void onSearchTextFocus( wxMouseEvent& event ) { event.Skip(); }
- virtual void onSearchText( wxCommandEvent& event ) { event.Skip(); }
- virtual void onClearSearch( wxCommandEvent& event ) { event.Skip(); }
- virtual void onTreeBeginDrag( wxTreeEvent& event ) { event.Skip(); }
- virtual void onTreeEndDrag( wxTreeEvent& event ) { event.Skip(); }
- virtual void onTreeActivate( wxTreeEvent& event ) { event.Skip(); }
- virtual void onTreeCollapse( wxTreeEvent& event ) { event.Skip(); }
- virtual void onTreeExpanded( wxTreeEvent& event ) { event.Skip(); }
- virtual void onTreeItemGetTooltip( wxTreeEvent& event ) { event.Skip(); }
- virtual void onTreeItemMenu( wxTreeEvent& event ) { event.Skip(); }
- virtual void onTreeSelect( wxTreeEvent& event ) { event.Skip(); }
- virtual void onTreeSelectChanging( wxTreeEvent& event ) { event.Skip(); }
- virtual void onLabelText( wxCommandEvent& event ) { event.Skip(); }
- virtual void onDoubleClickFreq( wxMouseEvent& event ) { event.Skip(); }
- virtual void onDoubleClickBandwidth( wxMouseEvent& event ) { event.Skip(); }
- virtual void onUpdateTimer( wxTimerEvent& event ) { event.Skip(); }
-
-
- public:
-
- BookmarkPanel( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 169,471 ), long style = wxTAB_TRAVERSAL );
- ~BookmarkPanel();
-
+private:
+
+protected:
+ wxTextCtrl* m_searchText;
+ wxButton* m_clearSearchButton;
+ wxTreeCtrl* m_treeView;
+ wxPanel* m_propPanel;
+ wxStaticText* m_labelLabel;
+ wxTextCtrl* m_labelText;
+ wxStaticText* m_frequencyLabel;
+ wxStaticText* m_frequencyVal;
+ wxStaticText* m_bandwidthLabel;
+ wxStaticText* m_bandwidthVal;
+ wxStaticText* m_modulationLabel;
+ wxStaticText* m_modulationVal;
+ wxPanel* m_buttonPanel;
+ wxTimer m_updateTimer;
+
+ // Virtual event handlers, overide them in your derived class
+ virtual void onEnterWindow(wxMouseEvent& event) { event.Skip(); }
+ virtual void onLeaveWindow(wxMouseEvent& event) { event.Skip(); }
+ virtual void onMotion(wxMouseEvent& event) { event.Skip(); }
+ virtual void onSearchTextFocus(wxMouseEvent& event) { event.Skip(); }
+ virtual void onSearchText(wxCommandEvent& event) { event.Skip(); }
+ virtual void onClearSearch(wxCommandEvent& event) { event.Skip(); }
+ virtual void onTreeBeginDrag(wxTreeEvent& event) { event.Skip(); }
+ virtual void onTreeEndDrag(wxTreeEvent& event) { event.Skip(); }
+ virtual void onTreeActivate(wxTreeEvent& event) { event.Skip(); }
+ virtual void onTreeCollapse(wxTreeEvent& event) { event.Skip(); }
+ virtual void onTreeExpanded(wxTreeEvent& event) { event.Skip(); }
+ virtual void onTreeItemGetTooltip(wxTreeEvent& event) { event.Skip(); }
+ virtual void onTreeItemMenu(wxTreeEvent& event) { event.Skip(); }
+ virtual void onTreeSelect(wxTreeEvent& event) { event.Skip(); }
+ virtual void onTreeSelectChanging(wxTreeEvent& event) { event.Skip(); }
+ virtual void onLabelText(wxCommandEvent& event) { event.Skip(); }
+ virtual void onDoubleClickFreq(wxMouseEvent& event) { event.Skip(); }
+ virtual void onDoubleClickBandwidth(wxMouseEvent& event) { event.Skip(); }
+ virtual void onUpdateTimer(wxTimerEvent& event) { event.Skip(); }
+
+
+public:
+
+ BookmarkPanel(wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(169, 471), long style = wxTAB_TRAVERSAL);
+ ~BookmarkPanel();
+
};
#endif //__BOOKMARKPANEL_H__
diff --git a/src/forms/Bookmark/BookmarkView.cpp b/src/forms/Bookmark/BookmarkView.cpp
index 883b813..ee84b13 100644
--- a/src/forms/Bookmark/BookmarkView.cpp
+++ b/src/forms/Bookmark/BookmarkView.cpp
@@ -78,7 +78,8 @@ public:
if (name.length() == 0) {
std::string wstr = frequencyToStr(rangeEnt->startFreq) + " - " + frequencyToStr(rangeEnt->endFreq);
- name = std::wstring(wstr.begin(),wstr.end());
+
+ name = wxString(wstr).ToStdWstring();
}
m_questionText->SetLabelText(L"Are you sure you want to remove the range\n '" + name + L"'?");
@@ -103,7 +104,8 @@ public:
if (name.length() == 0) {
std::string wstr = frequencyToStr(rangeEnt->startFreq) + " - " + frequencyToStr(rangeEnt->endFreq);
- name = std::wstring(wstr.begin(),wstr.end());
+
+ name = wxString(wstr).ToStdWstring();
}
m_questionText->SetLabelText(L"Are you sure you want to update the range\n '" + name + L"' to the active range?");
@@ -311,9 +313,9 @@ wxTreeItemId BookmarkView::refreshBookmarks() {
std::wstring fullText = labelVal +
L" " + bmEnt->label +
L" " + std::to_wstring(bmEnt->frequency) +
- L" " + std::wstring(freqStr.begin(),freqStr.end()) +
- L" " + std::wstring(bwStr.begin(),bwStr.end()) +
- L" " + std::wstring(bmEnt->type.begin(),bmEnt->type.end());
+ L" " + wxString(freqStr).ToStdWstring() +
+ L" " + wxString(bwStr).ToStdWstring() +
+ L" " + wxString(bmEnt->type).ToStdWstring();
if (!isKeywordMatch(fullText, searchKeywords)) {
continue;
@@ -348,10 +350,9 @@ wxTreeItemId BookmarkView::refreshBookmarks() {
void BookmarkView::doUpdateActiveList() {
- std::vector<DemodulatorInstance *> &demods = wxGetApp().getDemodMgr().getDemodulators();
-
-// DemodulatorInstance *activeDemodulator = wxGetApp().getDemodMgr().getActiveDemodulator();
- DemodulatorInstance *lastActiveDemodulator = wxGetApp().getDemodMgr().getLastActiveDemodulator();
+
+ auto demods = wxGetApp().getDemodMgr().getDemodulators();
+ auto lastActiveDemodulator = wxGetApp().getDemodMgr().getLastActiveDemodulator();
//capture the previously selected item info BY COPY (because the original will be destroyed together with the destroyed tree items) to restore it again after
//having rebuilding the whole tree.
@@ -380,9 +381,9 @@ void BookmarkView::doUpdateActiveList() {
std::wstring fullText = activeLabel.ToStdWstring() +
L" " + demod_i->getDemodulatorUserLabel() +
L" " + std::to_wstring(demod_i->getFrequency()) +
- L" " + std::wstring(freqStr.begin(),freqStr.end()) +
- L" " + std::wstring(bwStr.begin(),bwStr.end()) +
- L" " + std::wstring(mtype.begin(),mtype.end());
+ L" " + wxString(freqStr).ToStdWstring() +
+ L" " + wxString(bwStr).ToStdWstring() +
+ L" " + wxString(mtype).ToStdWstring();
if (!isKeywordMatch(fullText, searchKeywords)) {
continue;
@@ -419,9 +420,10 @@ void BookmarkView::doUpdateActiveList() {
std::wstring labelVal = re_i->label;
- if (labelVal == "") {
+ if (labelVal == L"") {
std::string wstr = frequencyToStr(re_i->startFreq) + " - " + frequencyToStr(re_i->endFreq);
- labelVal = std::wstring(wstr.begin(),wstr.end());
+
+ labelVal = wxString(wstr).ToStdWstring();
}
wxTreeItemId itm = m_treeView->AppendItem(rangeBranch, labelVal);
@@ -449,9 +451,10 @@ void BookmarkView::doUpdateActiveList() {
std::wstring labelVal;
bmr_i->node->child("user_label")->element()->get(labelVal);
- if (labelVal == "") {
- std::string wstr = frequencyToStr(bmr_i->frequency) + " " + bmr_i->type;
- labelVal = std::wstring(wstr.begin(),wstr.end());
+ if (labelVal == L"") {
+ std::string str = frequencyToStr(bmr_i->frequency) + " " + bmr_i->type;
+
+ labelVal = wxString(str).ToStdWstring();
}
if (searchKeywords.size()) {
@@ -461,9 +464,10 @@ void BookmarkView::doUpdateActiveList() {
std::wstring fullText = labelVal +
L" " + std::to_wstring(bmr_i->frequency) +
- L" " + std::wstring(freqStr.begin(),freqStr.end()) +
- L" " + std::wstring(bwStr.begin(),bwStr.end()) +
- L" " + std::wstring(bmr_i->type.begin(),tvi->bookmarkEnt->type.end());
+
+ L" " + wxString(freqStr).ToStdWstring() +
+ L" " + wxString(bwStr).ToStdWstring() +
+ L" " + wxString(bmr_i->type).ToStdWstring();
if (!isKeywordMatch(fullText, searchKeywords)) {
continue;
@@ -693,7 +697,8 @@ wxButton *BookmarkView::addButton(wxWindow *parent, std::string labelVal, wxObje
}
-void BookmarkView::doBookmarkActive(std::string group, DemodulatorInstance *demod) {
+void BookmarkView::doBookmarkActive(std::string group, DemodulatorInstancePtr demod) {
+
wxGetApp().getBookmarkMgr().addBookmark(group, demod);
wxGetApp().getBookmarkMgr().updateBookmarks();
}
@@ -717,7 +722,7 @@ void BookmarkView::doMoveBookmark(BookmarkEntryPtr be, std::string group) {
}
-void BookmarkView::doRemoveActive(DemodulatorInstance *demod) {
+void BookmarkView::doRemoveActive(DemodulatorInstancePtr demod) {
wxGetApp().getBookmarkMgr().removeActive(demod);
wxGetApp().getBookmarkMgr().updateActiveList();
@@ -792,7 +797,7 @@ void BookmarkView::onBookmarkChoice( wxCommandEvent & /* event */ ) {
}
-void BookmarkView::activeSelection(DemodulatorInstance *dsel) {
+void BookmarkView::activeSelection(DemodulatorInstancePtr dsel) {
m_frequencyVal->SetLabelText(frequencyToStr(dsel->getFrequency()));
m_bandwidthVal->SetLabelText(frequencyToStr(dsel->getBandwidth()));
@@ -816,8 +821,16 @@ void BookmarkView::activeSelection(DemodulatorInstance *dsel) {
clearButtons();
addBookmarkChoice(m_buttonPanel);
- addButton(m_buttonPanel, "Remove Active", wxCommandEventHandler( BookmarkView::onRemoveActive ));
+
+ if (dsel->isActive() && !(dsel->isRecording())) {
+ addButton(m_buttonPanel, "Start Recording", wxCommandEventHandler( BookmarkView::onStartRecording ));
+ } else {
+ addButton(m_buttonPanel, "Stop Recording", wxCommandEventHandler( BookmarkView::onStopRecording ));
+ }
+ addButton(m_buttonPanel, "Remove Active", wxCommandEventHandler( BookmarkView::onRemoveActive ));
+
+
showProps();
showButtons();
refreshLayout();
@@ -835,7 +848,7 @@ void BookmarkView::activateBookmark(BookmarkEntryPtr bmEnt) {
//the already existing one:
// we search among the list of existing demodulators the one matching
//bmEnt and activate it. The search is made backwards, to select the most recently created one.
- DemodulatorInstance *matchingDemod = wxGetApp().getDemodMgr().getLastDemodulatorWith(
+ DemodulatorInstancePtr matchingDemod = wxGetApp().getDemodMgr().getLastDemodulatorWith(
bmEnt->type,
bmEnt->label,
bmEnt->frequency,
@@ -845,7 +858,7 @@ void BookmarkView::activateBookmark(BookmarkEntryPtr bmEnt) {
matchingDemod = wxGetApp().getDemodMgr().loadInstance(bmEnt->node);
matchingDemod->run();
- wxGetApp().bindDemodulator(matchingDemod);
+ wxGetApp().notifyDemodulatorsChanged();
}
matchingDemod->setActive(true);
@@ -865,7 +878,11 @@ void BookmarkView::activateBookmark(BookmarkEntryPtr bmEnt) {
void BookmarkView::activateRange(BookmarkRangeEntryPtr rangeEnt) {
- wxGetApp().setFrequency(rangeEnt->freq);
+
+ //the following oly works if rangeEnt->freq is the middle of [rangeEnt->startFreq ; rangeEnt->startFreq]
+ wxGetApp().setFrequency(rangeEnt->freq);
+
+ // Change View limits to fit the range exactly.
wxGetApp().getAppFrame()->setViewState(rangeEnt->startFreq + (rangeEnt->endFreq - rangeEnt->startFreq) / 2, rangeEnt->endFreq - rangeEnt->startFreq);
}
@@ -971,7 +988,7 @@ void BookmarkView::rangeSelection(BookmarkRangeEntryPtr re) {
std::string strFreq = frequencyToStr(re->startFreq) + "-" + frequencyToStr(re->endFreq);
- m_frequencyVal->SetLabelText(std::wstring(strFreq.begin(),strFreq.end()));
+ m_frequencyVal->SetLabelText(wxString(strFreq));
showProps();
@@ -1149,6 +1166,30 @@ void BookmarkView::onRemoveActive( wxCommandEvent& /* event */ ) {
}
}
+void BookmarkView::onStartRecording( wxCommandEvent& /* event */ ) {
+ TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection());
+
+ if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) {
+ if (!curSel->demod->isRecording() && wxGetApp().getConfig()->verifyRecordingPath()) {
+ curSel->demod->setRecording(true);
+ wxGetApp().getBookmarkMgr().updateActiveList();
+ }
+ }
+}
+
+
+void BookmarkView::onStopRecording( wxCommandEvent& /* event */ ) {
+ TreeViewItem *curSel = itemToTVI(m_treeView->GetSelection());
+
+ if (curSel && curSel->type == TreeViewItem::TREEVIEW_ITEM_TYPE_ACTIVE) {
+ if (curSel->demod->isRecording()) {
+ curSel->demod->setRecording(false);
+ wxGetApp().getBookmarkMgr().updateActiveList();
+ }
+ }
+}
+
+
void BookmarkView::onRemoveBookmark( wxCommandEvent& /* event */ ) {
if (editingLabel) {
@@ -1411,7 +1452,7 @@ void BookmarkView::onEnterWindow( wxMouseEvent& event ) {
}
#endif
- setStatusText("You can mouse-drag a bookmark entry from one category to the next..etc. TODO: add more Bookmarks descriptions");
+ setStatusText("Drag & Drop to create / move bookmarks, Group and arrange bookmarks, quick Search by keywords.");
}
@@ -1473,16 +1514,16 @@ void BookmarkView::onSearchTextFocus( wxMouseEvent& event ) {
void BookmarkView::onSearchText( wxCommandEvent& event ) {
- wstring searchText = m_searchText->GetValue().Trim().Lower().ToStdWstring();
+ std::wstring searchText = m_searchText->GetValue().Trim().Lower().ToStdWstring();
searchKeywords.clear();
if (searchText.length() != 0) {
std::wstringstream searchTextLo(searchText);
- wstring tmp;
+ std::wstring tmp;
while(std::getline(searchTextLo, tmp, L' ')) {
- if (tmp.length() != 0 && tmp.find(L"search.") == wstring::npos) {
+ if (tmp.length() != 0 && tmp.find(L"search.") == std::wstring::npos) {
searchKeywords.push_back(tmp);
// std::wcout << L"Keyword: " << tmp << '\n';
}
@@ -1531,10 +1572,13 @@ void BookmarkView::loadDefaultRanges() {
BookmarkRangeEntryPtr BookmarkView::makeActiveRangeEntry() {
BookmarkRangeEntryPtr re(new BookmarkRangeEntry);
- re->freq = wxGetApp().getFrequency();
+
re->startFreq = wxGetApp().getAppFrame()->getViewCenterFreq() - (wxGetApp().getAppFrame()->getViewBandwidth()/2);
re->endFreq = wxGetApp().getAppFrame()->getViewCenterFreq() + (wxGetApp().getAppFrame()->getViewBandwidth()/2);
+ //to prevent problems, always make the re->freq the middle of the interval.
+ re->freq = (re->startFreq + re->endFreq) / 2;
+
return re;
}
diff --git a/src/forms/Bookmark/BookmarkView.h b/src/forms/Bookmark/BookmarkView.h
index 6470bd5..820ece1 100644
--- a/src/forms/Bookmark/BookmarkView.h
+++ b/src/forms/Bookmark/BookmarkView.h
@@ -43,7 +43,7 @@ public:
BookmarkEntryPtr bookmarkEnt;
BookmarkRangeEntryPtr rangeEnt;
- DemodulatorInstance* demod;
+ DemodulatorInstancePtr demod;
std::string groupName;
};
@@ -84,7 +84,7 @@ public:
static BookmarkRangeEntryPtr makeActiveRangeEntry();
protected:
- void activeSelection(DemodulatorInstance *dsel);
+ void activeSelection(DemodulatorInstancePtr dsel);
void bookmarkSelection(BookmarkEntryPtr bmSel);
void rangeSelection(BookmarkRangeEntryPtr re);
@@ -133,10 +133,10 @@ protected:
wxButton *makeButton(wxWindow *parent, std::string labelVal, wxObjectEventFunction handler);
wxButton *addButton(wxWindow *parent, std::string labelVal, wxObjectEventFunction handler);
- void doBookmarkActive(std::string group, DemodulatorInstance *demod);
+ void doBookmarkActive(std::string group, DemodulatorInstancePtr demod);
void doBookmarkRecent(std::string group, BookmarkEntryPtr be);
void doMoveBookmark(BookmarkEntryPtr be, std::string group);
- void doRemoveActive(DemodulatorInstance *demod);
+ void doRemoveActive(DemodulatorInstancePtr demod);
void doRemoveRecent(BookmarkEntryPtr be);
void doClearRecents();
@@ -145,6 +145,8 @@ protected:
void onBookmarkChoice( wxCommandEvent &event );
void onRemoveActive( wxCommandEvent& event );
+ void onStartRecording( wxCommandEvent& event );
+ void onStopRecording( wxCommandEvent& event );
void onRemoveBookmark( wxCommandEvent& event );
void onActivateBookmark( wxCommandEvent& event );
@@ -191,7 +193,7 @@ protected:
// Focus
BookmarkEntryPtr nextEnt;
BookmarkRangeEntryPtr nextRange;
- DemodulatorInstance *nextDemod;
+ DemodulatorInstancePtr nextDemod;
std::string nextGroup;
// Search
diff --git a/src/forms/Dialog/AboutDialog.fbp b/src/forms/Dialog/AboutDialog.fbp
index 9f9cb8a..3d91594 100644
--- a/src/forms/Dialog/AboutDialog.fbp
+++ b/src/forms/Dialog/AboutDialog.fbp
@@ -6534,6 +6534,421 @@
<event name="OnUpdateUI"></event>
</object>
</object>
+ <object class="sizeritem" expanded="1">
+ <property name="border">5</property>
+ <property name="flag">wxALL</property>
+ <property name="proportion">0</property>
+ <object class="wxStaticText" expanded="1">
+ <property name="BottomDockable">1</property>
+ <property name="LeftDockable">1</property>
+ <property name="RightDockable">1</property>
+ <property name="TopDockable">1</property>
+ <property name="aui_layer"></property>
+ <property name="aui_name"></property>
+ <property name="aui_position"></property>
+ <property name="aui_row"></property>
+ <property name="best_size"></property>
+ <property name="bg"></property>
+ <property name="caption"></property>
+ <property name="caption_visible">1</property>
+ <property name="center_pane">0</property>
+ <property name="close_button">1</property>
+ <property name="context_help"></property>
+ <property name="context_menu">1</property>
+ <property name="default_pane">0</property>
+ <property name="dock">Dock</property>
+ <property name="dock_fixed">0</property>
+ <property name="docking">Left</property>
+ <property name="enabled">1</property>
+ <property name="fg"></property>
+ <property name="floatable">1</property>
+ <property name="font"></property>
+ <property name="gripper">0</property>
+ <property name="hidden">0</property>
+ <property name="id">wxID_ANY</property>
+ <property name="label">Chad Myslinsky</property>
+ <property name="max_size"></property>
+ <property name="maximize_button">0</property>
+ <property name="maximum_size"></property>
+ <property name="min_size"></property>
+ <property name="minimize_button">0</property>
+ <property name="minimum_size"></property>
+ <property name="moveable">1</property>
+ <property name="name">m_dChadMyslinsky</property>
+ <property name="pane_border">1</property>
+ <property name="pane_position"></property>
+ <property name="pane_size"></property>
+ <property name="permission">protected</property>
+ <property name="pin_button">1</property>
+ <property name="pos"></property>
+ <property name="resize">Resizable</property>
+ <property name="show">1</property>
+ <property name="size"></property>
+ <property name="style"></property>
+ <property name="subclass"></property>
+ <property name="toolbar_pane">0</property>
+ <property name="tooltip"></property>
+ <property name="window_extra_style"></property>
+ <property name="window_name"></property>
+ <property name="window_style"></property>
+ <property name="wrap">-1</property>
+ <event name="OnChar"></event>
+ <event name="OnEnterWindow"></event>
+ <event name="OnEraseBackground"></event>
+ <event name="OnKeyDown"></event>
+ <event name="OnKeyUp"></event>
+ <event name="OnKillFocus"></event>
+ <event name="OnLeaveWindow"></event>
+ <event name="OnLeftDClick"></event>
+ <event name="OnLeftDown"></event>
+ <event name="OnLeftUp"></event>
+ <event name="OnMiddleDClick"></event>
+ <event name="OnMiddleDown"></event>
+ <event name="OnMiddleUp"></event>
+ <event name="OnMotion"></event>
+ <event name="OnMouseEvents"></event>
+ <event name="OnMouseWheel"></event>
+ <event name="OnPaint"></event>
+ <event name="OnRightDClick"></event>
+ <event name="OnRightDown"></event>
+ <event name="OnRightUp"></event>
+ <event name="OnSetFocus"></event>
+ <event name="OnSize"></event>
+ <event name="OnUpdateUI"></event>
+ </object>
+ </object>
+ <object class="sizeritem" expanded="1">
+ <property name="border">5</property>
+ <property name="flag">wxALL</property>
+ <property name="proportion">0</property>
+ <object class="wxStaticText" expanded="1">
+ <property name="BottomDockable">1</property>
+ <property name="LeftDockable">1</property>
+ <property name="RightDockable">1</property>
+ <property name="TopDockable">1</property>
+ <property name="aui_layer"></property>
+ <property name="aui_name"></property>
+ <property name="aui_position"></property>
+ <property name="aui_row"></property>
+ <property name="best_size"></property>
+ <property name="bg"></property>
+ <property name="caption"></property>
+ <property name="caption_visible">1</property>
+ <property name="center_pane">0</property>
+ <property name="close_button">1</property>
+ <property name="context_help"></property>
+ <property name="context_menu">1</property>
+ <property name="default_pane">0</property>
+ <property name="dock">Dock</property>
+ <property name="dock_fixed">0</property>
+ <property name="docking">Left</property>
+ <property name="enabled">1</property>
+ <property name="fg"></property>
+ <property name="floatable">1</property>
+ <property name="font"></property>
+ <property name="gripper">0</property>
+ <property name="hidden">0</property>
+ <property name="id">wxID_ANY</property>
+ <property name="label">Charlie Bruckner</property>
+ <property name="max_size"></property>
+ <property name="maximize_button">0</property>
+ <property name="maximum_size"></property>
+ <property name="min_size"></property>
+ <property name="minimize_button">0</property>
+ <property name="minimum_size"></property>
+ <property name="moveable">1</property>
+ <property name="name">m_dCharlieBruckner</property>
+ <property name="pane_border">1</property>
+ <property name="pane_position"></property>
+ <property name="pane_size"></property>
+ <property name="permission">protected</property>
+ <property name="pin_button">1</property>
+ <property name="pos"></property>
+ <property name="resize">Resizable</property>
+ <property name="show">1</property>
+ <property name="size"></property>
+ <property name="style"></property>
+ <property name="subclass"></property>
+ <property name="toolbar_pane">0</property>
+ <property name="tooltip"></property>
+ <property name="window_extra_style"></property>
+ <property name="window_name"></property>
+ <property name="window_style"></property>
+ <property name="wrap">-1</property>
+ <event name="OnChar"></event>
+ <event name="OnEnterWindow"></event>
+ <event name="OnEraseBackground"></event>
+ <event name="OnKeyDown"></event>
+ <event name="OnKeyUp"></event>
+ <event name="OnKillFocus"></event>
+ <event name="OnLeaveWindow"></event>
+ <event name="OnLeftDClick"></event>
+ <event name="OnLeftDown"></event>
+ <event name="OnLeftUp"></event>
+ <event name="OnMiddleDClick"></event>
+ <event name="OnMiddleDown"></event>
+ <event name="OnMiddleUp"></event>
+ <event name="OnMotion"></event>
+ <event name="OnMouseEvents"></event>
+ <event name="OnMouseWheel"></event>
+ <event name="OnPaint"></event>
+ <event name="OnRightDClick"></event>
+ <event name="OnRightDown"></event>
+ <event name="OnRightUp"></event>
+ <event name="OnSetFocus"></event>
+ <event name="OnSize"></event>
+ <event name="OnUpdateUI"></event>
+ </object>
+ </object>
+ <object class="sizeritem" expanded="1">
+ <property name="border">5</property>
+ <property name="flag">wxALL</property>
+ <property name="proportion">0</property>
+ <object class="wxStaticText" expanded="1">
+ <property name="BottomDockable">1</property>
+ <property name="LeftDockable">1</property>
+ <property name="RightDockable">1</property>
+ <property name="TopDockable">1</property>
+ <property name="aui_layer"></property>
+ <property name="aui_name"></property>
+ <property name="aui_position"></property>
+ <property name="aui_row"></property>
+ <property name="best_size"></property>
+ <property name="bg"></property>
+ <property name="caption"></property>
+ <property name="caption_visible">1</property>
+ <property name="center_pane">0</property>
+ <property name="close_button">1</property>
+ <property name="context_help"></property>
+ <property name="context_menu">1</property>
+ <property name="default_pane">0</property>
+ <property name="dock">Dock</property>
+ <property name="dock_fixed">0</property>
+ <property name="docking">Left</property>
+ <property name="enabled">1</property>
+ <property name="fg"></property>
+ <property name="floatable">1</property>
+ <property name="font"></property>
+ <property name="gripper">0</property>
+ <property name="hidden">0</property>
+ <property name="id">wxID_ANY</property>
+ <property name="label">Jordan Parker</property>
+ <property name="max_size"></property>
+ <property name="maximize_button">0</property>
+ <property name="maximum_size"></property>
+ <property name="min_size"></property>
+ <property name="minimize_button">0</property>
+ <property name="minimum_size"></property>
+ <property name="moveable">1</property>
+ <property name="name">m_dJordanParker</property>
+ <property name="pane_border">1</property>
+ <property name="pane_position"></property>
+ <property name="pane_size"></property>
+ <property name="permission">protected</property>
+ <property name="pin_button">1</property>
+ <property name="pos"></property>
+ <property name="resize">Resizable</property>
+ <property name="show">1</property>
+ <property name="size"></property>
+ <property name="style"></property>
+ <property name="subclass"></property>
+ <property name="toolbar_pane">0</property>
+ <property name="tooltip"></property>
+ <property name="window_extra_style"></property>
+ <property name="window_name"></property>
+ <property name="window_style"></property>
+ <property name="wrap">-1</property>
+ <event name="OnChar"></event>
+ <event name="OnEnterWindow"></event>
+ <event name="OnEraseBackground"></event>
+ <event name="OnKeyDown"></event>
+ <event name="OnKeyUp"></event>
+ <event name="OnKillFocus"></event>
+ <event name="OnLeaveWindow"></event>
+ <event name="OnLeftDClick"></event>
+ <event name="OnLeftDown"></event>
+ <event name="OnLeftUp"></event>
+ <event name="OnMiddleDClick"></event>
+ <event name="OnMiddleDown"></event>
+ <event name="OnMiddleUp"></event>
+ <event name="OnMotion"></event>
+ <event name="OnMouseEvents"></event>
+ <event name="OnMouseWheel"></event>
+ <event name="OnPaint"></event>
+ <event name="OnRightDClick"></event>
+ <event name="OnRightDown"></event>
+ <event name="OnRightUp"></event>
+ <event name="OnSetFocus"></event>
+ <event name="OnSize"></event>
+ <event name="OnUpdateUI"></event>
+ </object>
+ </object>
+ <object class="sizeritem" expanded="1">
+ <property name="border">5</property>
+ <property name="flag">wxALL</property>
+ <property name="proportion">0</property>
+ <object class="wxStaticText" expanded="1">
+ <property name="BottomDockable">1</property>
+ <property name="LeftDockable">1</property>
+ <property name="RightDockable">1</property>
+ <property name="TopDockable">1</property>
+ <property name="aui_layer"></property>
+ <property name="aui_name"></property>
+ <property name="aui_position"></property>
+ <property name="aui_row"></property>
+ <property name="best_size"></property>
+ <property name="bg"></property>
+ <property name="caption"></property>
+ <property name="caption_visible">1</property>
+ <property name="center_pane">0</property>
+ <property name="close_button">1</property>
+ <property name="context_help"></property>
+ <property name="context_menu">1</property>
+ <property name="default_pane">0</property>
+ <property name="dock">Dock</property>
+ <property name="dock_fixed">0</property>
+ <property name="docking">Left</property>
+ <property name="enabled">1</property>
+ <property name="fg"></property>
+ <property name="floatable">1</property>
+ <property name="font"></property>
+ <property name="gripper">0</property>
+ <property name="hidden">0</property>
+ <property name="id">wxID_ANY</property>
+ <property name="label">Robert Chave</property>
+ <property name="max_size"></property>
+ <property name="maximize_button">0</property>
+ <property name="maximum_size"></property>
+ <property name="min_size"></property>
+ <property name="minimize_button">0</property>
+ <property name="minimum_size"></property>
+ <property name="moveable">1</property>
+ <property name="name">m_dRobertChave</property>
+ <property name="pane_border">1</property>
+ <property name="pane_position"></property>
+ <property name="pane_size"></property>
+ <property name="permission">protected</property>
+ <property name="pin_button">1</property>
+ <property name="pos"></property>
+ <property name="resize">Resizable</property>
+ <property name="show">1</property>
+ <property name="size"></property>
+ <property name="style"></property>
+ <property name="subclass"></property>
+ <property name="toolbar_pane">0</property>
+ <property name="tooltip"></property>
+ <property name="window_extra_style"></property>
+ <property name="window_name"></property>
+ <property name="window_style"></property>
+ <property name="wrap">-1</property>
+ <event name="OnChar"></event>
+ <event name="OnEnterWindow"></event>
+ <event name="OnEraseBackground"></event>
+ <event name="OnKeyDown"></event>
+ <event name="OnKeyUp"></event>
+ <event name="OnKillFocus"></event>
+ <event name="OnLeaveWindow"></event>
+ <event name="OnLeftDClick"></event>
+ <event name="OnLeftDown"></event>
+ <event name="OnLeftUp"></event>
+ <event name="OnMiddleDClick"></event>
+ <event name="OnMiddleDown"></event>
+ <event name="OnMiddleUp"></event>
+ <event name="OnMotion"></event>
+ <event name="OnMouseEvents"></event>
+ <event name="OnMouseWheel"></event>
+ <event name="OnPaint"></event>
+ <event name="OnRightDClick"></event>
+ <event name="OnRightDown"></event>
+ <event name="OnRightUp"></event>
+ <event name="OnSetFocus"></event>
+ <event name="OnSize"></event>
+ <event name="OnUpdateUI"></event>
+ </object>
+ </object>
+ <object class="sizeritem" expanded="1">
+ <property name="border">5</property>
+ <property name="flag">wxALL</property>
+ <property name="proportion">0</property>
+ <object class="wxStaticText" expanded="1">
+ <property name="BottomDockable">1</property>
+ <property name="LeftDockable">1</property>
+ <property name="RightDockable">1</property>
+ <property name="TopDockable">1</property>
+ <property name="aui_layer"></property>
+ <property name="aui_name"></property>
+ <property name="aui_position"></property>
+ <property name="aui_row"></property>
+ <property name="best_size"></property>
+ <property name="bg"></property>
+ <property name="caption"></property>
+ <property name="caption_visible">1</property>
+ <property name="center_pane">0</property>
+ <property name="close_button">1</property>
+ <property name="context_help"></property>
+ <property name="context_menu">1</property>
+ <property name="default_pane">0</property>
+ <property name="dock">Dock</property>
+ <property name="dock_fixed">0</property>
+ <property name="docking">Left</property>
+ <property name="enabled">1</property>
+ <property name="fg"></property>
+ <property name="floatable">1</property>
+ <property name="font"></property>
+ <property name="gripper">0</property>
+ <property name="hidden">0</property>
+ <property name="id">wxID_ANY</property>
+ <property name="label">Marvin Calvert</property>
+ <property name="max_size"></property>
+ <property name="maximize_button">0</property>
+ <property name="maximum_size"></property>
+ <property name="min_size"></property>
+ <property name="minimize_button">0</property>
+ <property name="minimum_size"></property>
+ <property name="moveable">1</property>
+ <property name="name">m_dMarvinCalvert</property>
+ <property name="pane_border">1</property>
+ <property name="pane_position"></property>
+ <property name="pane_size"></property>
+ <property name="permission">protected</property>
+ <property name="pin_button">1</property>
+ <property name="pos"></property>
+ <property name="resize">Resizable</property>
+ <property name="show">1</property>
+ <property name="size"></property>
+ <property name="style"></property>
+ <property name="subclass">; forward_declare</property>
+ <property name="toolbar_pane">0</property>
+ <property name="tooltip"></property>
+ <property name="window_extra_style"></property>
+ <property name="window_name"></property>
+ <property name="window_style"></property>
+ <property name="wrap">-1</property>
+ <event name="OnChar"></event>
+ <event name="OnEnterWindow"></event>
+ <event name="OnEraseBackground"></event>
+ <event name="OnKeyDown"></event>
+ <event name="OnKeyUp"></event>
+ <event name="OnKillFocus"></event>
+ <event name="OnLeaveWindow"></event>
+ <event name="OnLeftDClick"></event>
+ <event name="OnLeftDown"></event>
+ <event name="OnLeftUp"></event>
+ <event name="OnMiddleDClick"></event>
+ <event name="OnMiddleDown"></event>
+ <event name="OnMiddleUp"></event>
+ <event name="OnMotion"></event>
+ <event name="OnMouseEvents"></event>
+ <event name="OnMouseWheel"></event>
+ <event name="OnPaint"></event>
+ <event name="OnRightDClick"></event>
+ <event name="OnRightDown"></event>
+ <event name="OnRightUp"></event>
+ <event name="OnSetFocus"></event>
+ <event name="OnSize"></event>
+ <event name="OnUpdateUI"></event>
+ </object>
+ </object>
</object>
</object>
</object>
diff --git a/src/forms/Dialog/AboutDialogBase.cpp b/src/forms/Dialog/AboutDialogBase.cpp
index 12ac933..fd26f24 100644
--- a/src/forms/Dialog/AboutDialogBase.cpp
+++ b/src/forms/Dialog/AboutDialogBase.cpp
@@ -1,8 +1,8 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Aug 23 2015)
+// C++ code generated with wxFormBuilder (version Nov 6 2017)
// http://www.wxformbuilder.org/
//
-// PLEASE DO "NOT" EDIT THIS FILE!
+// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "AboutDialogBase.h"
@@ -22,7 +22,7 @@ AboutDialogBase::AboutDialogBase( wxWindow* parent, wxWindowID id, const wxStrin
m_appName = new wxStaticText( m_hPanel, wxID_ANY, wxT("CubicSDR"), wxDefaultPosition, wxDefaultSize, 0 );
m_appName->Wrap( -1 );
- m_appName->SetFont( wxFont( 20, 70, 90, 90, false, wxEmptyString ) );
+ m_appName->SetFont( wxFont( 20, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
m_hSizer->Add( m_appName, 0, wxALL, 6 );
@@ -45,19 +45,19 @@ AboutDialogBase::AboutDialogBase( wxWindow* parent, wxWindowID id, const wxStrin
m_dbHeader = new wxStaticText( m_dbScroll, wxID_ANY, wxT("Developed By"), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
m_dbHeader->Wrap( -1 );
- m_dbHeader->SetFont( wxFont( 15, 70, 90, 90, false, wxEmptyString ) );
+ m_dbHeader->SetFont( wxFont( 15, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
m_dbSizer->Add( m_dbHeader, 0, wxALL, 5 );
m_dbGHHeader = new wxStaticText( m_dbScroll, wxID_ANY, wxT("GitHub"), wxDefaultPosition, wxDefaultSize, 0 );
m_dbGHHeader->Wrap( -1 );
- m_dbGHHeader->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) );
+ m_dbGHHeader->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
m_dbSizer->Add( m_dbGHHeader, 0, wxALL, 5 );
m_dbTwitter = new wxStaticText( m_dbScroll, wxID_ANY, wxT("Twitter"), wxDefaultPosition, wxDefaultSize, 0 );
m_dbTwitter->Wrap( -1 );
- m_dbTwitter->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) );
+ m_dbTwitter->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
m_dbSizer->Add( m_dbTwitter, 0, wxALL, 5 );
@@ -98,13 +98,13 @@ AboutDialogBase::AboutDialogBase( wxWindow* parent, wxWindowID id, const wxStrin
m_cContributorsHeader = new wxStaticText( m_dbScroll, wxID_ANY, wxT("Contributors"), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
m_cContributorsHeader->Wrap( -1 );
- m_cContributorsHeader->SetFont( wxFont( 15, 70, 90, 90, false, wxEmptyString ) );
+ m_cContributorsHeader->SetFont( wxFont( 15, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
m_cSizer->Add( m_cContributorsHeader, 0, wxALL, 5 );
m_cGitHub = new wxStaticText( m_dbScroll, wxID_ANY, wxT("GitHub"), wxDefaultPosition, wxDefaultSize, 0 );
m_cGitHub->Wrap( -1 );
- m_cGitHub->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) );
+ m_cGitHub->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
m_cSizer->Add( m_cGitHub, 0, wxALL, 5 );
@@ -190,7 +190,7 @@ AboutDialogBase::AboutDialogBase( wxWindow* parent, wxWindowID id, const wxStrin
m_dHeader = new wxStaticText( m_dScroll, wxID_ANY, wxT("Thanks to everyone who donated at cubicsdr.com!"), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
m_dHeader->Wrap( -1 );
- m_dHeader->SetFont( wxFont( 15, 70, 90, 90, false, wxEmptyString ) );
+ m_dHeader->SetFont( wxFont( 15, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
m_dSizer->Add( m_dHeader, 0, wxALL, 5 );
@@ -365,6 +365,26 @@ AboutDialogBase::AboutDialogBase( wxWindow* parent, wxWindowID id, const wxStrin
m_dPetrikaJaneku->Wrap( -1 );
m_dSizer->Add( m_dPetrikaJaneku, 0, wxALL, 5 );
+ m_dChadMyslinsky = new wxStaticText( m_dScroll, wxID_ANY, wxT("Chad Myslinsky"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_dChadMyslinsky->Wrap( -1 );
+ m_dSizer->Add( m_dChadMyslinsky, 0, wxALL, 5 );
+
+ m_dCharlieBruckner = new wxStaticText( m_dScroll, wxID_ANY, wxT("Charlie Bruckner"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_dCharlieBruckner->Wrap( -1 );
+ m_dSizer->Add( m_dCharlieBruckner, 0, wxALL, 5 );
+
+ m_dJordanParker = new wxStaticText( m_dScroll, wxID_ANY, wxT("Jordan Parker"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_dJordanParker->Wrap( -1 );
+ m_dSizer->Add( m_dJordanParker, 0, wxALL, 5 );
+
+ m_dRobertChave = new wxStaticText( m_dScroll, wxID_ANY, wxT("Robert Chave"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_dRobertChave->Wrap( -1 );
+ m_dSizer->Add( m_dRobertChave, 0, wxALL, 5 );
+
+ m_dMarvinCalvert = new wxStaticText( m_dScroll, wxID_ANY, wxT("Marvin Calvert"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_dMarvinCalvert->Wrap( -1 );
+ m_dSizer->Add( m_dMarvinCalvert, 0, wxALL, 5 );
+
m_dBSizer->Add( m_dSizer, 1, wxALL|wxEXPAND, 5 );
@@ -383,7 +403,7 @@ AboutDialogBase::AboutDialogBase( wxWindow* parent, wxWindowID id, const wxStrin
m_stHeader = new wxStaticText( m_stScroll, wxID_ANY, wxT("Special Thanks To"), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
m_stHeader->Wrap( -1 );
- m_stHeader->SetFont( wxFont( 15, 70, 90, 90, false, wxEmptyString ) );
+ m_stHeader->SetFont( wxFont( 15, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
m_stSizer->Add( m_stHeader, 0, wxALL, 5 );
@@ -392,7 +412,7 @@ AboutDialogBase::AboutDialogBase( wxWindow* parent, wxWindowID id, const wxStrin
m_stSoapyDevAssistHeader = new wxStaticText( m_stScroll, wxID_ANY, wxT("SoapySDR Development and Assistance:"), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
m_stSoapyDevAssistHeader->Wrap( -1 );
- m_stSoapyDevAssistHeader->SetFont( wxFont( 10, 70, 90, 92, false, wxEmptyString ) );
+ m_stSoapyDevAssistHeader->SetFont( wxFont( 10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
m_stSizer->Add( m_stSoapyDevAssistHeader, 0, wxALL, 5 );
@@ -405,7 +425,7 @@ AboutDialogBase::AboutDialogBase( wxWindow* parent, wxWindowID id, const wxStrin
m_stLiquidDSPHeader = new wxStaticText( m_stScroll, wxID_ANY, wxT("Liquid-DSP Development and Assistance:"), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
m_stLiquidDSPHeader->Wrap( -1 );
- m_stLiquidDSPHeader->SetFont( wxFont( 10, 70, 90, 92, false, wxEmptyString ) );
+ m_stLiquidDSPHeader->SetFont( wxFont( 10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
m_stSizer->Add( m_stLiquidDSPHeader, 0, wxALL, 5 );
@@ -418,7 +438,7 @@ AboutDialogBase::AboutDialogBase( wxWindow* parent, wxWindowID id, const wxStrin
m_stIdeasDirectionsHeader = new wxStaticText( m_stScroll, wxID_ANY, wxT("Ideas, Direction && Encouragement:"), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
m_stIdeasDirectionsHeader->Wrap( -1 );
- m_stIdeasDirectionsHeader->SetFont( wxFont( 10, 70, 90, 92, false, wxEmptyString ) );
+ m_stIdeasDirectionsHeader->SetFont( wxFont( 10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
m_stSizer->Add( m_stIdeasDirectionsHeader, 0, wxALL, 5 );
diff --git a/src/forms/Dialog/AboutDialogBase.h b/src/forms/Dialog/AboutDialogBase.h
index 6029b1b..a6b9cbd 100644
--- a/src/forms/Dialog/AboutDialogBase.h
+++ b/src/forms/Dialog/AboutDialogBase.h
@@ -1,8 +1,8 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Aug 23 2015)
+// C++ code generated with wxFormBuilder (version Nov 6 2017)
// http://www.wxformbuilder.org/
//
-// PLEASE DO "NOT" EDIT THIS FILE!
+// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#ifndef __ABOUTDIALOGBASE_H__
@@ -114,6 +114,11 @@ class AboutDialogBase : public wxDialog
wxStaticText* m_dSergeVanderTorre;
wxStaticText* m_dDieterSchneider;
wxStaticText* m_dPetrikaJaneku;
+ wxStaticText* m_dChadMyslinsky;
+ wxStaticText* m_dCharlieBruckner;
+ wxStaticText* m_dJordanParker;
+ wxStaticText* m_dRobertChave;
+ wxStaticText* m_dMarvinCalvert;
wxScrolledWindow* m_stScroll;
wxStaticText* m_stHeader;
wxStaticLine* m_stDivider1;
diff --git a/src/forms/Dialog/ActionDialogBase.cpp b/src/forms/Dialog/ActionDialogBase.cpp
index aaf21d3..eb61f13 100644
--- a/src/forms/Dialog/ActionDialogBase.cpp
+++ b/src/forms/Dialog/ActionDialogBase.cpp
@@ -1,53 +1,53 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Aug 23 2015)
+// C++ code generated with wxFormBuilder (version Oct 27 2017)
// http://www.wxformbuilder.org/
//
-// PLEASE DO "NOT" EDIT THIS FILE!
+// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "ActionDialogBase.h"
///////////////////////////////////////////////////////////////////////////
-ActionDialogBase::ActionDialogBase( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
+ActionDialogBase::ActionDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style)
{
- this->SetSizeHints( wxDefaultSize, wxDefaultSize );
-
+ this->SetSizeHints(wxDefaultSize, wxDefaultSize);
+
wxBoxSizer* mainSizer;
- mainSizer = new wxBoxSizer( wxVERTICAL );
-
- m_questionText = new wxStaticText( this, wxID_ANY, wxT("Question"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE );
- m_questionText->Wrap( -1 );
- mainSizer->Add( m_questionText, 1, wxALL|wxEXPAND, 5 );
-
+ mainSizer = new wxBoxSizer(wxVERTICAL);
+
+ m_questionText = new wxStaticText(this, wxID_ANY, wxT("Question"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE);
+ m_questionText->Wrap(-1);
+ mainSizer->Add(m_questionText, 1, wxALL | wxEXPAND, 5);
+
wxBoxSizer* buttonSizer;
- buttonSizer = new wxBoxSizer( wxHORIZONTAL );
-
- m_cancelButton = new wxButton( this, wxID_ANY, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0 );
- buttonSizer->Add( m_cancelButton, 1, wxALL|wxEXPAND, 5 );
-
- m_okButton = new wxButton( this, wxID_ANY, wxT("OK"), wxDefaultPosition, wxDefaultSize, 0 );
- buttonSizer->Add( m_okButton, 1, wxALL|wxEXPAND, 5 );
-
-
- mainSizer->Add( buttonSizer, 1, wxEXPAND, 5 );
-
-
- this->SetSizer( mainSizer );
+ buttonSizer = new wxBoxSizer(wxHORIZONTAL);
+
+ m_cancelButton = new wxButton(this, wxID_ANY, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0);
+ buttonSizer->Add(m_cancelButton, 1, wxALL | wxEXPAND, 5);
+
+ m_okButton = new wxButton(this, wxID_ANY, wxT("OK"), wxDefaultPosition, wxDefaultSize, 0);
+ buttonSizer->Add(m_okButton, 1, wxALL | wxEXPAND, 5);
+
+
+ mainSizer->Add(buttonSizer, 1, wxEXPAND, 5);
+
+
+ this->SetSizer(mainSizer);
this->Layout();
- mainSizer->Fit( this );
-
- this->Centre( wxBOTH );
-
+ mainSizer->Fit(this);
+
+ this->Centre(wxBOTH);
+
// Connect Events
- m_cancelButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ActionDialogBase::onClickCancel ), NULL, this );
- m_okButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ActionDialogBase::onClickOK ), NULL, this );
+ m_cancelButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ActionDialogBase::onClickCancel), NULL, this);
+ m_okButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ActionDialogBase::onClickOK), NULL, this);
}
ActionDialogBase::~ActionDialogBase()
{
// Disconnect Events
- m_cancelButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ActionDialogBase::onClickCancel ), NULL, this );
- m_okButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ActionDialogBase::onClickOK ), NULL, this );
-
+ m_cancelButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ActionDialogBase::onClickCancel), NULL, this);
+ m_okButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ActionDialogBase::onClickOK), NULL, this);
+
}
diff --git a/src/forms/Dialog/ActionDialogBase.h b/src/forms/Dialog/ActionDialogBase.h
index 72b0b85..b3f3a86 100644
--- a/src/forms/Dialog/ActionDialogBase.h
+++ b/src/forms/Dialog/ActionDialogBase.h
@@ -1,8 +1,8 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Aug 23 2015)
+// C++ code generated with wxFormBuilder (version Oct 27 2017)
// http://www.wxformbuilder.org/
//
-// PLEASE DO "NOT" EDIT THIS FILE!
+// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#ifndef __ACTIONDIALOGBASE_H__
@@ -26,25 +26,25 @@
///////////////////////////////////////////////////////////////////////////////
/// Class ActionDialogBase
///////////////////////////////////////////////////////////////////////////////
-class ActionDialogBase : public wxDialog
+class ActionDialogBase : public wxDialog
{
- private:
-
- protected:
- wxStaticText* m_questionText;
- wxButton* m_cancelButton;
- wxButton* m_okButton;
-
- // Virtual event handlers, overide them in your derived class
- virtual void onClickCancel( wxCommandEvent& event ) { event.Skip(); }
- virtual void onClickOK( wxCommandEvent& event ) { event.Skip(); }
-
-
- public:
-
- ActionDialogBase( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("QuestionTitle"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE );
- ~ActionDialogBase();
-
+private:
+
+protected:
+ wxStaticText* m_questionText;
+ wxButton* m_cancelButton;
+ wxButton* m_okButton;
+
+ // Virtual event handlers, overide them in your derived class
+ virtual void onClickCancel(wxCommandEvent& event) { event.Skip(); }
+ virtual void onClickOK(wxCommandEvent& event) { event.Skip(); }
+
+
+public:
+
+ ActionDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("QuestionTitle"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE);
+ ~ActionDialogBase();
+
};
#endif //__ACTIONDIALOGBASE_H__
diff --git a/src/forms/Dialog/PortSelectorDialogBase.cpp b/src/forms/Dialog/PortSelectorDialogBase.cpp
index a8d5bf2..ea4e561 100644
--- a/src/forms/Dialog/PortSelectorDialogBase.cpp
+++ b/src/forms/Dialog/PortSelectorDialogBase.cpp
@@ -1,78 +1,78 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Aug 23 2015)
+// C++ code generated with wxFormBuilder (version Oct 27 2017)
// http://www.wxformbuilder.org/
//
-// PLEASE DO "NOT" EDIT THIS FILE!
+// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "PortSelectorDialogBase.h"
///////////////////////////////////////////////////////////////////////////
-PortSelectorDialogBase::PortSelectorDialogBase( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
+PortSelectorDialogBase::PortSelectorDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style)
{
- this->SetSizeHints( wxDefaultSize, wxDefaultSize );
-
+ this->SetSizeHints(wxDefaultSize, wxDefaultSize);
+
wxBoxSizer* dlgSizer;
- dlgSizer = new wxBoxSizer( wxVERTICAL );
-
- m_staticText1 = new wxStaticText( this, wxID_ANY, wxT("Select a detected port or enter your own"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText1->Wrap( -1 );
- dlgSizer->Add( m_staticText1, 0, wxEXPAND|wxALL, 5 );
-
- m_portList = new wxListBox( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
- dlgSizer->Add( m_portList, 1, wxALL|wxEXPAND, 5 );
-
+ dlgSizer = new wxBoxSizer(wxVERTICAL);
+
+ m_staticText1 = new wxStaticText(this, wxID_ANY, wxT("Select a detected port or enter your own"), wxDefaultPosition, wxDefaultSize, 0);
+ m_staticText1->Wrap(-1);
+ dlgSizer->Add(m_staticText1, 0, wxEXPAND | wxALL, 5);
+
+ m_portList = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, 0);
+ dlgSizer->Add(m_portList, 1, wxALL | wxEXPAND, 5);
+
wxBoxSizer* bSizer3;
- bSizer3 = new wxBoxSizer( wxHORIZONTAL );
-
- bSizer3->SetMinSize( wxSize( -1,30 ) );
- m_staticText2 = new wxStaticText( this, wxID_ANY, wxT("Port"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText2->Wrap( -1 );
- bSizer3->Add( m_staticText2, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
-
- m_portSelection = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
- bSizer3->Add( m_portSelection, 1, wxEXPAND|wxRIGHT, 5 );
-
-
- dlgSizer->Add( bSizer3, 1, wxEXPAND, 5 );
-
- m_buttonPanel = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ bSizer3 = new wxBoxSizer(wxHORIZONTAL);
+
+ bSizer3->SetMinSize(wxSize(-1, 30));
+ m_staticText2 = new wxStaticText(this, wxID_ANY, wxT("Port"), wxDefaultPosition, wxDefaultSize, 0);
+ m_staticText2->Wrap(-1);
+ bSizer3->Add(m_staticText2, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
+
+ m_portSelection = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
+ bSizer3->Add(m_portSelection, 1, wxEXPAND | wxRIGHT, 5);
+
+
+ dlgSizer->Add(bSizer3, 1, wxEXPAND, 5);
+
+ m_buttonPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
wxBoxSizer* bSizer2;
- bSizer2 = new wxBoxSizer( wxHORIZONTAL );
-
- m_cancelButton = new wxButton( m_buttonPanel, wxID_ANY, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0 );
- bSizer2->Add( m_cancelButton, 0, wxALL|wxALIGN_BOTTOM, 5 );
-
-
- bSizer2->Add( 0, 0, 1, wxEXPAND, 5 );
-
- m_okButton = new wxButton( m_buttonPanel, wxID_ANY, wxT("OK"), wxDefaultPosition, wxDefaultSize, 0 );
- bSizer2->Add( m_okButton, 0, wxALL|wxALIGN_BOTTOM, 5 );
-
-
- m_buttonPanel->SetSizer( bSizer2 );
+ bSizer2 = new wxBoxSizer(wxHORIZONTAL);
+
+ m_cancelButton = new wxButton(m_buttonPanel, wxID_ANY, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0);
+ bSizer2->Add(m_cancelButton, 0, wxALL | wxALIGN_BOTTOM, 5);
+
+
+ bSizer2->Add(0, 0, 1, wxEXPAND, 5);
+
+ m_okButton = new wxButton(m_buttonPanel, wxID_ANY, wxT("OK"), wxDefaultPosition, wxDefaultSize, 0);
+ bSizer2->Add(m_okButton, 0, wxALL | wxALIGN_BOTTOM, 5);
+
+
+ m_buttonPanel->SetSizer(bSizer2);
m_buttonPanel->Layout();
- bSizer2->Fit( m_buttonPanel );
- dlgSizer->Add( m_buttonPanel, 0, wxEXPAND | wxALL, 5 );
-
-
- this->SetSizer( dlgSizer );
+ bSizer2->Fit(m_buttonPanel);
+ dlgSizer->Add(m_buttonPanel, 0, wxEXPAND | wxALL, 5);
+
+
+ this->SetSizer(dlgSizer);
this->Layout();
-
- this->Centre( wxBOTH );
-
+
+ this->Centre(wxBOTH);
+
// Connect Events
- m_portList->Connect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( PortSelectorDialogBase::onListSelect ), NULL, this );
- m_cancelButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PortSelectorDialogBase::onCancelButton ), NULL, this );
- m_okButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PortSelectorDialogBase::onOKButton ), NULL, this );
+ m_portList->Connect(wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler(PortSelectorDialogBase::onListSelect), NULL, this);
+ m_cancelButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PortSelectorDialogBase::onCancelButton), NULL, this);
+ m_okButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PortSelectorDialogBase::onOKButton), NULL, this);
}
PortSelectorDialogBase::~PortSelectorDialogBase()
{
// Disconnect Events
- m_portList->Disconnect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( PortSelectorDialogBase::onListSelect ), NULL, this );
- m_cancelButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PortSelectorDialogBase::onCancelButton ), NULL, this );
- m_okButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PortSelectorDialogBase::onOKButton ), NULL, this );
-
+ m_portList->Disconnect(wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler(PortSelectorDialogBase::onListSelect), NULL, this);
+ m_cancelButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PortSelectorDialogBase::onCancelButton), NULL, this);
+ m_okButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(PortSelectorDialogBase::onOKButton), NULL, this);
+
}
diff --git a/src/forms/Dialog/PortSelectorDialogBase.h b/src/forms/Dialog/PortSelectorDialogBase.h
index 8e8c2af..50db428 100644
--- a/src/forms/Dialog/PortSelectorDialogBase.h
+++ b/src/forms/Dialog/PortSelectorDialogBase.h
@@ -1,8 +1,8 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Aug 23 2015)
+// C++ code generated with wxFormBuilder (version Oct 27 2017)
// http://www.wxformbuilder.org/
//
-// PLEASE DO "NOT" EDIT THIS FILE!
+// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#ifndef __PORTSELECTORDIALOGBASE_H__
@@ -29,30 +29,30 @@
///////////////////////////////////////////////////////////////////////////////
/// Class PortSelectorDialogBase
///////////////////////////////////////////////////////////////////////////////
-class PortSelectorDialogBase : public wxDialog
+class PortSelectorDialogBase : public wxDialog
{
- private:
-
- protected:
- wxStaticText* m_staticText1;
- wxListBox* m_portList;
- wxStaticText* m_staticText2;
- wxTextCtrl* m_portSelection;
- wxPanel* m_buttonPanel;
- wxButton* m_cancelButton;
- wxButton* m_okButton;
-
- // Virtual event handlers, overide them in your derived class
- virtual void onListSelect( wxCommandEvent& event ) { event.Skip(); }
- virtual void onCancelButton( wxCommandEvent& event ) { event.Skip(); }
- virtual void onOKButton( wxCommandEvent& event ) { event.Skip(); }
-
-
- public:
-
- PortSelectorDialogBase( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Select Port"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 304,197 ), long style = wxDEFAULT_DIALOG_STYLE );
- ~PortSelectorDialogBase();
-
+private:
+
+protected:
+ wxStaticText* m_staticText1;
+ wxListBox* m_portList;
+ wxStaticText* m_staticText2;
+ wxTextCtrl* m_portSelection;
+ wxPanel* m_buttonPanel;
+ wxButton* m_cancelButton;
+ wxButton* m_okButton;
+
+ // Virtual event handlers, overide them in your derived class
+ virtual void onListSelect(wxCommandEvent& event) { event.Skip(); }
+ virtual void onCancelButton(wxCommandEvent& event) { event.Skip(); }
+ virtual void onOKButton(wxCommandEvent& event) { event.Skip(); }
+
+
+public:
+
+ PortSelectorDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Select Port"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(304, 197), long style = wxDEFAULT_DIALOG_STYLE);
+ ~PortSelectorDialogBase();
+
};
#endif //__PORTSELECTORDIALOGBASE_H__
diff --git a/src/forms/DigitalConsole/DigitalConsoleFrame.cpp b/src/forms/DigitalConsole/DigitalConsoleFrame.cpp
index b1e4885..16324db 100644
--- a/src/forms/DigitalConsole/DigitalConsoleFrame.cpp
+++ b/src/forms/DigitalConsole/DigitalConsoleFrame.cpp
@@ -1,73 +1,73 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Aug 23 2015)
+// C++ code generated with wxFormBuilder (version Oct 27 2017)
// http://www.wxformbuilder.org/
//
-// PLEASE DO "NOT" EDIT THIS FILE!
+// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "DigitalConsoleFrame.h"
///////////////////////////////////////////////////////////////////////////
-DigitalConsoleFrame::DigitalConsoleFrame( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style )
+DigitalConsoleFrame::DigitalConsoleFrame(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style)
{
- this->SetSizeHints( wxDefaultSize, wxDefaultSize );
- this->SetExtraStyle( wxWS_EX_PROCESS_UI_UPDATES );
-
+ this->SetSizeHints(wxDefaultSize, wxDefaultSize);
+ this->SetExtraStyle(wxWS_EX_PROCESS_UI_UPDATES);
+
wxBoxSizer* mainSizer;
- mainSizer = new wxBoxSizer( wxVERTICAL );
-
+ mainSizer = new wxBoxSizer(wxVERTICAL);
+
wxBoxSizer* dataViewSizer;
- dataViewSizer = new wxBoxSizer( wxVERTICAL );
-
- m_dataView = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_CHARWRAP|wxTE_MULTILINE|wxTE_NOHIDESEL|wxTE_READONLY|wxTE_WORDWRAP|wxALWAYS_SHOW_SB|wxFULL_REPAINT_ON_RESIZE|wxNO_BORDER|wxSIMPLE_BORDER|wxVSCROLL );
- m_dataView->SetExtraStyle( wxWS_EX_PROCESS_UI_UPDATES );
- m_dataView->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 76, 90, 90, false, wxEmptyString ) );
-
- dataViewSizer->Add( m_dataView, 1, wxEXPAND, 5 );
-
-
- mainSizer->Add( dataViewSizer, 1, wxEXPAND, 5 );
-
+ dataViewSizer = new wxBoxSizer(wxVERTICAL);
+
+ m_dataView = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_CHARWRAP | wxTE_MULTILINE | wxTE_NOHIDESEL | wxTE_READONLY | wxTE_WORDWRAP | wxALWAYS_SHOW_SB | wxFULL_REPAINT_ON_RESIZE | wxNO_BORDER | wxSIMPLE_BORDER | wxVSCROLL);
+ m_dataView->SetExtraStyle(wxWS_EX_PROCESS_UI_UPDATES);
+ m_dataView->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString));
+
+ dataViewSizer->Add(m_dataView, 1, wxEXPAND, 5);
+
+
+ mainSizer->Add(dataViewSizer, 1, wxEXPAND, 5);
+
wxBoxSizer* buttonSizer;
- buttonSizer = new wxBoxSizer( wxHORIZONTAL );
-
- m_clearButton = new wxButton( this, wxID_ANY, wxT("Clear"), wxDefaultPosition, wxDefaultSize, 0 );
- buttonSizer->Add( m_clearButton, 1, wxEXPAND, 5 );
-
- m_copyButton = new wxButton( this, wxID_ANY, wxT("Copy"), wxDefaultPosition, wxDefaultSize, 0 );
- buttonSizer->Add( m_copyButton, 1, wxEXPAND, 5 );
-
- m_pauseButton = new wxButton( this, wxID_ANY, wxT("Stop"), wxDefaultPosition, wxDefaultSize, 0 );
- buttonSizer->Add( m_pauseButton, 1, wxEXPAND, 5 );
-
-
- mainSizer->Add( buttonSizer, 0, wxALL|wxEXPAND, 5 );
-
-
- this->SetSizer( mainSizer );
+ buttonSizer = new wxBoxSizer(wxHORIZONTAL);
+
+ m_clearButton = new wxButton(this, wxID_ANY, wxT("Clear"), wxDefaultPosition, wxDefaultSize, 0);
+ buttonSizer->Add(m_clearButton, 1, wxEXPAND, 5);
+
+ m_copyButton = new wxButton(this, wxID_ANY, wxT("Copy"), wxDefaultPosition, wxDefaultSize, 0);
+ buttonSizer->Add(m_copyButton, 1, wxEXPAND, 5);
+
+ m_pauseButton = new wxButton(this, wxID_ANY, wxT("Stop"), wxDefaultPosition, wxDefaultSize, 0);
+ buttonSizer->Add(m_pauseButton, 1, wxEXPAND, 5);
+
+
+ mainSizer->Add(buttonSizer, 0, wxALL | wxEXPAND, 5);
+
+
+ this->SetSizer(mainSizer);
this->Layout();
- m_refreshTimer.SetOwner( this, wxID_ANY );
- m_refreshTimer.Start( 250 );
-
-
- this->Centre( wxBOTH );
-
+ m_refreshTimer.SetOwner(this, wxID_ANY);
+ m_refreshTimer.Start(250);
+
+
+ this->Centre(wxBOTH);
+
// Connect Events
- this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DigitalConsoleFrame::OnClose ) );
- m_clearButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DigitalConsoleFrame::OnClear ), NULL, this );
- m_copyButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DigitalConsoleFrame::OnCopy ), NULL, this );
- m_pauseButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DigitalConsoleFrame::OnPause ), NULL, this );
- this->Connect( wxID_ANY, wxEVT_TIMER, wxTimerEventHandler( DigitalConsoleFrame::DoRefresh ) );
+ this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(DigitalConsoleFrame::OnClose));
+ m_clearButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DigitalConsoleFrame::OnClear), NULL, this);
+ m_copyButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DigitalConsoleFrame::OnCopy), NULL, this);
+ m_pauseButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DigitalConsoleFrame::OnPause), NULL, this);
+ this->Connect(wxID_ANY, wxEVT_TIMER, wxTimerEventHandler(DigitalConsoleFrame::DoRefresh));
}
DigitalConsoleFrame::~DigitalConsoleFrame()
{
// Disconnect Events
- this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DigitalConsoleFrame::OnClose ) );
- m_clearButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DigitalConsoleFrame::OnClear ), NULL, this );
- m_copyButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DigitalConsoleFrame::OnCopy ), NULL, this );
- m_pauseButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DigitalConsoleFrame::OnPause ), NULL, this );
- this->Disconnect( wxID_ANY, wxEVT_TIMER, wxTimerEventHandler( DigitalConsoleFrame::DoRefresh ) );
-
+ this->Disconnect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(DigitalConsoleFrame::OnClose));
+ m_clearButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DigitalConsoleFrame::OnClear), NULL, this);
+ m_copyButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DigitalConsoleFrame::OnCopy), NULL, this);
+ m_pauseButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DigitalConsoleFrame::OnPause), NULL, this);
+ this->Disconnect(wxID_ANY, wxEVT_TIMER, wxTimerEventHandler(DigitalConsoleFrame::DoRefresh));
+
}
diff --git a/src/forms/DigitalConsole/DigitalConsoleFrame.h b/src/forms/DigitalConsole/DigitalConsoleFrame.h
index 6665b66..76d7b9e 100644
--- a/src/forms/DigitalConsole/DigitalConsoleFrame.h
+++ b/src/forms/DigitalConsole/DigitalConsoleFrame.h
@@ -1,8 +1,8 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Aug 23 2015)
+// C++ code generated with wxFormBuilder (version Oct 27 2017)
// http://www.wxformbuilder.org/
//
-// PLEASE DO "NOT" EDIT THIS FILE!
+// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#ifndef __DIGITALCONSOLEFRAME_H__
@@ -27,31 +27,31 @@
///////////////////////////////////////////////////////////////////////////////
/// Class DigitalConsoleFrame
///////////////////////////////////////////////////////////////////////////////
-class DigitalConsoleFrame : public wxFrame
+class DigitalConsoleFrame : public wxFrame
{
- private:
-
- protected:
- wxTextCtrl* m_dataView;
- wxButton* m_clearButton;
- wxButton* m_copyButton;
- wxButton* m_pauseButton;
- wxTimer m_refreshTimer;
-
- // Virtual event handlers, overide them in your derived class
- virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
- virtual void OnClear( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnCopy( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnPause( wxCommandEvent& event ) { event.Skip(); }
- virtual void DoRefresh( wxTimerEvent& event ) { event.Skip(); }
-
-
- public:
-
- DigitalConsoleFrame( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Digital Output"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 441,394 ), long style = wxCAPTION|wxFRAME_FLOAT_ON_PARENT|wxMAXIMIZE|wxMAXIMIZE_BOX|wxMINIMIZE|wxMINIMIZE_BOX|wxRESIZE_BORDER|wxFULL_REPAINT_ON_RESIZE|wxTAB_TRAVERSAL );
-
- ~DigitalConsoleFrame();
-
+private:
+
+protected:
+ wxTextCtrl* m_dataView;
+ wxButton* m_clearButton;
+ wxButton* m_copyButton;
+ wxButton* m_pauseButton;
+ wxTimer m_refreshTimer;
+
+ // Virtual event handlers, overide them in your derived class
+ virtual void OnClose(wxCloseEvent& event) { event.Skip(); }
+ virtual void OnClear(wxCommandEvent& event) { event.Skip(); }
+ virtual void OnCopy(wxCommandEvent& event) { event.Skip(); }
+ virtual void OnPause(wxCommandEvent& event) { event.Skip(); }
+ virtual void DoRefresh(wxTimerEvent& event) { event.Skip(); }
+
+
+public:
+
+ DigitalConsoleFrame(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Digital Output"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(441, 394), long style = wxCAPTION | wxFRAME_FLOAT_ON_PARENT | wxMAXIMIZE | wxMAXIMIZE_BOX | wxMINIMIZE | wxMINIMIZE_BOX | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE | wxTAB_TRAVERSAL);
+
+ ~DigitalConsoleFrame();
+
};
#endif //__DIGITALCONSOLEFRAME_H__
diff --git a/src/forms/SDRDevices/SDRDeviceAddForm.cpp b/src/forms/SDRDevices/SDRDeviceAddForm.cpp
index 09afe15..d305a10 100644
--- a/src/forms/SDRDevices/SDRDeviceAddForm.cpp
+++ b/src/forms/SDRDevices/SDRDeviceAddForm.cpp
@@ -1,83 +1,83 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Aug 23 2015)
+// C++ code generated with wxFormBuilder (version Oct 27 2017)
// http://www.wxformbuilder.org/
//
-// PLEASE DO "NOT" EDIT THIS FILE!
+// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "SDRDeviceAddForm.h"
///////////////////////////////////////////////////////////////////////////
-SDRDeviceAddForm::SDRDeviceAddForm( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
+SDRDeviceAddForm::SDRDeviceAddForm(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style)
{
- this->SetSizeHints( wxDefaultSize, wxDefaultSize );
-
+ this->SetSizeHints(wxDefaultSize, wxDefaultSize);
+
wxBoxSizer* bSizer6;
- bSizer6 = new wxBoxSizer( wxVERTICAL );
-
- m_staticText4 = new wxStaticText( this, wxID_ANY, wxT("Manually add a SoapyRemote or SoapySDR device. \n\nUseful for a device that is not detected automatically."), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText4->Wrap( -1 );
- bSizer6->Add( m_staticText4, 0, wxALL, 8 );
-
-
- bSizer6->Add( 0, 0, 1, wxEXPAND, 5 );
-
+ bSizer6 = new wxBoxSizer(wxVERTICAL);
+
+ m_staticText4 = new wxStaticText(this, wxID_ANY, wxT("Manually add a SoapyRemote or SoapySDR device. \n\nUseful for a device that is not detected automatically."), wxDefaultPosition, wxDefaultSize, 0);
+ m_staticText4->Wrap(-1);
+ bSizer6->Add(m_staticText4, 0, wxALL, 8);
+
+
+ bSizer6->Add(0, 0, 1, wxEXPAND, 5);
+
wxArrayString m_soapyModuleChoices;
- m_soapyModule = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_soapyModuleChoices, 0 );
- m_soapyModule->SetSelection( 0 );
- bSizer6->Add( m_soapyModule, 0, wxALL, 8 );
-
-
- bSizer6->Add( 0, 0, 1, wxEXPAND, 5 );
-
- m_paramLabel = new wxStaticText( this, wxID_ANY, wxT("<Parameter>"), wxDefaultPosition, wxDefaultSize, 0 );
- m_paramLabel->Wrap( -1 );
- bSizer6->Add( m_paramLabel, 0, wxALL, 8 );
-
- m_paramText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_DONTWRAP|wxHSCROLL );
- m_paramText->SetMinSize( wxSize( -1,48 ) );
-
- bSizer6->Add( m_paramText, 1, wxALL|wxEXPAND, 8 );
-
-
- bSizer6->Add( 0, 0, 1, wxEXPAND, 5 );
-
+ m_soapyModule = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_soapyModuleChoices, 0);
+ m_soapyModule->SetSelection(0);
+ bSizer6->Add(m_soapyModule, 0, wxALL, 8);
+
+
+ bSizer6->Add(0, 0, 1, wxEXPAND, 5);
+
+ m_paramLabel = new wxStaticText(this, wxID_ANY, wxT("<Parameter>"), wxDefaultPosition, wxDefaultSize, 0);
+ m_paramLabel->Wrap(-1);
+ bSizer6->Add(m_paramLabel, 0, wxALL, 8);
+
+ m_paramText = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_DONTWRAP | wxHSCROLL);
+ m_paramText->SetMinSize(wxSize(-1, 48));
+
+ bSizer6->Add(m_paramText, 1, wxALL | wxEXPAND, 8);
+
+
+ bSizer6->Add(0, 0, 1, wxEXPAND, 5);
+
wxBoxSizer* bSizer7;
- bSizer7 = new wxBoxSizer( wxHORIZONTAL );
-
-
- bSizer7->Add( 0, 0, 1, wxEXPAND, 5 );
-
- m_cancelButton = new wxButton( this, wxID_ANY, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0 );
- bSizer7->Add( m_cancelButton, 0, wxALL, 2 );
-
- m_OkButton = new wxButton( this, wxID_ANY, wxT("Ok"), wxDefaultPosition, wxDefaultSize, 0 );
- bSizer7->Add( m_OkButton, 0, wxALL, 2 );
-
-
- bSizer6->Add( bSizer7, 1, wxEXPAND, 8 );
-
-
- bSizer6->Add( 0, 0, 1, wxEXPAND, 5 );
-
-
- this->SetSizer( bSizer6 );
+ bSizer7 = new wxBoxSizer(wxHORIZONTAL);
+
+
+ bSizer7->Add(0, 0, 1, wxEXPAND, 5);
+
+ m_cancelButton = new wxButton(this, wxID_ANY, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0);
+ bSizer7->Add(m_cancelButton, 0, wxALL, 2);
+
+ m_OkButton = new wxButton(this, wxID_ANY, wxT("Ok"), wxDefaultPosition, wxDefaultSize, 0);
+ bSizer7->Add(m_OkButton, 0, wxALL, 2);
+
+
+ bSizer6->Add(bSizer7, 1, wxEXPAND, 8);
+
+
+ bSizer6->Add(0, 0, 1, wxEXPAND, 5);
+
+
+ this->SetSizer(bSizer6);
this->Layout();
-
- this->Centre( wxBOTH );
-
+
+ this->Centre(wxBOTH);
+
// Connect Events
- m_soapyModule->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( SDRDeviceAddForm::OnSoapyModuleChanged ), NULL, this );
- m_cancelButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SDRDeviceAddForm::OnCancelButton ), NULL, this );
- m_OkButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SDRDeviceAddForm::OnOkButton ), NULL, this );
+ m_soapyModule->Connect(wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler(SDRDeviceAddForm::OnSoapyModuleChanged), NULL, this);
+ m_cancelButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SDRDeviceAddForm::OnCancelButton), NULL, this);
+ m_OkButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SDRDeviceAddForm::OnOkButton), NULL, this);
}
SDRDeviceAddForm::~SDRDeviceAddForm()
{
// Disconnect Events
- m_soapyModule->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( SDRDeviceAddForm::OnSoapyModuleChanged ), NULL, this );
- m_cancelButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SDRDeviceAddForm::OnCancelButton ), NULL, this );
- m_OkButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SDRDeviceAddForm::OnOkButton ), NULL, this );
-
+ m_soapyModule->Disconnect(wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler(SDRDeviceAddForm::OnSoapyModuleChanged), NULL, this);
+ m_cancelButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SDRDeviceAddForm::OnCancelButton), NULL, this);
+ m_OkButton->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SDRDeviceAddForm::OnOkButton), NULL, this);
+
}
diff --git a/src/forms/SDRDevices/SDRDeviceAddForm.h b/src/forms/SDRDevices/SDRDeviceAddForm.h
index 58ffa20..3f60877 100644
--- a/src/forms/SDRDevices/SDRDeviceAddForm.h
+++ b/src/forms/SDRDevices/SDRDeviceAddForm.h
@@ -1,8 +1,8 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Aug 23 2015)
+// C++ code generated with wxFormBuilder (version Oct 27 2017)
// http://www.wxformbuilder.org/
//
-// PLEASE DO "NOT" EDIT THIS FILE!
+// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#ifndef __SDRDEVICEADDFORM_H__
@@ -28,29 +28,29 @@
///////////////////////////////////////////////////////////////////////////////
/// Class SDRDeviceAddForm
///////////////////////////////////////////////////////////////////////////////
-class SDRDeviceAddForm : public wxDialog
+class SDRDeviceAddForm : public wxDialog
{
- private:
-
- protected:
- wxStaticText* m_staticText4;
- wxChoice* m_soapyModule;
- wxStaticText* m_paramLabel;
- wxTextCtrl* m_paramText;
- wxButton* m_cancelButton;
- wxButton* m_OkButton;
-
- // Virtual event handlers, overide them in your derived class
- virtual void OnSoapyModuleChanged( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnCancelButton( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnOkButton( wxCommandEvent& event ) { event.Skip(); }
-
-
- public:
-
- SDRDeviceAddForm( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Add SoapySDR Device"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 395,293 ), long style = wxDEFAULT_DIALOG_STYLE );
- ~SDRDeviceAddForm();
-
+private:
+
+protected:
+ wxStaticText* m_staticText4;
+ wxChoice* m_soapyModule;
+ wxStaticText* m_paramLabel;
+ wxTextCtrl* m_paramText;
+ wxButton* m_cancelButton;
+ wxButton* m_OkButton;
+
+ // Virtual event handlers, overide them in your derived class
+ virtual void OnSoapyModuleChanged(wxCommandEvent& event) { event.Skip(); }
+ virtual void OnCancelButton(wxCommandEvent& event) { event.Skip(); }
+ virtual void OnOkButton(wxCommandEvent& event) { event.Skip(); }
+
+
+public:
+
+ SDRDeviceAddForm(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Add SoapySDR Device"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(395, 293), long style = wxDEFAULT_DIALOG_STYLE);
+ ~SDRDeviceAddForm();
+
};
#endif //__SDRDEVICEADDFORM_H__
diff --git a/src/forms/SDRDevices/SDRDevices.cpp b/src/forms/SDRDevices/SDRDevices.cpp
index e0bd51e..b5ae9e5 100644
--- a/src/forms/SDRDevices/SDRDevices.cpp
+++ b/src/forms/SDRDevices/SDRDevices.cpp
@@ -8,6 +8,8 @@
#include "CubicSDR.h"
+#include <algorithm>
+
#ifdef __linux__
#include "CubicSDR.xpm"
#endif
@@ -110,6 +112,7 @@ wxPGProperty *SDRDevicesDialog::addArgInfoProperty(wxPropertyGrid *pg, SoapySDR:
}
void SDRDevicesDialog::refreshDeviceProperties() {
+
SDRDeviceInfo *selDev = getSelectedDevice(devTree->GetSelection());
if (selDev && selDev->isAvailable()) {
dev = selDev;
@@ -119,14 +122,62 @@ void SDRDevicesDialog::refreshDeviceProperties() {
SoapySDR::Device *soapyDev = dev->getSoapyDevice();
SoapySDR::ArgInfoList args = soapyDev->getSettingInfo();
- SoapySDR::ArgInfoList::const_iterator args_i;
+ //A) General settings: name, offset, sample rate, agc, antennas (if > 1)
m_propertyGrid->Append(new wxPropertyCategory("General Settings"));
- devSettings.erase(devSettings.begin(),devSettings.end());
+ devSettings.clear();
+
+ //A-1) Name
devSettings["name"] = m_propertyGrid->Append( new wxStringProperty("Name", wxPG_LABEL, devConfig->getDeviceName()) );
- devSettings["offset"] = m_propertyGrid->Append( new wxIntProperty("Offset (Hz)", wxPG_LABEL, devConfig->getOffset()) );
+ //A-2) Offset
+ devSettings["offset"] = m_propertyGrid->Append( new wxIntProperty("Offset (KHz)", wxPG_LABEL, devConfig->getOffset() / 1000) );
+ //A-3) Antennas, is there are more than 1 RX antenna, else do not expose the setting.
+ //get the saved setting
+ const std::string& currentSetAntenna = devConfig->getAntennaName();
+
+ //compare to the list of existing antennas
+ SoapySDR::ArgInfo antennasArg;
+ std::vector<std::string> antennaOpts = selDev->getAntennaNames(SOAPY_SDR_RX, 0);
+
+ //only do something if there is more than 1 antenna
+ if (antennaOpts.size() > 1) {
+
+ //by default, choose the first of the list.
+ std::string antennaToSelect = antennaOpts.front();
+
+ auto found_i = std::find(antennaOpts.begin(), antennaOpts.end(), currentSetAntenna);
+
+ if (found_i != antennaOpts.end()) {
+ antennaToSelect = currentSetAntenna;
+ }
+ else {
+ //erroneous antenna name, re-write device config with the first choice of teh list.
+ devConfig->setAntennaName(antennaToSelect);
+ }
+
+ //build device settings
+ for (std::string antenna : antennaOpts) {
+ antennasArg.options.push_back(antenna);
+ antennasArg.optionNames.push_back(antenna);
+ }
+
+ antennasArg.type = SoapySDR::ArgInfo::STRING;
+ antennasArg.units = "";
+ antennasArg.name = "Antenna";
+ antennasArg.key = "antenna";
+ antennasArg.value = antennaToSelect;
+
+ devSettings["antenna"] = addArgInfoProperty(m_propertyGrid, antennasArg);
+ deviceArgs["antenna"] = antennasArg;
+
+ } //end if more than 1 antenna
+ else {
+ devConfig->setAntennaName("");
+ }
+
+ //A-4) Sample_rate:
long currentSampleRate = wxGetApp().getSampleRate();
long deviceSampleRate = devConfig->getSampleRate();
@@ -137,9 +188,9 @@ void SDRDevicesDialog::refreshDeviceProperties() {
SoapySDR::ArgInfo sampleRateArg;
std::vector<long> rateOpts = selDev->getSampleRates(SOAPY_SDR_RX, 0);
- for (std::vector<long>::iterator rate_i = rateOpts.begin(); rate_i != rateOpts.end(); rate_i++) {
- sampleRateArg.options.push_back(std::to_string(*rate_i));
- sampleRateArg.optionNames.push_back(frequencyToStr(*rate_i));
+ for (long rate : rateOpts) {
+ sampleRateArg.options.push_back(std::to_string(rate));
+ sampleRateArg.optionNames.push_back(frequencyToStr(rate));
}
sampleRateArg.type = SoapySDR::ArgInfo::STRING;
@@ -150,17 +201,30 @@ void SDRDevicesDialog::refreshDeviceProperties() {
devSettings["sample_rate"] = addArgInfoProperty(m_propertyGrid, sampleRateArg);
deviceArgs["sample_rate"] = sampleRateArg;
+
- runtimeArgs.erase(runtimeArgs.begin(), runtimeArgs.end());
- runtimeProps.erase(runtimeProps.begin(), runtimeProps.end());
- streamProps.erase(streamProps.begin(), streamProps.end());
-
+
+ //B) Runtime Settings:
+ runtimeArgs.clear();
+ runtimeProps.clear();
+ streamProps.clear();
+
if (args.size()) {
m_propertyGrid->Append(new wxPropertyCategory("Run-time Settings"));
- for (args_i = args.begin(); args_i != args.end(); args_i++) {
+ for (SoapySDR::ArgInfoList::const_iterator args_i = args.begin(); args_i != args.end(); args_i++) {
SoapySDR::ArgInfo arg = (*args_i);
- arg.value = soapyDev->readSetting(arg.key);
+ //We-reread the Device configuration, else we use the user settings.
+ if (dev) {
+ //Apply saved settings
+ DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId());
+ arg.value = devConfig->getSetting(arg.key, soapyDev->readSetting(arg.key)); //use SoapyDevice data as fallback.
+ }
+ else {
+ //re-read the SoapyDevice
+ arg.value = soapyDev->readSetting(arg.key);
+ }
+
runtimeProps[arg.key] = addArgInfoProperty(m_propertyGrid, arg);
runtimeArgs[arg.key] = arg;
}
@@ -182,8 +246,8 @@ void SDRDevicesDialog::refreshDeviceProperties() {
if (args.size()) {
m_propertyGrid->Append(new wxPropertyCategory("Stream Settings"));
- for (args_i = args.begin(); args_i != args.end(); args_i++) {
- SoapySDR::ArgInfo arg = (*args_i);
+ for (SoapySDR::ArgInfo arg : args) {
+
streamProps[arg.key] = addArgInfoProperty(m_propertyGrid, arg);
}
}
@@ -199,10 +263,12 @@ void SDRDevicesDialog::refreshDeviceProperties() {
} else if (selDev && !selDev->isAvailable() && selDev->isManual()) {
m_propertyGrid->Clear();
- devSettings.erase(devSettings.begin(),devSettings.end());
- runtimeArgs.erase(runtimeArgs.begin(), runtimeArgs.end());
- runtimeProps.erase(runtimeProps.begin(), runtimeProps.end());
- streamProps.erase(streamProps.begin(), streamProps.end());
+
+ devSettings.clear();
+ runtimeArgs.clear();
+ runtimeProps.clear();
+ streamProps.clear();
+
removeId = devTree->GetSelection();
dev = nullptr;
selId = nullptr;
@@ -227,10 +293,10 @@ void SDRDevicesDialog::OnAddRemote( wxMouseEvent& /* event */) {
if (selDev) {
SDREnumerator::removeManual(selDev->getDriver(),selDev->getManualParams());
m_propertyGrid->Clear();
- devSettings.erase(devSettings.begin(),devSettings.end());
- runtimeArgs.erase(runtimeArgs.begin(), runtimeArgs.end());
- runtimeProps.erase(runtimeProps.begin(), runtimeProps.end());
- streamProps.erase(streamProps.begin(), streamProps.end());
+ devSettings.clear();
+ runtimeArgs.clear();
+ runtimeProps.clear();
+ streamProps.clear();
dev = nullptr;
selId = nullptr;
editId = nullptr;
@@ -284,14 +350,14 @@ SDRDeviceInfo *SDRDevicesDialog::getSelectedDevice(wxTreeItemId selId) {
void SDRDevicesDialog::OnUseSelected( wxMouseEvent& event) {
if (dev != NULL) {
- SoapySDR::ArgInfoList::const_iterator args_i;
+
SoapySDR::ArgInfoList args = dev->getSoapyDevice()->getSettingInfo();
SoapySDR::Kwargs settingArgs;
SoapySDR::Kwargs streamArgs;
- for (args_i = args.begin(); args_i != args.end(); args_i++) {
- SoapySDR::ArgInfo arg = (*args_i);
+ for (SoapySDR::ArgInfo arg : args) {
+
wxPGProperty *prop = runtimeProps[arg.key];
if (arg.type == SoapySDR::ArgInfo::STRING && arg.options.size()) {
@@ -307,7 +373,7 @@ void SDRDevicesDialog::OnUseSelected( wxMouseEvent& event) {
args = dev->getSoapyDevice()->getStreamArgsInfo(SOAPY_SDR_RX, 0);
if (args.size()) {
- for (args_i = args.begin(); args_i != args.end(); args_i++) {
+ for (SoapySDR::ArgInfoList::const_iterator args_i = args.begin(); args_i != args.end(); args_i++) {
SoapySDR::ArgInfo arg = (*args_i);
wxPGProperty *prop = streamProps[arg.key];
@@ -329,7 +395,6 @@ void SDRDevicesDialog::OnUseSelected( wxMouseEvent& event) {
wxGetApp().setDeviceArgs(settingArgs);
wxGetApp().setStreamArgs(streamArgs);
wxGetApp().setDevice(dev,0);
- wxGetApp().notifyMainUIOfDeviceChange();
Close();
}
event.Skip();
@@ -392,18 +457,18 @@ void SDRDevicesDialog::OnDeviceTimer( wxTimerEvent& event ) {
}
std::vector<std::string> remotes = SDREnumerator::getRemotes();
- std::vector<std::string>::iterator remotes_i;
+
std::vector<SDRDeviceInfo *>::iterator remoteDevs_i;
if (remotes.size()) {
- for (remotes_i = remotes.begin(); remotes_i != remotes.end(); remotes_i++) {
- devs[*remotes_i] = SDREnumerator::enumerate_devices(*remotes_i, true);
- DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(*remotes_i);
+ for (std::string remote : remotes) {
+ devs[remote] = SDREnumerator::enumerate_devices(remote, true);
+ DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(remote);
wxTreeItemId remoteNode = devTree->AppendItem(remoteBranch, devConfig->getDeviceName());
- if (devs[*remotes_i] != NULL) {
- for (remoteDevs_i = devs[*remotes_i]->begin(); remoteDevs_i != devs[*remotes_i]->end(); remoteDevs_i++) {
+ if (devs[remote] != NULL) {
+ for (remoteDevs_i = devs[remote]->begin(); remoteDevs_i != devs[remote]->end(); remoteDevs_i++) {
devItems[devTree->AppendItem(remoteNode, (*remoteDevs_i)->getName())] = (*remoteDevs_i);
}
}
@@ -427,7 +492,8 @@ void SDRDevicesDialog::OnRefreshDevices( wxMouseEvent& /* event */) {
}
void SDRDevicesDialog::OnPropGridChanged( wxPropertyGridEvent& event ) {
- if (editId && event.GetProperty() == devSettings["name"]) {
+
+ if (event.GetProperty() == devSettings["name"]) {
DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId());
wxString devName = event.GetPropertyValue().GetString();
@@ -445,7 +511,14 @@ void SDRDevicesDialog::OnPropGridChanged( wxPropertyGridEvent& event ) {
long offset = event.GetPropertyValue().GetInteger();
devConfig->setOffset(offset);
- } else if (dev && event.GetProperty() == devSettings["sample_rate"]) {
+ if (dev->isActive() || !wxGetApp().getDevice()) {
+
+ wxGetApp().setOffset(offset);
+ }
+
+ }
+ else if (dev && event.GetProperty() == devSettings["sample_rate"]) {
+
DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId());
std::string strRate = deviceArgs["sample_rate"].options[event.GetPropertyValue().GetInteger()];
@@ -453,17 +526,31 @@ void SDRDevicesDialog::OnPropGridChanged( wxPropertyGridEvent& event ) {
try {
srate = std::stol(strRate);
devConfig->setSampleRate(srate);
-
- if (dev->isActive() || !wxGetApp().getDevice()) {
+ if (dev->isActive() || !wxGetApp().getDevice()) {
wxGetApp().setSampleRate(srate);
- wxGetApp().notifyMainUIOfDeviceChange();
- }
+ }
} catch (std::invalid_argument e) {
// nop
}
- } else if (editId && dev) {
- wxPGProperty *prop = event.GetProperty();
+ } else if (dev && event.GetProperty() == devSettings["antenna"]) {
+ DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId());
+
+ std::string strAntennaName = deviceArgs["antenna"].options[event.GetPropertyValue().GetInteger()];
+ try {
+ devConfig->setAntennaName(strAntennaName);
+
+ if (dev->isActive() || !wxGetApp().getDevice()) {
+ wxGetApp().setAntennaName(strAntennaName);
+ }
+ }
+ catch (std::invalid_argument e) {
+ // nop
+ }
+ }
+ else if (dev) {
+ wxPGProperty *prop = event.GetProperty();
+ //change value of RuntimeProps
for (std::map<std::string, wxPGProperty *>::iterator rtp = runtimeProps.begin(); rtp != runtimeProps.end(); rtp++) {
if (rtp->second == prop) {
SoapySDR::Device *soapyDev = dev->getSoapyDevice();
@@ -481,7 +568,6 @@ void SDRDevicesDialog::OnPropGridChanged( wxPropertyGridEvent& event ) {
if (dev->isActive()) {
wxGetApp().getSDRThread()->writeSetting(rtp->first, settingValue);
}
- refreshDeviceProperties();
return;
}
}
@@ -502,10 +588,11 @@ void SDRDevicesDialog::doRefreshDevices() {
devTree->DeleteAllItems();
devTree->Disable();
m_propertyGrid->Clear();
- runtimeArgs.erase(runtimeArgs.begin(), runtimeArgs.end());
- runtimeProps.erase(runtimeProps.begin(), runtimeProps.end());
- streamProps.erase(streamProps.begin(), streamProps.end());
- devSettings.erase(devSettings.begin(), devSettings.end());
+ devSettings.clear();
+ runtimeArgs.clear();
+ runtimeProps.clear();
+ streamProps.clear();
+
m_refreshButton->Disable();
m_addRemoteButton->Disable();
m_useSelectedButton->Disable();
diff --git a/src/forms/SDRDevices/SDRDevicesForm.cpp b/src/forms/SDRDevices/SDRDevicesForm.cpp
index cea3da8..d3f0fd5 100644
--- a/src/forms/SDRDevices/SDRDevicesForm.cpp
+++ b/src/forms/SDRDevices/SDRDevicesForm.cpp
@@ -1,115 +1,115 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Aug 23 2015)
+// C++ code generated with wxFormBuilder (version Oct 27 2017)
// http://www.wxformbuilder.org/
//
-// PLEASE DO "NOT" EDIT THIS FILE!
+// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "SDRDevicesForm.h"
///////////////////////////////////////////////////////////////////////////
-devFrame::devFrame( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style )
+devFrame::devFrame(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style)
{
- this->SetSizeHints( wxDefaultSize, wxDefaultSize );
-
- devStatusBar = this->CreateStatusBar( 1, wxST_SIZEGRIP, wxID_ANY );
+ this->SetSizeHints(wxDefaultSize, wxDefaultSize);
+
+ devStatusBar = this->CreateStatusBar(1, wxST_SIZEGRIP, wxID_ANY);
wxBoxSizer* devFrameSizer;
- devFrameSizer = new wxBoxSizer( wxVERTICAL );
-
- m_panel3 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ devFrameSizer = new wxBoxSizer(wxVERTICAL);
+
+ m_panel3 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
wxBoxSizer* bSizer4;
- bSizer4 = new wxBoxSizer( wxHORIZONTAL );
-
- m_panel6 = new wxPanel( m_panel3, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ bSizer4 = new wxBoxSizer(wxHORIZONTAL);
+
+ m_panel6 = new wxPanel(m_panel3, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
wxBoxSizer* bSizer6;
- bSizer6 = new wxBoxSizer( wxVERTICAL );
-
- devTree = new wxTreeCtrl( m_panel6, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_DEFAULT_STYLE );
- devTree->Enable( false );
-
- bSizer6->Add( devTree, 1, wxEXPAND, 5 );
-
- m_panel4 = new wxPanel( m_panel6, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ bSizer6 = new wxBoxSizer(wxVERTICAL);
+
+ devTree = new wxTreeCtrl(m_panel6, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_DEFAULT_STYLE);
+ devTree->Enable(false);
+
+ bSizer6->Add(devTree, 1, wxEXPAND, 5);
+
+ m_panel4 = new wxPanel(m_panel6, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
wxBoxSizer* bSizer5;
- bSizer5 = new wxBoxSizer( wxHORIZONTAL );
-
- m_refreshButton = new wxButton( m_panel4, wxID_ANY, wxT("Refresh"), wxDefaultPosition, wxDefaultSize, 0 );
- bSizer5->Add( m_refreshButton, 1, wxALL, 5 );
-
- m_addRemoteButton = new wxButton( m_panel4, wxID_ANY, wxT("Add"), wxDefaultPosition, wxDefaultSize, 0 );
- bSizer5->Add( m_addRemoteButton, 1, wxALL, 5 );
-
- m_useSelectedButton = new wxButton( m_panel4, wxID_ANY, wxT("Start"), wxDefaultPosition, wxDefaultSize, 0 );
- bSizer5->Add( m_useSelectedButton, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
-
-
- m_panel4->SetSizer( bSizer5 );
+ bSizer5 = new wxBoxSizer(wxHORIZONTAL);
+
+ m_refreshButton = new wxButton(m_panel4, wxID_ANY, wxT("Refresh"), wxDefaultPosition, wxDefaultSize, 0);
+ bSizer5->Add(m_refreshButton, 1, wxALL, 5);
+
+ m_addRemoteButton = new wxButton(m_panel4, wxID_ANY, wxT("Add"), wxDefaultPosition, wxDefaultSize, 0);
+ bSizer5->Add(m_addRemoteButton, 1, wxALL, 5);
+
+ m_useSelectedButton = new wxButton(m_panel4, wxID_ANY, wxT("Start"), wxDefaultPosition, wxDefaultSize, 0);
+ bSizer5->Add(m_useSelectedButton, 1, wxALL | wxALIGN_CENTER_VERTICAL, 5);
+
+
+ m_panel4->SetSizer(bSizer5);
m_panel4->Layout();
- bSizer5->Fit( m_panel4 );
- bSizer6->Add( m_panel4, 0, wxEXPAND, 5 );
-
-
- m_panel6->SetSizer( bSizer6 );
+ bSizer5->Fit(m_panel4);
+ bSizer6->Add(m_panel4, 0, wxEXPAND, 5);
+
+
+ m_panel6->SetSizer(bSizer6);
m_panel6->Layout();
- bSizer6->Fit( m_panel6 );
- bSizer4->Add( m_panel6, 1, wxEXPAND | wxALL, 5 );
-
- m_panel61 = new wxPanel( m_panel3, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ bSizer6->Fit(m_panel6);
+ bSizer4->Add(m_panel6, 1, wxEXPAND | wxALL, 5);
+
+ m_panel61 = new wxPanel(m_panel3, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
wxBoxSizer* bSizer7;
- bSizer7 = new wxBoxSizer( wxVERTICAL );
-
- m_staticText1 = new wxStaticText( m_panel61, wxID_ANY, wxT("SoapySDR Device Options"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText1->Wrap( -1 );
- bSizer7->Add( m_staticText1, 0, wxALL, 5 );
-
+ bSizer7 = new wxBoxSizer(wxVERTICAL);
+
+ m_staticText1 = new wxStaticText(m_panel61, wxID_ANY, wxT("SoapySDR Device Options"), wxDefaultPosition, wxDefaultSize, 0);
+ m_staticText1->Wrap(-1);
+ bSizer7->Add(m_staticText1, 0, wxALL, 5);
+
m_propertyGrid = new wxPropertyGrid(m_panel61, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxPG_DEFAULT_STYLE);
- bSizer7->Add( m_propertyGrid, 1, wxALL|wxEXPAND, 5 );
-
-
- m_panel61->SetSizer( bSizer7 );
+ bSizer7->Add(m_propertyGrid, 1, wxALL | wxEXPAND, 5);
+
+
+ m_panel61->SetSizer(bSizer7);
m_panel61->Layout();
- bSizer7->Fit( m_panel61 );
- bSizer4->Add( m_panel61, 1, wxEXPAND | wxALL, 5 );
-
-
- m_panel3->SetSizer( bSizer4 );
+ bSizer7->Fit(m_panel61);
+ bSizer4->Add(m_panel61, 1, wxEXPAND | wxALL, 5);
+
+
+ m_panel3->SetSizer(bSizer4);
m_panel3->Layout();
- bSizer4->Fit( m_panel3 );
- devFrameSizer->Add( m_panel3, 1, wxEXPAND | wxALL, 5 );
-
-
- this->SetSizer( devFrameSizer );
+ bSizer4->Fit(m_panel3);
+ devFrameSizer->Add(m_panel3, 1, wxEXPAND | wxALL, 5);
+
+
+ this->SetSizer(devFrameSizer);
this->Layout();
- m_deviceTimer.SetOwner( this, wxID_ANY );
-
- this->Centre( wxBOTH );
-
+ m_deviceTimer.SetOwner(this, wxID_ANY);
+
+ this->Centre(wxBOTH);
+
// Connect Events
- this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( devFrame::OnClose ) );
- devTree->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( devFrame::OnTreeDoubleClick ), NULL, this );
- devTree->Connect( wxEVT_COMMAND_TREE_DELETE_ITEM, wxTreeEventHandler( devFrame::OnDeleteItem ), NULL, this );
- devTree->Connect( wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler( devFrame::OnSelectionChanged ), NULL, this );
- m_refreshButton->Connect( wxEVT_LEFT_UP, wxMouseEventHandler( devFrame::OnRefreshDevices ), NULL, this );
- m_addRemoteButton->Connect( wxEVT_LEFT_UP, wxMouseEventHandler( devFrame::OnAddRemote ), NULL, this );
- m_useSelectedButton->Connect( wxEVT_LEFT_UP, wxMouseEventHandler( devFrame::OnUseSelected ), NULL, this );
- m_propertyGrid->Connect( wxEVT_PG_CHANGED, wxPropertyGridEventHandler( devFrame::OnPropGridChanged ), NULL, this );
- m_propertyGrid->Connect( wxEVT_SET_FOCUS, wxFocusEventHandler( devFrame::OnPropGridFocus ), NULL, this );
- this->Connect( wxID_ANY, wxEVT_TIMER, wxTimerEventHandler( devFrame::OnDeviceTimer ) );
+ this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(devFrame::OnClose));
+ devTree->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(devFrame::OnTreeDoubleClick), NULL, this);
+ devTree->Connect(wxEVT_COMMAND_TREE_DELETE_ITEM, wxTreeEventHandler(devFrame::OnDeleteItem), NULL, this);
+ devTree->Connect(wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler(devFrame::OnSelectionChanged), NULL, this);
+ m_refreshButton->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(devFrame::OnRefreshDevices), NULL, this);
+ m_addRemoteButton->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(devFrame::OnAddRemote), NULL, this);
+ m_useSelectedButton->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(devFrame::OnUseSelected), NULL, this);
+ m_propertyGrid->Connect(wxEVT_PG_CHANGED, wxPropertyGridEventHandler(devFrame::OnPropGridChanged), NULL, this);
+ m_propertyGrid->Connect(wxEVT_SET_FOCUS, wxFocusEventHandler(devFrame::OnPropGridFocus), NULL, this);
+ this->Connect(wxID_ANY, wxEVT_TIMER, wxTimerEventHandler(devFrame::OnDeviceTimer));
}
devFrame::~devFrame()
{
// Disconnect Events
- this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( devFrame::OnClose ) );
- devTree->Disconnect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( devFrame::OnTreeDoubleClick ), NULL, this );
- devTree->Disconnect( wxEVT_COMMAND_TREE_DELETE_ITEM, wxTreeEventHandler( devFrame::OnDeleteItem ), NULL, this );
- devTree->Disconnect( wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler( devFrame::OnSelectionChanged ), NULL, this );
- m_refreshButton->Disconnect( wxEVT_LEFT_UP, wxMouseEventHandler( devFrame::OnRefreshDevices ), NULL, this );
- m_addRemoteButton->Disconnect( wxEVT_LEFT_UP, wxMouseEventHandler( devFrame::OnAddRemote ), NULL, this );
- m_useSelectedButton->Disconnect( wxEVT_LEFT_UP, wxMouseEventHandler( devFrame::OnUseSelected ), NULL, this );
- m_propertyGrid->Disconnect( wxEVT_PG_CHANGED, wxPropertyGridEventHandler( devFrame::OnPropGridChanged ), NULL, this );
- m_propertyGrid->Disconnect( wxEVT_SET_FOCUS, wxFocusEventHandler( devFrame::OnPropGridFocus ), NULL, this );
- this->Disconnect( wxID_ANY, wxEVT_TIMER, wxTimerEventHandler( devFrame::OnDeviceTimer ) );
-
+ this->Disconnect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(devFrame::OnClose));
+ devTree->Disconnect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(devFrame::OnTreeDoubleClick), NULL, this);
+ devTree->Disconnect(wxEVT_COMMAND_TREE_DELETE_ITEM, wxTreeEventHandler(devFrame::OnDeleteItem), NULL, this);
+ devTree->Disconnect(wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler(devFrame::OnSelectionChanged), NULL, this);
+ m_refreshButton->Disconnect(wxEVT_LEFT_UP, wxMouseEventHandler(devFrame::OnRefreshDevices), NULL, this);
+ m_addRemoteButton->Disconnect(wxEVT_LEFT_UP, wxMouseEventHandler(devFrame::OnAddRemote), NULL, this);
+ m_useSelectedButton->Disconnect(wxEVT_LEFT_UP, wxMouseEventHandler(devFrame::OnUseSelected), NULL, this);
+ m_propertyGrid->Disconnect(wxEVT_PG_CHANGED, wxPropertyGridEventHandler(devFrame::OnPropGridChanged), NULL, this);
+ m_propertyGrid->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(devFrame::OnPropGridFocus), NULL, this);
+ this->Disconnect(wxID_ANY, wxEVT_TIMER, wxTimerEventHandler(devFrame::OnDeviceTimer));
+
}
diff --git a/src/forms/SDRDevices/SDRDevicesForm.h b/src/forms/SDRDevices/SDRDevicesForm.h
index 74d8d6c..c4976f0 100644
--- a/src/forms/SDRDevices/SDRDevicesForm.h
+++ b/src/forms/SDRDevices/SDRDevicesForm.h
@@ -1,8 +1,8 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Aug 23 2015)
+// C++ code generated with wxFormBuilder (version Oct 27 2017)
// http://www.wxformbuilder.org/
//
-// PLEASE DO "NOT" EDIT THIS FILE!
+// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#ifndef __SDRDEVICESFORM_H__
@@ -35,43 +35,43 @@
///////////////////////////////////////////////////////////////////////////////
/// Class devFrame
///////////////////////////////////////////////////////////////////////////////
-class devFrame : public wxFrame
+class devFrame : public wxFrame
{
- private:
-
- protected:
- wxStatusBar* devStatusBar;
- wxPanel* m_panel3;
- wxPanel* m_panel6;
- wxTreeCtrl* devTree;
- wxPanel* m_panel4;
- wxButton* m_refreshButton;
- wxButton* m_addRemoteButton;
- wxButton* m_useSelectedButton;
- wxPanel* m_panel61;
- wxStaticText* m_staticText1;
- wxPropertyGrid* m_propertyGrid;
- wxTimer m_deviceTimer;
-
- // Virtual event handlers, overide them in your derived class
- virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
- virtual void OnTreeDoubleClick( wxMouseEvent& event ) { event.Skip(); }
- virtual void OnDeleteItem( wxTreeEvent& event ) { event.Skip(); }
- virtual void OnSelectionChanged( wxTreeEvent& event ) { event.Skip(); }
- virtual void OnRefreshDevices( wxMouseEvent& event ) { event.Skip(); }
- virtual void OnAddRemote( wxMouseEvent& event ) { event.Skip(); }
- virtual void OnUseSelected( wxMouseEvent& event ) { event.Skip(); }
- virtual void OnPropGridChanged( wxPropertyGridEvent& event ) { event.Skip(); }
- virtual void OnPropGridFocus( wxFocusEvent& event ) { event.Skip(); }
- virtual void OnDeviceTimer( wxTimerEvent& event ) { event.Skip(); }
-
-
- public:
-
- devFrame( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("CubicSDR :: SDR Devices"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 700,467 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
-
- ~devFrame();
-
+private:
+
+protected:
+ wxStatusBar* devStatusBar;
+ wxPanel* m_panel3;
+ wxPanel* m_panel6;
+ wxTreeCtrl* devTree;
+ wxPanel* m_panel4;
+ wxButton* m_refreshButton;
+ wxButton* m_addRemoteButton;
+ wxButton* m_useSelectedButton;
+ wxPanel* m_panel61;
+ wxStaticText* m_staticText1;
+ wxPropertyGrid* m_propertyGrid;
+ wxTimer m_deviceTimer;
+
+ // Virtual event handlers, overide them in your derived class
+ virtual void OnClose(wxCloseEvent& event) { event.Skip(); }
+ virtual void OnTreeDoubleClick(wxMouseEvent& event) { event.Skip(); }
+ virtual void OnDeleteItem(wxTreeEvent& event) { event.Skip(); }
+ virtual void OnSelectionChanged(wxTreeEvent& event) { event.Skip(); }
+ virtual void OnRefreshDevices(wxMouseEvent& event) { event.Skip(); }
+ virtual void OnAddRemote(wxMouseEvent& event) { event.Skip(); }
+ virtual void OnUseSelected(wxMouseEvent& event) { event.Skip(); }
+ virtual void OnPropGridChanged(wxPropertyGridEvent& event) { event.Skip(); }
+ virtual void OnPropGridFocus(wxFocusEvent& event) { event.Skip(); }
+ virtual void OnDeviceTimer(wxTimerEvent& event) { event.Skip(); }
+
+
+public:
+
+ devFrame(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("CubicSDR :: SDR Devices"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(700, 467), long style = wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL);
+
+ ~devFrame();
+
};
#endif //__SDRDEVICESFORM_H__
diff --git a/src/modules/modem/Modem.h b/src/modules/modem/Modem.h
index a22166d..355f492 100644
--- a/src/modules/modem/Modem.h
+++ b/src/modules/modem/Modem.h
@@ -8,6 +8,7 @@
#include "AudioThread.h"
#include <cmath>
#include <atomic>
+#include <memory>
#define MIN_BANDWIDTH 500
@@ -25,7 +26,7 @@ public:
int audioSampleRate;
};
-class ModemIQData: public ReferenceCounter {
+class ModemIQData {
public:
std::vector<liquid_float_complex> data;
long long sampleRate;
@@ -34,11 +35,13 @@ public:
}
- ~ModemIQData() {
- std::lock_guard < std::recursive_mutex > lock(m_mutex);
+ virtual ~ModemIQData() {
+
}
};
+typedef std::shared_ptr<ModemIQData> ModemIQDataPtr;
+
// Copy of SoapySDR::Range, original comments
class ModemRange
{
diff --git a/src/modules/modem/analog/ModemAM.cpp b/src/modules/modem/analog/ModemAM.cpp
index 03da7e8..5c3604f 100644
--- a/src/modules/modem/analog/ModemAM.cpp
+++ b/src/modules/modem/analog/ModemAM.cpp
@@ -24,13 +24,13 @@ int ModemAM::getDefaultSampleRate() {
return 6000;
}
-void ModemAM::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *audioOut) {
+void ModemAM::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput* audioOut) {
ModemKitAnalog *amkit = (ModemKitAnalog *)kit;
initOutputBuffers(amkit,input);
if (!bufSize) {
- input->decRefCount();
+
return;
}
diff --git a/src/modules/modem/analog/ModemDSB.cpp b/src/modules/modem/analog/ModemDSB.cpp
index 37f5b26..4c69c12 100644
--- a/src/modules/modem/analog/ModemDSB.cpp
+++ b/src/modules/modem/analog/ModemDSB.cpp
@@ -30,7 +30,7 @@ void ModemDSB::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *a
initOutputBuffers(amkit, input);
if (!bufSize) {
- input->decRefCount();
+
return;
}
diff --git a/src/modules/modem/analog/ModemFM.cpp b/src/modules/modem/analog/ModemFM.cpp
index da969b8..cf20709 100644
--- a/src/modules/modem/analog/ModemFM.cpp
+++ b/src/modules/modem/analog/ModemFM.cpp
@@ -29,7 +29,7 @@ void ModemFM::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *au
initOutputBuffers(fmkit, input);
if (!bufSize) {
- input->decRefCount();
+
return;
}
diff --git a/src/modules/modem/analog/ModemIQ.cpp b/src/modules/modem/analog/ModemIQ.cpp
index 9fa3ec7..81af37d 100644
--- a/src/modules/modem/analog/ModemIQ.cpp
+++ b/src/modules/modem/analog/ModemIQ.cpp
@@ -42,7 +42,7 @@ void ModemIQ::demodulate(ModemKit * /* kit */, ModemIQData *input, AudioThreadIn
size_t bufSize = input->data.size();
if (!bufSize) {
- input->decRefCount();
+
return;
}
diff --git a/src/modules/modem/analog/ModemLSB.cpp b/src/modules/modem/analog/ModemLSB.cpp
index 295a9c4..c3fb76d 100644
--- a/src/modules/modem/analog/ModemLSB.cpp
+++ b/src/modules/modem/analog/ModemLSB.cpp
@@ -46,7 +46,7 @@ void ModemLSB::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *a
initOutputBuffers(akit,input);
if (!bufSize) {
- input->decRefCount();
+
return;
}
diff --git a/src/modules/modem/analog/ModemNBFM.cpp b/src/modules/modem/analog/ModemNBFM.cpp
index fd1b255..84e3202 100644
--- a/src/modules/modem/analog/ModemNBFM.cpp
+++ b/src/modules/modem/analog/ModemNBFM.cpp
@@ -29,7 +29,7 @@ void ModemNBFM::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *
initOutputBuffers(fmkit, input);
if (!bufSize) {
- input->decRefCount();
+
return;
}
diff --git a/src/modules/modem/analog/ModemUSB.cpp b/src/modules/modem/analog/ModemUSB.cpp
index 775fadc..8b30761 100644
--- a/src/modules/modem/analog/ModemUSB.cpp
+++ b/src/modules/modem/analog/ModemUSB.cpp
@@ -46,7 +46,7 @@ void ModemUSB::demodulate(ModemKit *kit, ModemIQData *input, AudioThreadInput *a
initOutputBuffers(akit,input);
if (!bufSize) {
- input->decRefCount();
+
return;
}
diff --git a/src/panel/MeterPanel.cpp b/src/panel/MeterPanel.cpp
index c3d191f..ea74311 100644
--- a/src/panel/MeterPanel.cpp
+++ b/src/panel/MeterPanel.cpp
@@ -126,7 +126,7 @@ bool MeterPanel::isMeterHit(CubicVR::vec2 mousePoint) {
return false;
}
-float MeterPanel::getMeterHitValue(CubicVR::vec2 mousePoint, GLPanel &panel) {
+float MeterPanel::getMeterHitValue(CubicVR::vec2 mousePoint) {
CubicVR::vec2 hitResult;
if (bgPanel.hitTest(mousePoint, hitResult)) {
diff --git a/src/panel/MeterPanel.h b/src/panel/MeterPanel.h
index 21b4c58..86a737d 100644
--- a/src/panel/MeterPanel.h
+++ b/src/panel/MeterPanel.h
@@ -20,7 +20,7 @@ public:
void setHighlightVisible(bool vis);
float getValue();
bool isMeterHit(CubicVR::vec2 mousePoint);
- float getMeterHitValue(CubicVR::vec2 mousePoint, GLPanel &panel);
+ float getMeterHitValue(CubicVR::vec2 mousePoint);
void setChanged(bool changed);
bool getChanged();
@@ -32,7 +32,7 @@ protected:
private:
std::string name;
float low, high, current;
- bool changed;
+ bool changed = false;
GLPanel bgPanel;
GLPanel levelPanel;
GLPanel highlightPanel;
diff --git a/src/panel/ScopePanel.cpp b/src/panel/ScopePanel.cpp
index bde35cd..c55d9f4 100644
--- a/src/panel/ScopePanel.cpp
+++ b/src/panel/ScopePanel.cpp
@@ -35,7 +35,7 @@ void ScopePanel::drawPanelContents() {
bgPanel.draw();
glLineWidth(1.0);
glEnable(GL_LINE_SMOOTH);
- glLoadMatrixf(transform);
+ glLoadMatrixf(transform.to_ptr());
glColor3f(ThemeMgr::mgr.currentTheme->scopeLine.r * 0.35, ThemeMgr::mgr.currentTheme->scopeLine.g * 0.35,
ThemeMgr::mgr.currentTheme->scopeLine.b * 0.35);
glBegin (GL_LINES);
@@ -52,7 +52,7 @@ void ScopePanel::drawPanelContents() {
bgPanelStereo[1].draw();
glLineWidth(1.0);
- glLoadMatrixf(transform);
+ glLoadMatrixf(transform.to_ptr());
glColor3f(ThemeMgr::mgr.currentTheme->scopeLine.r, ThemeMgr::mgr.currentTheme->scopeLine.g, ThemeMgr::mgr.currentTheme->scopeLine.b);
glEnable(GL_LINE_SMOOTH);
glBegin (GL_LINES);
@@ -76,7 +76,7 @@ void ScopePanel::drawPanelContents() {
glLineWidth(1.0);
glEnable(GL_POINT_SMOOTH);
glPointSize(1.0);
- glLoadMatrixf(transform);
+ glLoadMatrixf(transform.to_ptr());
glColor3f(ThemeMgr::mgr.currentTheme->scopeLine.r * 0.15, ThemeMgr::mgr.currentTheme->scopeLine.g * 0.15,
ThemeMgr::mgr.currentTheme->scopeLine.b * 0.15);
}
@@ -95,16 +95,16 @@ void ScopePanel::drawPanelContents() {
glVertexPointer(2, GL_FLOAT, 0, &points[0]);
glLineWidth(1.5);
if (scopeMode == SCOPE_MODE_Y) {
- glLoadMatrixf(bgPanel.transform);
+ glLoadMatrixf(bgPanel.transform.to_ptr());
glDrawArrays(GL_LINE_STRIP, 0, points.size() / 2);
} else if (scopeMode == SCOPE_MODE_2Y) {
- glLoadMatrixf(bgPanelStereo[0].transform);
+ glLoadMatrixf(bgPanelStereo[0].transform.to_ptr());
glDrawArrays(GL_LINE_STRIP, 0, points.size() / 4);
- glLoadMatrixf(bgPanelStereo[1].transform);
+ glLoadMatrixf(bgPanelStereo[1].transform.to_ptr());
glDrawArrays(GL_LINE_STRIP, points.size() / 4, points.size() / 4);
} else if (scopeMode == SCOPE_MODE_XY) {
- glLoadMatrixf(bgPanel.transform);
+ glLoadMatrixf(bgPanel.transform.to_ptr());
glDrawArrays(GL_POINTS, 0, points.size() / 2);
}
glLineWidth(1.0);
diff --git a/src/panel/SpectrumPanel.cpp b/src/panel/SpectrumPanel.cpp
index 780f442..a9ba1fb 100644
--- a/src/panel/SpectrumPanel.cpp
+++ b/src/panel/SpectrumPanel.cpp
@@ -113,7 +113,7 @@ void SpectrumPanel::drawPanelContents() {
glEnable(GL_LINE_SMOOTH);
glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
- glLoadMatrixf(transform * (CubicVR::mat4::translate(-1.0f, -0.75f, 0.0f) * CubicVR::mat4::scale(2.0f, 1.5f, 1.0f)));
+ glLoadMatrixf((transform * (CubicVR::mat4::translate(-1.0f, -0.75f, 0.0f) * CubicVR::mat4::scale(2.0f, 1.5f, 1.0f))).to_ptr());
if (points.size()) {
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
@@ -166,7 +166,7 @@ void SpectrumPanel::drawPanelContents() {
float viewHeight = (float) vp[3];
float viewWidth = (float) vp[2];
- glLoadMatrixf(transform);
+ glLoadMatrixf(transform.to_ptr());
long long leftFreq = (double) freq - ((double) bandwidth / 2.0);
diff --git a/src/panel/WaterfallPanel.cpp b/src/panel/WaterfallPanel.cpp
index 20b6fa1..bdbc8d8 100644
--- a/src/panel/WaterfallPanel.cpp
+++ b/src/panel/WaterfallPanel.cpp
@@ -165,7 +165,7 @@ void WaterfallPanel::drawPanelContents() {
int half_fft_size = fft_size / 2;
- glLoadMatrixf(transform);
+ glLoadMatrixf(transform.to_ptr());
glEnable (GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
diff --git a/src/process/FFTDataDistributor.cpp b/src/process/FFTDataDistributor.cpp
index 8636b70..07a6a3d 100644
--- a/src/process/FFTDataDistributor.cpp
+++ b/src/process/FFTDataDistributor.cpp
@@ -5,6 +5,9 @@
#include <algorithm>
#include <ThreadBlockingQueue.h>
+//50 ms
+#define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000)
+
FFTDataDistributor::FFTDataDistributor() : outputBuffers("FFTDataDistributorBuffers"), fftSize(DEFAULT_FFT_SIZE), linesPerSecond(DEFAULT_WATERFALL_LPS), lineRateAccum(0.0) {
}
@@ -28,8 +31,11 @@ void FFTDataDistributor::process() {
if (!isAnyOutputEmpty()) {
return;
}
- DemodulatorThreadIQData *inp;
- input->pop(inp);
+ DemodulatorThreadIQDataPtr inp;
+
+ if (!input->pop(inp, HEARTBEAT_CHECK_PERIOD_MICROS)) {
+ continue;
+ }
if (inp) {
//Settings have changed, set new values and dump all previous samples stored in inputBuffer:
@@ -73,7 +79,7 @@ void FFTDataDistributor::process() {
memcpy(&inputBuffer.data[bufferOffset+bufferedItems],&inp->data[0], nbSamplesToAdd *sizeof(liquid_float_complex));
bufferedItems += nbSamplesToAdd;
//
- inp->decRefCount();
+
} else {
//empty inp, wait for another.
continue;
@@ -105,7 +111,8 @@ void FFTDataDistributor::process() {
if (lineRateAccum >= 1.0) {
//each i represents a FFT computation
- DemodulatorThreadIQData *outp = outputBuffers.getBuffer();
+ DemodulatorThreadIQDataPtr outp = outputBuffers.getBuffer();
+
outp->frequency = inputBuffer.frequency;
outp->sampleRate = inputBuffer.sampleRate;
outp->data.assign(inputBuffer.data.begin()+bufferOffset+i,
diff --git a/src/process/FFTVisualDataThread.cpp b/src/process/FFTVisualDataThread.cpp
index c0c820e..0dd398e 100644
--- a/src/process/FFTVisualDataThread.cpp
+++ b/src/process/FFTVisualDataThread.cpp
@@ -27,11 +27,12 @@ SpectrumVisualProcessor *FFTVisualDataThread::getProcessor() {
}
void FFTVisualDataThread::run() {
- DemodulatorThreadInputQueue *pipeIQDataIn = static_cast<DemodulatorThreadInputQueue *>(getInputQueue("IQDataInput"));
- SpectrumVisualDataQueue *pipeFFTDataOut = static_cast<SpectrumVisualDataQueue *>(getOutputQueue("FFTDataOutput"));
+
+ DemodulatorThreadInputQueuePtr pipeIQDataIn = std::static_pointer_cast<DemodulatorThreadInputQueue>(getInputQueue("IQDataInput"));
+ SpectrumVisualDataQueuePtr pipeFFTDataOut = std::static_pointer_cast<SpectrumVisualDataQueue>(getOutputQueue("FFTDataOutput"));
- fftQueue.set_max_num_items(100);
+ fftQueue->set_max_num_items(100);
pipeFFTDataOut->set_max_num_items(100);
//FFT distributor plumbing:
@@ -39,10 +40,10 @@ void FFTVisualDataThread::run() {
fftDistrib.setInput(pipeIQDataIn);
//The FFT distributor has actually 1 output only, so it doesn't distribute at all :)
- fftDistrib.attachOutput(&fftQueue);
+ fftDistrib.attachOutput(fftQueue);
//FFT Distributor output is ==> SpectrumVisualProcessor input.
- wproc.setInput(&fftQueue);
+ wproc.setInput(fftQueue);
wproc.attachOutput(pipeFFTDataOut);
wproc.setup(DEFAULT_FFT_SIZE);
@@ -72,11 +73,20 @@ void FFTVisualDataThread::run() {
fftDistrib.run();
// Make wproc do a FFT of each of the sample sets provided by fftDistrib:
- while (!wproc.isInputEmpty()) {
+ while (!stopping && !wproc.isInputEmpty()) {
wproc.run();
}
}
+
+ pipeIQDataIn->flush();
+ pipeFFTDataOut->flush();
// std::cout << "FFT visual data thread done." << std::endl;
}
+void FFTVisualDataThread::terminate() {
+ IOThread::terminate();
+ fftDistrib.flushQueues();
+ wproc.flushQueues();
+}
+
diff --git a/src/process/FFTVisualDataThread.h b/src/process/FFTVisualDataThread.h
index 55ff408..549950b 100644
--- a/src/process/FFTVisualDataThread.h
+++ b/src/process/FFTVisualDataThread.h
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-2.0+
#pragma once
-
+#include <memory>
#include "IOThread.h"
#include "SpectrumVisualProcessor.h"
#include "FFTDataDistributor.h"
@@ -17,10 +17,12 @@ public:
SpectrumVisualProcessor *getProcessor();
virtual void run();
+
+ virtual void terminate();
protected:
FFTDataDistributor fftDistrib;
- DemodulatorThreadInputQueue fftQueue;
+ DemodulatorThreadInputQueuePtr fftQueue = std::make_shared<DemodulatorThreadInputQueue>();
SpectrumVisualProcessor wproc;
std::atomic_int linesPerSecond;
diff --git a/src/process/ScopeVisualProcessor.cpp b/src/process/ScopeVisualProcessor.cpp
index 5b83b07..a1eefac 100644
--- a/src/process/ScopeVisualProcessor.cpp
+++ b/src/process/ScopeVisualProcessor.cpp
@@ -47,7 +47,7 @@ void ScopeVisualProcessor::process() {
if (!isOutputEmpty()) {
return;
}
- AudioThreadInput *audioInputData;
+ AudioThreadInputPtr audioInputData;
if (input->try_pop(audioInputData)) {
@@ -56,11 +56,12 @@ void ScopeVisualProcessor::process() {
}
size_t i, iMax = audioInputData->data.size();
if (!iMax) {
- delete audioInputData; //->decRefCount();
+ //discard audioInputData.
+ audioInputData = nullptr;
return;
}
- ScopeRenderData *renderData = NULL;
+ ScopeRenderDataPtr renderData = nullptr;
if (scopeEnabled) {
iMax = audioInputData->data.size();
@@ -150,7 +151,7 @@ void ScopeVisualProcessor::process() {
renderData->inputRate = audioInputData->inputRate;
renderData->sampleRate = audioInputData->sampleRate;
- delete audioInputData; //->decRefCount();
+ audioInputData = nullptr; //->decRefCount();
double fft_ceil = 0, fft_floor = 1;
@@ -212,8 +213,6 @@ void ScopeVisualProcessor::process() {
renderData->spectrum = true;
distribute(renderData);
- } else {
- delete audioInputData; //->decRefCount();
- }
+ }
} //end if try_pop()
}
diff --git a/src/process/ScopeVisualProcessor.h b/src/process/ScopeVisualProcessor.h
index d5ba64c..0cd5a44 100644
--- a/src/process/ScopeVisualProcessor.h
+++ b/src/process/ScopeVisualProcessor.h
@@ -6,8 +6,9 @@
#include "VisualProcessor.h"
#include "AudioThread.h"
#include "ScopePanel.h"
+#include <memory>
-class ScopeRenderData: public ReferenceCounter {
+class ScopeRenderData {
public:
std::vector<float> waveform_points;
ScopePanel::ScopeMode mode = ScopePanel::SCOPE_MODE_Y;
@@ -17,9 +18,17 @@ public:
bool spectrum;
int fft_size;
double fft_floor, fft_ceil;
+
+ virtual ~ScopeRenderData() {
+
+ }
};
-typedef ThreadBlockingQueue<ScopeRenderData *> ScopeRenderDataQueue;
+typedef std::shared_ptr<ScopeRenderData> ScopeRenderDataPtr;
+
+typedef ThreadBlockingQueue<ScopeRenderDataPtr> ScopeRenderDataQueue;
+
+typedef std::shared_ptr<ScopeRenderDataQueue> ScopeRenderDataQueuePtr;
class ScopeVisualProcessor : public VisualProcessor<AudioThreadInput, ScopeRenderData> {
public:
diff --git a/src/process/SpectrumVisualDataThread.cpp b/src/process/SpectrumVisualDataThread.cpp
index 1e954a2..5841f52 100644
--- a/src/process/SpectrumVisualDataThread.cpp
+++ b/src/process/SpectrumVisualDataThread.cpp
@@ -28,3 +28,7 @@ void SpectrumVisualDataThread::run() {
// std::cout << "Spectrum visual data thread done." << std::endl;
}
+void SpectrumVisualDataThread::terminate() {
+ IOThread::terminate();
+ sproc.flushQueues();
+}
\ No newline at end of file
diff --git a/src/process/SpectrumVisualDataThread.h b/src/process/SpectrumVisualDataThread.h
index c6efc6c..0c3eccf 100644
--- a/src/process/SpectrumVisualDataThread.h
+++ b/src/process/SpectrumVisualDataThread.h
@@ -13,6 +13,8 @@ public:
SpectrumVisualProcessor *getProcessor();
virtual void run();
+
+ virtual void terminate();
protected:
SpectrumVisualProcessor sproc;
diff --git a/src/process/SpectrumVisualProcessor.cpp b/src/process/SpectrumVisualProcessor.cpp
index 7b02892..19f27a1 100644
--- a/src/process/SpectrumVisualProcessor.cpp
+++ b/src/process/SpectrumVisualProcessor.cpp
@@ -4,6 +4,8 @@
#include "SpectrumVisualProcessor.h"
#include "CubicSDR.h"
+//50 ms
+#define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000)
SpectrumVisualProcessor::SpectrumVisualProcessor() : outputBuffers("SpectrumVisualProcessorBuffers") {
lastInputBandwidth = 0;
@@ -18,11 +20,11 @@ SpectrumVisualProcessor::SpectrumVisualProcessor() : outputBuffers("SpectrumVisu
fftLastData = nullptr;
fftPlan = nullptr;
- is_view.store(false);
- fftSize.store(0);
- centerFreq.store(0);
- bandwidth.store(0);
- hideDC.store(false);
+ is_view = false;
+ fftSize = 0;
+ centerFreq = 0;
+ bandwidth = 0;
+ hideDC = false;
freqShifter = nco_crcf_create(LIQUID_NCO);
shiftFrequency = 0;
@@ -30,14 +32,14 @@ SpectrumVisualProcessor::SpectrumVisualProcessor() : outputBuffers("SpectrumVisu
fft_ceil_ma = fft_ceil_maa = 100.0;
fft_floor_ma = fft_floor_maa = 0.0;
fft_floor_peak = 0.0;
- desiredInputSize.store(0);
+ desiredInputSize = 0;
fft_average_rate = 0.65f;
- scaleFactor.store(1.0);
- fftSizeChanged.store(false);
- newFFTSize.store(0);
+ scaleFactor = 1.0;
+ fftSizeChanged = false;
+ newFFTSize = 0;
lastView = false;
- peakHold.store(false);
- peakReset.store(false);
+ peakHold = false;
+ peakReset = false;
}
@@ -46,75 +48,93 @@ SpectrumVisualProcessor::~SpectrumVisualProcessor() {
}
bool SpectrumVisualProcessor::isView() {
- return is_view.load();
+
+ std::lock_guard < std::mutex > busy_lock(busy_run);
+
+ return is_view;
}
void SpectrumVisualProcessor::setView(bool bView) {
-
- std::lock_guard < std::mutex > busy_lock(busy_run);
- is_view.store(bView);
-
+
+ std::lock_guard < std::mutex > busy_lock(busy_run);
+
+ is_view = bView;
}
void SpectrumVisualProcessor::setView(bool bView, long long centerFreq_in, long bandwidth_in) {
std::lock_guard < std::mutex > busy_lock(busy_run);
- is_view.store(bView);
- bandwidth.store(bandwidth_in);
- centerFreq.store(centerFreq_in);
-
+ is_view = bView;
+ bandwidth = bandwidth_in;
+ centerFreq = centerFreq_in;
}
void SpectrumVisualProcessor::setFFTAverageRate(float fftAverageRate) {
std::lock_guard < std::mutex > busy_lock(busy_run);
- this->fft_average_rate.store(fftAverageRate);
-
+
+ this->fft_average_rate = fftAverageRate;
}
float SpectrumVisualProcessor::getFFTAverageRate() {
- return this->fft_average_rate.load();
+
+ std::lock_guard < std::mutex > busy_lock(busy_run);
+
+ return this->fft_average_rate;
}
void SpectrumVisualProcessor::setCenterFrequency(long long centerFreq_in) {
- std::lock_guard < std::mutex > busy_lock(busy_run);
- centerFreq.store(centerFreq_in);
-
+ std::lock_guard < std::mutex > busy_lock(busy_run);
+
+ centerFreq = centerFreq_in;
}
long long SpectrumVisualProcessor::getCenterFrequency() {
- return centerFreq.load();
+
+ std::lock_guard < std::mutex > busy_lock(busy_run);
+
+ return centerFreq;
}
void SpectrumVisualProcessor::setBandwidth(long bandwidth_in) {
std::lock_guard < std::mutex > busy_lock(busy_run);
- bandwidth.store(bandwidth_in);
-
+
+ bandwidth = bandwidth_in;
}
long SpectrumVisualProcessor::getBandwidth() {
- return bandwidth.load();
+
+ std::lock_guard < std::mutex > busy_lock(busy_run);
+
+ return bandwidth;
}
void SpectrumVisualProcessor::setPeakHold(bool peakHold_in) {
-
- if (peakHold.load() && peakHold_in) {
- peakReset.store(PEAK_RESET_COUNT);
+
+ std::lock_guard < std::mutex > busy_lock(busy_run);
+
+ if (peakHold && peakHold_in) {
+ peakReset = PEAK_RESET_COUNT;
} else {
- peakHold.store(peakHold_in);
- peakReset.store(1);
+ peakHold = peakHold_in;
+ peakReset = 1;
}
}
bool SpectrumVisualProcessor::getPeakHold() {
- return peakHold.load();
+
+ std::lock_guard < std::mutex > busy_lock(busy_run);
+
+ return peakHold;
}
int SpectrumVisualProcessor::getDesiredInputSize() {
- return desiredInputSize.load();
+ std::lock_guard < std::mutex > busy_lock(busy_run);
+
+ return desiredInputSize;
}
void SpectrumVisualProcessor::setup(unsigned int fftSize_in) {
@@ -155,27 +175,37 @@ void SpectrumVisualProcessor::setup(unsigned int fftSize_in) {
fft_destroy_plan(fftPlan);
}
fftPlan = fft_create_plan(fftSizeInternal, fftInput, fftOutput, LIQUID_FFT_FORWARD, 0);
-
}
void SpectrumVisualProcessor::setFFTSize(unsigned int fftSize_in) {
+
+ //then get the busy_lock
+ std::lock_guard < std::mutex > busy_lock(busy_run);
+
if (fftSize_in == fftSize) {
return;
}
newFFTSize = fftSize_in;
- fftSizeChanged.store(true);
+ fftSizeChanged = true;
}
unsigned int SpectrumVisualProcessor::getFFTSize() {
- if (fftSizeChanged.load()) {
+
+ //then get the busy_lock
+ std::lock_guard < std::mutex > busy_lock(busy_run);
+
+ if (fftSizeChanged) {
return newFFTSize;
}
- return fftSize.load();
+ return fftSize;
}
void SpectrumVisualProcessor::setHideDC(bool hideDC) {
- this->hideDC.store(hideDC);
+
+ std::lock_guard < std::mutex > busy_lock(busy_run);
+
+ this->hideDC = hideDC;
}
@@ -187,31 +217,37 @@ void SpectrumVisualProcessor::process() {
return;
}
- if (fftSizeChanged.load()) {
- setup(newFFTSize);
- fftSizeChanged.store(false);
- }
-
- DemodulatorThreadIQData *iqData;
+ bool executeSetup = false;
+
+ { // scoped lock here
+ std::lock_guard < std::mutex > busy_lock(busy_run);
+ if (fftSizeChanged) {
+ executeSetup = true;
+ fftSizeChanged = false;
+ }
+ }
+
+ if (executeSetup) {
+ setup(newFFTSize);
+ }
+
+ DemodulatorThreadIQDataPtr iqData;
- input->pop(iqData);
+ if (!input->pop(iqData, HEARTBEAT_CHECK_PERIOD_MICROS)) {
+ return;
+ }
if (!iqData) {
return;
}
-
- //Start by locking concurrent access to iqData
- std::lock_guard < std::recursive_mutex > lock(iqData->getMonitor());
-
- //then get the busy_lock
+ //then get the busy_lock for the rest of the processing.
std::lock_guard < std::mutex > busy_lock(busy_run);
-
-
- bool doPeak = peakHold.load() && (peakReset.load() == 0);
+ bool doPeak = peakHold && (peakReset == 0);
if (fft_result.size() != fftSizeInternal) {
+
if (fft_result.capacity() < fftSizeInternal) {
fft_result.reserve(fftSizeInternal);
fft_result_ma.reserve(fftSizeInternal);
@@ -225,9 +261,9 @@ void SpectrumVisualProcessor::process() {
fft_result_peak.resize(fftSizeInternal);
}
- if (peakReset.load() != 0) {
+ if (peakReset != 0) {
peakReset--;
- if (peakReset.load() == 0) {
+ if (peakReset == 0) {
for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) {
fft_result_peak[i] = fft_floor_maa;
}
@@ -242,11 +278,10 @@ void SpectrumVisualProcessor::process() {
unsigned int num_written;
long resampleBw = iqData->sampleRate;
bool newResampler = false;
- int bwDiff;
+ int bwDiff = 0;
- if (is_view.load()) {
+ if (is_view) {
if (!iqData->sampleRate) {
- iqData->decRefCount();
return;
}
@@ -259,7 +294,7 @@ void SpectrumVisualProcessor::process() {
size_t desired_input_size = fftSizeInternal / resamplerRatio;
- this->desiredInputSize.store(desired_input_size);
+ this->desiredInputSize = desired_input_size;
if (iqData->data.size() < desired_input_size) {
// std::cout << "fft underflow, desired: " << desired_input_size << " actual:" << input->data.size() << std::endl;
@@ -273,7 +308,7 @@ void SpectrumVisualProcessor::process() {
shiftFrequency = centerFreq - iqData->frequency;
nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) iqData->sampleRate)));
- if (is_view.load()) {
+ if (is_view) {
long freqDiff = shiftFrequency - lastShiftFrequency;
if (lastBandwidth!=0) {
@@ -297,7 +332,7 @@ void SpectrumVisualProcessor::process() {
}
}
}
- peakReset.store(PEAK_RESET_COUNT);
+ peakReset = PEAK_RESET_COUNT;
}
if (shiftBuffer.size() != desired_input_size) {
@@ -329,10 +364,9 @@ void SpectrumVisualProcessor::process() {
lastBandwidth = resampleBw;
lastInputBandwidth = iqData->sampleRate;
newResampler = true;
- peakReset.store(PEAK_RESET_COUNT);
+ peakReset = PEAK_RESET_COUNT;
}
-
-
+
unsigned int out_size = ceil((double) (desired_input_size) * resamplerRatio) + 512;
if (resampleBuffer.size() != out_size) {
@@ -351,7 +385,7 @@ void SpectrumVisualProcessor::process() {
memcpy(fftInData, resampleBuffer.data(), fftSizeInternal * sizeof(liquid_float_complex));
}
} else {
- this->desiredInputSize.store(fftSizeInternal);
+ this->desiredInputSize = fftSizeInternal;
num_written = data->size();
if (data->size() < fftSizeInternal) {
@@ -387,7 +421,7 @@ void SpectrumVisualProcessor::process() {
}
if (execute) {
- SpectrumVisualData *output = outputBuffers.getBuffer();
+ SpectrumVisualDataPtr output = outputBuffers.getBuffer();
if (output->spectrum_points.size() != fftSize * 2) {
output->spectrum_points.resize(fftSize * 2);
@@ -495,7 +529,7 @@ void SpectrumVisualProcessor::process() {
}
}
- float sf = scaleFactor.load();
+ float sf = scaleFactor;
double visualRatio = (double(bandwidth) / double(resampleBw));
double visualStart = (double(fftSizeInternal) / 2.0) - (double(fftSizeInternal) * (visualRatio / 2.0));
@@ -541,7 +575,7 @@ void SpectrumVisualProcessor::process() {
}
}
- if (hideDC.load()) { // DC-spike removal
+ if (hideDC) { // DC-spike removal
long long freqMin = centerFreq-(bandwidth/2);
long long freqMax = centerFreq+(bandwidth/2);
long long zeroPt = (iqData->frequency-freqMin);
@@ -597,21 +631,21 @@ void SpectrumVisualProcessor::process() {
distribute(output);
}
- }
-
- iqData->decRefCount();
-
+ }
- lastView = is_view.load();
+ lastView = is_view;
}
void SpectrumVisualProcessor::setScaleFactor(float sf) {
- scaleFactor.store(sf);
+ std::lock_guard < std::mutex > busy_lock(busy_run);
+
+ scaleFactor = sf;
}
float SpectrumVisualProcessor::getScaleFactor() {
- return scaleFactor.load();
+ std::lock_guard < std::mutex > busy_lock(busy_run);
+ return scaleFactor;
}
diff --git a/src/process/SpectrumVisualProcessor.h b/src/process/SpectrumVisualProcessor.h
index bef6490..f40069c 100644
--- a/src/process/SpectrumVisualProcessor.h
+++ b/src/process/SpectrumVisualProcessor.h
@@ -6,20 +6,25 @@
#include "VisualProcessor.h"
#include "DemodDefs.h"
#include <cmath>
+#include <memory>
#define SPECTRUM_VZM 2
#define PEAK_RESET_COUNT 30
-class SpectrumVisualData : public ReferenceCounter {
+class SpectrumVisualData {
public:
std::vector<float> spectrum_points;
std::vector<float> spectrum_hold_points;
double fft_ceiling, fft_floor;
long long centerFreq;
int bandwidth;
+
+ virtual ~SpectrumVisualData() {};
};
-typedef ThreadBlockingQueue<SpectrumVisualData *> SpectrumVisualDataQueue;
+typedef std::shared_ptr<SpectrumVisualData> SpectrumVisualDataPtr;
+typedef ThreadBlockingQueue<SpectrumVisualDataPtr> SpectrumVisualDataQueue;
+typedef std::shared_ptr<SpectrumVisualDataQueue> SpectrumVisualDataQueuePtr;
class SpectrumVisualProcessor : public VisualProcessor<DemodulatorThreadIQData, SpectrumVisualData> {
public:
@@ -56,13 +61,19 @@ protected:
virtual void process();
ReBuffer<SpectrumVisualData> outputBuffers;
- std::atomic_bool is_view;
- std::atomic_uint fftSize, newFFTSize;
- std::atomic_uint fftSizeInternal;
- std::atomic_llong centerFreq;
- std::atomic_long bandwidth;
+
private:
+ //protects all access to fields below
+ std::mutex busy_run;
+
+ bool is_view;
+ size_t fftSize, newFFTSize;
+ size_t fftSizeInternal;
+ long long centerFreq;
+ size_t bandwidth;
+
+
long lastInputBandwidth;
long lastBandwidth;
bool lastView;
@@ -75,7 +86,7 @@ private:
double fft_ceil_ma, fft_ceil_maa;
double fft_floor_ma, fft_floor_maa;
double fft_ceil_peak, fft_floor_peak;
- std::atomic<float> fft_average_rate;
+ float fft_average_rate;
std::vector<double> fft_result;
std::vector<double> fft_result_ma;
@@ -90,11 +101,11 @@ private:
std::vector<liquid_float_complex> shiftBuffer;
std::vector<liquid_float_complex> resampleBuffer;
- std::atomic_int desiredInputSize;
+ size_t desiredInputSize;
- std::mutex busy_run;
- std::atomic_bool hideDC, peakHold;
- std::atomic_int peakReset;
- std::atomic<float> scaleFactor;
- std::atomic_bool fftSizeChanged;
+
+ bool hideDC, peakHold;
+ int peakReset;
+ float scaleFactor;
+ bool fftSizeChanged;
};
diff --git a/src/process/VisualProcessor.h b/src/process/VisualProcessor.h
index 7a9ee70..94bb9c7 100644
--- a/src/process/VisualProcessor.h
+++ b/src/process/VisualProcessor.h
@@ -8,29 +8,40 @@
#include "IOThread.h"
#include <algorithm>
#include <vector>
+#include <typeinfo>
-template<typename InputDataType = ReferenceCounter, typename OutputDataType = ReferenceCounter>
+template<typename InputDataType, typename OutputDataType>
class VisualProcessor {
- //
- typedef ThreadBlockingQueue<InputDataType*> VisualInputQueueType;
- typedef ThreadBlockingQueue<OutputDataType*> VisualOutputQueueType;
- typedef typename std::vector< VisualOutputQueueType *>::iterator outputs_i;
+
public:
- virtual ~VisualProcessor() {
+ //
+ typedef typename std::shared_ptr<InputDataType> InputDataTypePtr;
+ typedef typename std::shared_ptr<OutputDataType> OutputDataTypePtr;
+
+ typedef ThreadBlockingQueue<InputDataTypePtr> VisualInputQueueType;
+ typedef ThreadBlockingQueue<OutputDataTypePtr> VisualOutputQueueType;
+ typedef std::shared_ptr<VisualInputQueueType> VisualInputQueueTypePtr;
+ typedef std::shared_ptr<VisualOutputQueueType> VisualOutputQueueTypePtr;
+
+ virtual ~VisualProcessor() {
}
bool isInputEmpty() {
- std::lock_guard < std::recursive_mutex > busy_lock(busy_update);
+ std::lock_guard < std::mutex > busy_lock(busy_update);
+
+ if (input) {
+ return input->empty();
+ }
- return input->empty();
+ return true;
}
bool isOutputEmpty() {
- std::lock_guard < std::recursive_mutex > busy_lock(busy_update);
+ std::lock_guard < std::mutex > busy_lock(busy_update);
- for (outputs_i it = outputs.begin(); it != outputs.end(); it++) {
- if ((*it)->full()) {
+ for (VisualOutputQueueTypePtr single_output : outputs) {
+ if (single_output->full()) {
return false;
}
}
@@ -38,10 +49,10 @@ public:
}
bool isAnyOutputEmpty() {
- std::lock_guard < std::recursive_mutex > busy_lock(busy_update);
+ std::lock_guard < std::mutex > busy_lock(busy_update);
- for (outputs_i it = outputs.begin(); it != outputs.end(); it++) {
- if (!(*it)->full()) {
+ for (VisualOutputQueueTypePtr single_output : outputs) {
+ if (!(single_output)->full()) {
return true;
}
}
@@ -49,45 +60,68 @@ public:
}
//Set a (new) 'input' queue for incoming data.
- void setInput(VisualInputQueueType *vis_in) {
- std::lock_guard < std::recursive_mutex > busy_lock(busy_update);
+ void setInput(VisualInputQueueTypePtr vis_in) {
+ std::lock_guard < std::mutex > busy_lock(busy_update);
input = vis_in;
}
//Add a vis_out queue where to consumed 'input' data will be
//dispatched by distribute().
- void attachOutput(VisualOutputQueueType *vis_out) {
+ void attachOutput(VisualOutputQueueTypePtr vis_out) {
// attach an output queue
- std::lock_guard < std::recursive_mutex > busy_lock(busy_update);
+ std::lock_guard < std::mutex > busy_lock(busy_update);
outputs.push_back(vis_out);
}
//reverse of attachOutput(), removed an existing attached vis_out.
- void removeOutput(VisualOutputQueueType *vis_out) {
+ void removeOutput(VisualOutputQueueTypePtr vis_out) {
// remove an output queue
- std::lock_guard < std::recursive_mutex > busy_lock(busy_update);
+ std::lock_guard < std::mutex > busy_lock(busy_update);
- outputs_i i = std::find(outputs.begin(), outputs.end(), vis_out);
- if (i != outputs.end()) {
- outputs.erase(i);
+ auto it = std::find(outputs.begin(), outputs.end(), vis_out);
+ if (it != outputs.end()) {
+ outputs.erase(it);
+ }
+ }
+ //Flush all queues, either input or outputs clearing their accumulated messages.
+ //this is purposefully (almost) non-blocking call.
+ void flushQueues() {
+
+ //capture a local copy atomically, so we don't need to protect input.
+ VisualInputQueueTypePtr localInput = input;
+
+ if (localInput) {
+ localInput->flush();
+ }
+
+ //scoped-lock: create a local copy of outputs, and work with it.
+ std::vector<VisualOutputQueueTypePtr> local_outputs;
+ {
+ std::lock_guard < std::mutex > busy_lock(busy_update);
+ local_outputs = outputs;
+ }
+
+ for (auto single_output : local_outputs) {
+
+ single_output->flush();
}
}
//Call process() repeateadly until all available 'input' data is consumed.
void run() {
-
- std::lock_guard < std::recursive_mutex > busy_lock(busy_update);
+
+ //capture a local copy atomically, so we don't need to protect input.
+ VisualInputQueueTypePtr localInput = input;
- if (input && !input->empty()) {
+ if (localInput && !localInput->empty()) {
process();
- }
-
+ }
}
protected:
// derived class must implement a process() interface
- //where typically 'input' data is consummed, procerssed, and then dispatched
+ //where typically 'input' data is consummed, processed, and then dispatched
//with distribute() to all 'outputs'.
virtual void process() = 0;
@@ -96,58 +130,53 @@ protected:
//available outputs, previously set by attachOutput().
//* \param[in] timeout The number of microseconds to wait to push an item in each one of the outputs, 0(default) means indefinite wait.
//* \param[in] errorMessage an error message written on std::cout in case pf push timeout.
- void distribute(OutputDataType *item, std::uint64_t timeout = BLOCKING_INFINITE_TIMEOUT, const char* errorMessage = "") {
+ void distribute(OutputDataTypePtr item, std::uint64_t timeout = BLOCKING_INFINITE_TIMEOUT, const char* errorMessage = nullptr) {
- std::lock_guard < std::recursive_mutex > busy_lock(busy_update);
+ std::lock_guard < std::mutex > busy_lock(busy_update);
//We will try to distribute 'output' among all 'outputs',
- //so 'output' will a-priori be shared among all 'outputs' so set its ref count to this
- //amount.
- item->setRefCount((int)outputs.size());
- for (outputs_i it = outputs.begin(); it != outputs.end(); it++) {
- //if 'output' failed to be given to an outputs_i, dec its ref count accordingly.
- //blocking push, with a timeout
- if (!(*it)->push(item, timeout, errorMessage)) {
- item->decRefCount();
+ //so 'output' will a-priori be shared among all 'outputs'.
+
+ for (VisualOutputQueueTypePtr single_output : outputs) {
+ //'output' can fail to be given to an single_output,
+ //using a blocking push, with a timeout
+ if (!(single_output)->push(item, timeout, errorMessage)) {
+ //trace will be std::output if timeout != 0 is set and errorMessage != null.
}
}
- // Now 'item' refcount matches the times 'item' has been successfully distributed,
- //i.e shared among the outputs.
}
//the incoming data queue
- VisualInputQueueType *input = nullptr;
+ VisualInputQueueTypePtr input;
//the n-outputs where to process()-ed data is distribute()-ed.
- std::vector<VisualOutputQueueType *> outputs;
+ std::vector<VisualOutputQueueTypePtr> outputs;
- //protects input and outputs, must be recursive because of re-entrance
- std::recursive_mutex busy_update;
+ //protects input and outputs
+ std::mutex busy_update;
};
//Specialization much like VisualDataReDistributor, except
//the input (pointer) is directly re-dispatched
//to outputs, so that all output indeed SHARE the same instance.
-template<typename OutputDataType = ReferenceCounter>
+template<typename OutputDataType>
class VisualDataDistributor : public VisualProcessor<OutputDataType, OutputDataType> {
protected:
+
virtual void process() {
- OutputDataType *inp;
+
+ typename VisualProcessor<OutputDataType, OutputDataType>::OutputDataTypePtr inp;
+
while (VisualProcessor<OutputDataType, OutputDataType>::input->try_pop(inp)) {
+ //do not try to distribute if all outputs are already full.
if (!VisualProcessor<OutputDataType, OutputDataType>::isAnyOutputEmpty()) {
- if (inp) {
- inp->decRefCount();
- }
+
return;
}
if (inp) {
- int previousRefCount = inp->getRefCount();
VisualProcessor<OutputDataType, OutputDataType>::distribute(inp);
- //inp is now shared through the distribute(), which overwrite the previous ref count,
- //so increment it properly.
- int distributeRefCount = inp->getRefCount();
- inp->setRefCount(previousRefCount + distributeRefCount);
+ //inp is now shared through the distribute() call.
}
}
}
@@ -155,27 +184,39 @@ protected:
//specialization class which process() take an input item and re-dispatch
//A COPY to every outputs, without further processing. This is a 1-to-n dispatcher.
-template<typename OutputDataType = ReferenceCounter>
+template<typename OutputDataType>
class VisualDataReDistributor : public VisualProcessor<OutputDataType, OutputDataType> {
+
protected:
+
+ VisualDataReDistributor() : buffers (std::string(typeid(*this).name())) {
+
+ }
+
+ ReBuffer<OutputDataType> buffers;
+
virtual void process() {
- OutputDataType *inp;
+
+ typename VisualProcessor<OutputDataType, OutputDataType>::OutputDataTypePtr inp;
+
while (VisualProcessor<OutputDataType, OutputDataType>::input->try_pop(inp)) {
+ //do not try to distribute if all outputs are already full.
if (!VisualProcessor<OutputDataType, OutputDataType>::isAnyOutputEmpty()) {
- if (inp) {
- inp->decRefCount();
- }
+
return;
}
if (inp) {
- OutputDataType *outp = buffers.getBuffer();
+
+ typename VisualProcessor<OutputDataType, OutputDataType>::OutputDataTypePtr outp = buffers.getBuffer();
+
+ //'deep copy' of the contents
(*outp) = (*inp);
- inp->decRefCount();
+
VisualProcessor<OutputDataType, OutputDataType>::distribute(outp);
}
}
}
- ReBuffer<OutputDataType> buffers;
+
};
diff --git a/src/rig/RigThread.cpp b/src/rig/RigThread.cpp
index 7bdcf4b..29ba0aa 100644
--- a/src/rig/RigThread.cpp
+++ b/src/rig/RigThread.cpp
@@ -73,8 +73,8 @@ void RigThread::run() {
while (!stopping) {
std::this_thread::sleep_for(std::chrono::milliseconds(150));
- DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator();
- DemodulatorInstance *lastDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
+ auto activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator();
+ auto lastDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
if (freqChanged.load() && (controlMode.load() || setOneShot.load())) {
status = rig_get_freq(rig, RIG_VFO_CURR, &freq);
diff --git a/src/sdr/SDRDeviceInfo.cpp b/src/sdr/SDRDeviceInfo.cpp
index e79b77d..2dbaf03 100644
--- a/src/sdr/SDRDeviceInfo.cpp
+++ b/src/sdr/SDRDeviceInfo.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0+
#include "SDRDeviceInfo.h"
+#include "CubicSDRDefs.h"
#include <cstdlib>
#include <algorithm>
@@ -179,15 +180,64 @@ bool SDRDeviceInfo::hasCORR(int direction, size_t channel) {
}
std::vector<long> SDRDeviceInfo::getSampleRates(int direction, size_t channel) {
+
SoapySDR::Device *dev = getSoapyDevice();
+
+ size_t nbMaxDifferentRates = DEVICE_SAMPLE_RATES_MAX_NB;
std::vector<long> result;
+
+ //the original list returned from the driver:
std::vector<double> sampleRates = dev->listSampleRates(direction, channel);
- for (std::vector<double>::iterator si = sampleRates.begin(); si != sampleRates.end(); si++) {
- result.push_back((long)(*si));
+
+ //be paranoid, sort by increasing rates...
+ std::sort(sampleRates.begin(), sampleRates.end(), [](double a, double b) -> bool { return a < b; });
+
+ //if sampleRates.size() > nbMaxDifferentRates, decimate this number to only return nbMaxDifferentRates sample
+ //rates values.
+ size_t sampleRateSelectionStep = 1;
+
+ if (sampleRates.size() / nbMaxDifferentRates >= 2) {
+
+ sampleRateSelectionStep = sampleRates.size() / nbMaxDifferentRates;
+ }
+
+ for (size_t i = 0; sampleRateSelectionStep * i < sampleRates.size(); i++) {
+
+ //convert to longs...
+ result.push_back((long)sampleRates[sampleRateSelectionStep * i]);
+ }
+
+ //always include the biggest value:
+ if ((long)sampleRates.back() > result.back()) {
+
+ result.push_back((long)sampleRates.back());
+ }
+
+ return result;
+}
+
+std::vector<std::string> SDRDeviceInfo::getAntennaNames(int direction, size_t channel) {
+
+ SoapySDR::Device *dev = getSoapyDevice();
+
+ if (dev) {
+
+ return dev->listAntennas(direction, channel);
}
+
+ return std::vector<std::string>();
+}
+
+std::string SDRDeviceInfo::getAntennaName(int direction, size_t channel) {
+ SoapySDR::Device *dev = getSoapyDevice();
- return result;
+ if (dev) {
+ return dev->getAntenna(direction, channel);
+ }
+
+ return std::string("");
+
}
long SDRDeviceInfo::getSampleRateNear(int direction, size_t channel, long sampleRate_in) {
@@ -195,11 +245,11 @@ long SDRDeviceInfo::getSampleRateNear(int direction, size_t channel, long sample
long returnRate = sampleRates[0];
long sDelta = (long)sampleRate_in-sampleRates[0];
long minDelta = std::abs(sDelta);
- for (std::vector<long>::iterator i = sampleRates.begin(); i != sampleRates.end(); i++) {
- long thisDelta = std::abs(sampleRate_in - (*i));
+ for (long i : sampleRates) {
+ long thisDelta = std::abs(sampleRate_in - i);
if (thisDelta < minDelta) {
minDelta = thisDelta;
- returnRate = (*i);
+ returnRate = i;
}
}
return returnRate;
@@ -207,12 +257,36 @@ long SDRDeviceInfo::getSampleRateNear(int direction, size_t channel, long sample
SDRRangeMap SDRDeviceInfo::getGains(int direction, size_t channel) {
SoapySDR::Device *dev = getSoapyDevice();
- std::vector<std::string> gainNames = dev->listGains(direction, channel);
- std::map<std::string, SoapySDR::Range> gainMap;
- for (std::vector<std::string>::iterator gname = gainNames.begin(); gname!= gainNames.end(); gname++) {
- gainMap[(*gname)] = dev->getGainRange(direction, channel, (*gname));
+ std::vector<std::string> gainNames = dev->listGains(direction, channel);
+
+ std::map<std::string, SoapySDR::Range> gainMap;
+
+ for (std::string gname : gainNames) {
+
+ gainMap[gname] = dev->getGainRange(direction, channel, gname);
}
return gainMap;
}
+
+//read the current gain of name gainName (must exit in getGains(), else return 0)
+//in the device.
+double SDRDeviceInfo::getCurrentGain(int direction, size_t channel, const std::string& gainName) {
+
+ SoapySDR::Device *dev = getSoapyDevice();
+
+ if (dev) {
+
+ std::vector<std::string> gainNames = dev->listGains(direction, channel);
+
+ auto itFoundName = std::find(gainNames.begin(), gainNames.end(), gainName);
+
+ if (itFoundName != gainNames.end()) {
+
+ return dev->getGain(direction, channel, gainName);
+ }
+ }
+
+ return 0.0;
+}
diff --git a/src/sdr/SDRDeviceInfo.h b/src/sdr/SDRDeviceInfo.h
index cc903c1..622d56f 100644
--- a/src/sdr/SDRDeviceInfo.h
+++ b/src/sdr/SDRDeviceInfo.h
@@ -83,11 +83,19 @@ public:
bool hasCORR(int direction, size_t channel);
std::vector<long> getSampleRates(int direction, size_t channel);
+
+ std::vector<std::string> getAntennaNames(int direction, size_t channel);
+
+ std::string getAntennaName(int direction, size_t channel);
long getSampleRateNear(int direction, size_t channel, long sampleRate_in);
SDRRangeMap getGains(int direction, size_t channel);
+ //read the current gain of name gainName (must exist in getGains(), else return 0)
+ //in the device.
+ double getCurrentGain(int direction, size_t channel, const std::string& gainName);
+
private:
int index = 0;
std::string name, serial, product, manufacturer, tuner;
diff --git a/src/sdr/SDREnumerator.cpp b/src/sdr/SDREnumerator.cpp
index a7680c0..2feff22 100644
--- a/src/sdr/SDREnumerator.cpp
+++ b/src/sdr/SDREnumerator.cpp
@@ -90,22 +90,22 @@ std::vector<SDRDeviceInfo *> *SDREnumerator::enumerate_devices(std::string remot
}
if (!soapy_initialized) {
- std::cout << "SoapySDR init.." << std::endl;
- std::cout << "\tAPI Version: v" << SoapySDR::getAPIVersion() << std::endl;
- std::cout << "\tABI Version: v" << SoapySDR::getABIVersion() << std::endl;
- std::cout << "\tInstall root: " << SoapySDR::getRootPath() << std::endl;
+ std::cout << "SoapySDR init.." << std::endl << std::flush;
+ std::cout << "\tAPI Version: v" << SoapySDR::getAPIVersion() << std::endl << std::flush;
+ std::cout << "\tABI Version: v" << SoapySDR::getABIVersion() << std::endl << std::flush;
+ std::cout << "\tInstall root: " << SoapySDR::getRootPath() << std::endl << std::flush;
- std::cout << "\tLoading modules... " << std::endl;
+ std::cout << "\tLoading modules... " << std::endl << std::flush;
std::string userModPath = wxGetApp().getModulePath();
if (userModPath != "") {
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Loading SoapySDR modules from " + userModPath + "..");
std::vector<std::string> localMods = SoapySDR::listModules(userModPath);
- for (std::vector<std::string>::iterator mods_i = localMods.begin(); mods_i != localMods.end(); mods_i++) {
- wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Initializing user specified SoapySDR module " + (*mods_i) + "..");
- std::cout << "Initializing user specified SoapySDR module " << (*mods_i) << ".." << std::endl;
- SoapySDR::loadModule(*mods_i);
+ for (std::string mod : localMods) {
+ wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Initializing user specified SoapySDR module " + (mod) + "..");
+ std::cout << "Initializing user specified SoapySDR module " << (mod) << ".." << std::endl << std::flush;
+ SoapySDR::loadModule(mod);
}
} else {
#ifdef BUNDLE_SOAPY_MODS
@@ -114,28 +114,28 @@ std::vector<SDRDeviceInfo *> *SDREnumerator::enumerate_devices(std::string remot
std::vector<std::string> localMods = SoapySDR::listModules(exePath.GetPath().ToStdString() + "/modules/");
for (std::vector<std::string>::iterator mods_i = localMods.begin(); mods_i != localMods.end(); mods_i++) {
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Initializing bundled SoapySDR module " + (*mods_i) + "..");
- std::cout << "Loading bundled SoapySDR module " << (*mods_i) << ".." << std::endl;
+ std::cout << "Loading bundled SoapySDR module " << (*mods_i) << ".." << std::endl << std::flush;
SoapySDR::loadModule(*mods_i);
}
#else
bool localModPref = wxGetApp().getUseLocalMod();
if (localModPref) {
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Loading SoapySDR modules..");
- std::cout << "Checking local system SoapySDR modules.." << std::flush;
+ std::cout << "Checking local system SoapySDR modules.." << std::endl << std::flush;
SoapySDR::loadModules();
}
wxFileName exePath = wxFileName(wxStandardPaths::Get().GetExecutablePath());
std::vector<std::string> localMods = SoapySDR::listModules(exePath.GetPath().ToStdString() + "/modules/");
- for (std::vector<std::string>::iterator mods_i = localMods.begin(); mods_i != localMods.end(); mods_i++) {
- wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Initializing bundled SoapySDR module " + (*mods_i) + "..");
- std::cout << "Loading bundled SoapySDR module " << (*mods_i) << ".." << std::endl;
- SoapySDR::loadModule(*mods_i);
+ for (std::string mod : localMods) {
+ wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Initializing bundled SoapySDR module " + (mod) + "..");
+ std::cout << "Loading bundled SoapySDR module " << (mod) << ".." << std::endl << std::flush;
+ SoapySDR::loadModule(mod);
}
if (!localModPref) {
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Loading SoapySDR modules..");
- std::cout << "Checking system SoapySDR modules.." << std::flush;
+ std::cout << "Checking system SoapySDR modules.." << std::endl << std::flush;
SoapySDR::loadModules();
}
#endif
@@ -146,9 +146,8 @@ std::vector<SDRDeviceInfo *> *SDREnumerator::enumerate_devices(std::string remot
}
- if (SDREnumerator::factories.size()) {
- SDREnumerator::factories.erase(SDREnumerator::factories.begin(), SDREnumerator::factories.end());
- }
+ SDREnumerator::factories.clear();
+
std::cout << "\tAvailable factories...";
SoapySDR::FindFunctions factories = SoapySDR::Registry::listFindFunctions();
@@ -275,6 +274,7 @@ std::vector<SDRDeviceInfo *> *SDREnumerator::enumerate_devices(std::string remot
ConfigSettings devSettings = cfg->getSettings();
if (devSettings.size()) {
+ // Load the saved device settings to deviceArgs, and back to settingsInfo.
for (ConfigSettings::const_iterator set_i = devSettings.begin(); set_i != devSettings.end(); set_i++) {
deviceArgs[set_i->first] = set_i->second;
}
@@ -318,10 +318,10 @@ void SDREnumerator::run() {
SDREnumerator::enumerate_devices("");
if (remotes.size()) {
- std::vector<std::string>::iterator remote_i;
- for (remote_i = remotes.begin(); remote_i != remotes.end(); remote_i++) {
- wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Scanning devices at " + (*remote_i) + ", please wait..");
- SDREnumerator::enumerate_devices(*remote_i);
+
+ for (std::string remote : remotes) {
+ wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Scanning devices at " + (remote) + ", please wait..");
+ SDREnumerator::enumerate_devices(remote);
}
}
@@ -400,15 +400,17 @@ bool SDREnumerator::hasRemoteModule() {
void SDREnumerator::reset() {
soapy_initialized = false;
- factories.erase(factories.begin(), factories.end());
- modules.erase(modules.begin(), modules.end());
+ factories.clear();
+ modules.clear();
+
for (std::map< std::string, std::vector<SDRDeviceInfo *> >::iterator di = devs.begin(); di != devs.end(); di++) {
+
for (std::vector<SDRDeviceInfo *>::iterator i = di->second.begin(); i != di->second.end(); i++) {
(*i)->setSoapyDevice(nullptr);
}
}
- devs.erase(devs.begin(), devs.end());
+ devs.clear();
}
std::vector<std::string> &SDREnumerator::getFactories() {
diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp
index afefd60..42919b2 100644
--- a/src/sdr/SDRPostThread.cpp
+++ b/src/sdr/SDRPostThread.cpp
@@ -7,17 +7,20 @@
#include <vector>
#include <deque>
+#include <memory>
+
+//50 ms
+#define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000)
SDRPostThread::SDRPostThread() : IOThread(), buffers("SDRPostThreadBuffers"), visualDataBuffers("SDRPostThreadVisualDataBuffers"), frequency(0) {
- iqDataInQueue = NULL;
- iqDataOutQueue = NULL;
- iqVisualQueue = NULL;
+ iqDataInQueue = nullptr;
+ iqDataOutQueue = nullptr;
+ iqVisualQueue = nullptr;
numChannels = 0;
- channelizer = NULL;
+ channelizer = nullptr;
sampleRate = 0;
- nRunDemods = 0;
visFrequency.store(0);
visBandwidth.store(0);
@@ -29,44 +32,12 @@ SDRPostThread::SDRPostThread() : IOThread(), buffers("SDRPostThreadBuffers"), vi
SDRPostThread::~SDRPostThread() {
}
-void SDRPostThread::bindDemodulator(DemodulatorInstance *demod) {
-
- std::lock_guard < std::mutex > lock(busy_demod);
-
- demodulators.push_back(demod);
+void SDRPostThread::notifyDemodulatorsChanged() {
+
doRefresh.store(true);
}
-void SDRPostThread::bindDemodulators(std::vector<DemodulatorInstance *> *demods) {
- if (!demods) {
- return;
- }
- std::lock_guard < std::mutex > lock(busy_demod);
-
- for (std::vector<DemodulatorInstance *>::iterator di = demods->begin(); di != demods->end(); di++) {
- demodulators.push_back(*di);
- doRefresh.store(true);
- }
-
-}
-
-void SDRPostThread::removeDemodulator(DemodulatorInstance *demod) {
- if (!demod) {
- return;
- }
-
- std::lock_guard < std::mutex > lock(busy_demod);
-
- std::vector<DemodulatorInstance *>::iterator i = std::find(demodulators.begin(), demodulators.end(), demod);
-
- if (i != demodulators.end()) {
- demodulators.erase(i);
- doRefresh.store(true);
- }
-
-}
-
void SDRPostThread::initPFBChannelizer() {
// std::cout << "Initializing post-process FIR polyphase filterbank channelizer with " << numChannels << " channels." << std::endl;
if (channelizer) {
@@ -84,16 +55,17 @@ void SDRPostThread::initPFBChannelizer() {
void SDRPostThread::updateActiveDemodulators() {
// In range?
- std::vector<DemodulatorInstance *>::iterator demod_i;
-
- nRunDemods = 0;
-
+
+ runDemods.clear();
+ demodChannel.clear();
+
long long centerFreq = wxGetApp().getFrequency();
- for (demod_i = demodulators.begin(); demod_i != demodulators.end(); demod_i++) {
- DemodulatorInstance *demod = *demod_i;
- DemodulatorThreadInputQueue *demodQueue = demod->getIQInputDataPipe();
-
+ //retreive the current list of demodulators:
+ auto demodulators = wxGetApp().getDemodMgr().getDemodulators();
+
+ for (auto demod : demodulators) {
+
// not in range?
if (demod->isDeltaLock()) {
if (demod->getFrequency() != centerFreq + demod->getDeltaLockOfs()) {
@@ -106,9 +78,14 @@ void SDRPostThread::updateActiveDemodulators() {
if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) {
// deactivate if active
- if (demod->isActive() && !demod->isFollow() && !demod->isTracking()) {
+
+ if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == demod) {
+
demod->setActive(false);
}
+ else if (demod->isActive() && !demod->isFollow() && !demod->isTracking()) {
+ demod->setActive(false);
+ }
// follow if follow mode
if (demod->isFollow() && centerFreq != demod->getFrequency()) {
@@ -117,7 +94,7 @@ void SDRPostThread::updateActiveDemodulators() {
}
} else if (!demod->isActive()) { // in range, activate if not activated
demod->setActive(true);
- if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == NULL) {
+ if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == nullptr) {
wxGetApp().getDemodMgr().setActiveDemodulator(demod);
}
@@ -127,16 +104,25 @@ void SDRPostThread::updateActiveDemodulators() {
continue;
}
- // Add to the current run
- if (nRunDemods == runDemods.size()) {
- runDemods.push_back(demod);
- demodChannel.push_back(-1);
- } else {
- runDemods[nRunDemods] = demod;
- demodChannel[nRunDemods] = -1;
- }
- nRunDemods++;
+ // Add active demods to the current run:
+
+ runDemods.push_back(demod);
+ demodChannel.push_back(-1);
+ }
+}
+
+void SDRPostThread::resetAllDemodulators() {
+
+ //retreive the current list of demodulators:
+ auto demodulators = wxGetApp().getDemodMgr().getDemodulators();
+
+ for (auto demod : demodulators) {
+
+ demod->setActive(false);
+ demod->getIQInputDataPipe()->flush();
}
+
+ doRefresh = true;
}
void SDRPostThread::updateChannels() {
@@ -177,62 +163,57 @@ void SDRPostThread::run() {
// std::cout << "SDR post-processing thread started.." << std::endl;
- iqDataInQueue = static_cast<SDRThreadIQDataQueue*>(getInputQueue("IQDataInput"));
- iqDataOutQueue = static_cast<DemodulatorThreadInputQueue*>(getOutputQueue("IQDataOutput"));
- iqVisualQueue = static_cast<DemodulatorThreadInputQueue*>(getOutputQueue("IQVisualDataOutput"));
- iqActiveDemodVisualQueue = static_cast<DemodulatorThreadInputQueue*>(getOutputQueue("IQActiveDemodVisualDataOutput"));
+ iqDataInQueue = std::static_pointer_cast<SDRThreadIQDataQueue>(getInputQueue("IQDataInput"));
+ iqDataOutQueue = std::static_pointer_cast<DemodulatorThreadInputQueue>(getOutputQueue("IQDataOutput"));
+ iqVisualQueue = std::static_pointer_cast<DemodulatorThreadInputQueue>(getOutputQueue("IQVisualDataOutput"));
+ iqActiveDemodVisualQueue = std::static_pointer_cast<DemodulatorThreadInputQueue>(getOutputQueue("IQActiveDemodVisualDataOutput"));
while (!stopping) {
- SDRThreadIQData *data_in;
+ SDRThreadIQDataPtr data_in;
- iqDataInQueue->pop(data_in);
- // std::lock_guard < std::mutex > lock(data_in->m_mutex);
-
- std::lock_guard < std::mutex > lock(busy_demod);
+ if (!iqDataInQueue->pop(data_in, HEARTBEAT_CHECK_PERIOD_MICROS)) {
+ continue;
+ }
+
+ bool doUpdate = false;
if (data_in && data_in->data.size()) {
if(data_in->numChannels > 1) {
- runPFBCH(data_in);
+ runPFBCH(data_in.get());
} else {
- runSingleCH(data_in);
+ runSingleCH(data_in.get());
}
}
-
- if (data_in) {
- data_in->decRefCount();
- }
-
- bool doUpdate = false;
- for (size_t j = 0; j < nRunDemods; j++) {
- DemodulatorInstance *demod = runDemods[j];
+
+ for (size_t j = 0; j < runDemods.size(); j++) {
+ DemodulatorInstancePtr demod = runDemods[j];
if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) {
doUpdate = true;
}
}
//Only update the list of demodulators here
- if (doUpdate) {
+ if (doUpdate || doRefresh) {
updateActiveDemodulators();
}
} //end while
//Be safe, remove as many elements as possible
- DemodulatorThreadIQData *visualDataDummy;
- while (iqVisualQueue && iqVisualQueue->try_pop(visualDataDummy)) {
- visualDataDummy->decRefCount();
- }
-
- // buffers.purge();
- // visualDataBuffers.purge();
+ iqVisualQueue->flush();
+ iqDataInQueue->flush();
+ iqDataOutQueue->flush();
+ iqActiveDemodVisualQueue->flush();
// std::cout << "SDR post-processing thread done." << std::endl;
}
void SDRPostThread::terminate() {
IOThread::terminate();
- SDRThreadIQData *dummy = new SDRThreadIQData;
- //VSO: blocking push
- iqDataInQueue->push(dummy);
+ //unblock push()
+ iqVisualQueue->flush();
+ iqDataInQueue->flush();
+ iqDataOutQueue->flush();
+ iqActiveDemodVisualQueue->flush();
}
void SDRPostThread::runSingleCH(SDRThreadIQData *data_in) {
@@ -262,10 +243,10 @@ void SDRPostThread::runSingleCH(SDRThreadIQData *data_in) {
doRefresh.store(false);
}
- size_t refCount = nRunDemods;
- bool doIQDataOut = (iqDataOutQueue != NULL && !iqDataOutQueue->full());
- bool doDemodVisOut = (nRunDemods && iqActiveDemodVisualQueue != NULL && !iqActiveDemodVisualQueue->full());
- bool doVisOut = (iqVisualQueue != NULL && !iqVisualQueue->full());
+ size_t refCount = runDemods.size();
+ bool doIQDataOut = (iqDataOutQueue != nullptr && !iqDataOutQueue->full());
+ bool doDemodVisOut = (runDemods.size() > 0 && iqActiveDemodVisualQueue != nullptr && !iqActiveDemodVisualQueue->full());
+ bool doVisOut = (iqVisualQueue != nullptr && !iqVisualQueue->full());
if (doIQDataOut) {
refCount++;
@@ -278,8 +259,8 @@ void SDRPostThread::runSingleCH(SDRThreadIQData *data_in) {
}
if (refCount) {
- DemodulatorThreadIQData *demodDataOut = buffers.getBuffer();
- demodDataOut->setRefCount(refCount);
+ DemodulatorThreadIQDataPtr demodDataOut = buffers.getBuffer();
+
demodDataOut->frequency = frequency;
demodDataOut->sampleRate = sampleRate;
@@ -307,9 +288,15 @@ void SDRPostThread::runSingleCH(SDRThreadIQData *data_in) {
iqVisualQueue->push(demodDataOut);
}
- for (size_t i = 0; i < nRunDemods; i++) {
- //VSO: blocking push
- runDemods[i]->getIQInputDataPipe()->push(demodDataOut);
+ for (size_t i = 0; i < runDemods.size(); i++) {
+ // try-push() : we do our best to only stimulate active demods, but some could happen to be dead, full, or indeed non-active.
+ //so in short never block here no matter what.
+ if (!runDemods[i]->getIQInputDataPipe()->try_push(demodDataOut)) {
+
+ // std::cout << "SDRPostThread::runSingleCH() attempt to push into demod '" << runDemods[i]->getLabel()
+ // << "' (" << runDemods[i]->getFrequency() << " Hz) failed, demod is either too busy, not-active, or dead..." << std::endl << std::flush;
+ std::this_thread::yield();
+ }
}
}
}
@@ -332,17 +319,15 @@ void SDRPostThread::runPFBCH(SDRThreadIQData *data_in) {
dataOut.resize(outSize);
}
- if (iqDataOutQueue != NULL && !iqDataOutQueue->full()) {
- DemodulatorThreadIQData *iqDataOut = visualDataBuffers.getBuffer();
+ if (iqDataOutQueue != nullptr && !iqDataOutQueue->full()) {
+ DemodulatorThreadIQDataPtr iqDataOut = visualDataBuffers.getBuffer();
bool doVis = false;
- if (iqVisualQueue != NULL && !iqVisualQueue->full()) {
+ if (iqVisualQueue != nullptr && !iqVisualQueue->full()) {
doVis = true;
}
- iqDataOut->setRefCount(1 + (doVis?1:0));
-
iqDataOut->frequency = data_in->frequency;
iqDataOut->sampleRate = data_in->sampleRate;
iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + dataSize);
@@ -367,11 +352,11 @@ void SDRPostThread::runPFBCH(SDRThreadIQData *data_in) {
doRefresh.store(false);
}
- DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
+ DemodulatorInstancePtr activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
int activeDemodChannel = -1;
// Find active demodulators
- if (nRunDemods) {
+ if (runDemods.size() > 0) {
// channelize data
// firpfbch output rate is (input rate / channels)
@@ -384,15 +369,15 @@ void SDRPostThread::runPFBCH(SDRThreadIQData *data_in) {
}
// Find nearest channel for each demodulator
- for (size_t i = 0; i < nRunDemods; i++) {
- DemodulatorInstance *demod = runDemods[i];
+ for (size_t i = 0; i < runDemods.size(); i++) {
+ DemodulatorInstancePtr demod = runDemods[i];
demodChannel[i] = getChannelAt(demod->getFrequency());
if (demod == activeDemod) {
activeDemodChannel = demodChannel[i];
}
}
- for (size_t i = 0; i < nRunDemods; i++) {
+ for (size_t i = 0; i < runDemods.size(); i++) {
// cache channel usage refcounts
if (demodChannel[i] >= 0) {
demodChannelActive[demodChannel[i]]++;
@@ -401,14 +386,13 @@ void SDRPostThread::runPFBCH(SDRThreadIQData *data_in) {
// Run channels
for (int i = 0; i < numChannels+1; i++) {
- int doDemodVis = ((activeDemodChannel == i) && (iqActiveDemodVisualQueue != NULL) && !iqActiveDemodVisualQueue->full())?1:0;
+ int doDemodVis = ((activeDemodChannel == i) && (iqActiveDemodVisualQueue != nullptr) && !iqActiveDemodVisualQueue->full())?1:0;
if (!doDemodVis && demodChannelActive[i] == 0) {
continue;
}
- DemodulatorThreadIQData *demodDataOut = buffers.getBuffer();
- demodDataOut->setRefCount(demodChannelActive[i] + doDemodVis);
+ DemodulatorThreadIQDataPtr demodDataOut = buffers.getBuffer();
demodDataOut->frequency = chanCenters[i];
demodDataOut->sampleRate = chanBw;
@@ -452,13 +436,18 @@ void SDRPostThread::runPFBCH(SDRThreadIQData *data_in) {
iqActiveDemodVisualQueue->push(demodDataOut);
}
- for (size_t j = 0; j < nRunDemods; j++) {
+ for (size_t j = 0; j < runDemods.size(); j++) {
if (demodChannel[j] == i) {
- DemodulatorInstance *demod = runDemods[j];
- //VSO: blocking push
- demod->getIQInputDataPipe()->push(demodDataOut);
+
+ // try-push() : we do our best to only stimulate active demods, but some could happen to be dead, full, or indeed non-active.
+ //so in short never block here no matter what.
+ if (!runDemods[j]->getIQInputDataPipe()->try_push(demodDataOut)) {
+ // std::cout << "SDRPostThread::runPFBCH() attempt to push into demod '" << runDemods[i]->getLabel()
+ // << "' (" << runDemods[i]->getFrequency() << " Hz) failed, demod is either too busy, not-active, or dead..." << std::endl << std::flush;
+ std::this_thread::yield();
+ }
}
- }
+ } //end for
}
}
}
diff --git a/src/sdr/SDRPostThread.h b/src/sdr/SDRPostThread.h
index 2f7ad3e..2725901 100644
--- a/src/sdr/SDRPostThread.h
+++ b/src/sdr/SDRPostThread.h
@@ -11,10 +11,8 @@ public:
SDRPostThread();
~SDRPostThread();
- void bindDemodulator(DemodulatorInstance *demod);
- void bindDemodulators(std::vector<DemodulatorInstance *> *demods);
- void removeDemodulator(DemodulatorInstance *demod);
-
+ void notifyDemodulatorsChanged();
+
virtual void run();
virtual void terminate();
@@ -23,16 +21,10 @@ public:
void setIQVisualRange(long long frequency, int bandwidth);
protected:
- SDRThreadIQDataQueue *iqDataInQueue;
- DemodulatorThreadInputQueue *iqDataOutQueue;
- DemodulatorThreadInputQueue *iqVisualQueue;
- DemodulatorThreadInputQueue *iqActiveDemodVisualQueue;
-
- //protects access to demodulators lists and such
- std::mutex busy_demod;
- std::vector<DemodulatorInstance *> demodulators;
-
-
+ SDRThreadIQDataQueuePtr iqDataInQueue;
+ DemodulatorThreadInputQueuePtr iqDataOutQueue;
+ DemodulatorThreadInputQueuePtr iqVisualQueue;
+ DemodulatorThreadInputQueuePtr iqActiveDemodVisualQueue;
private:
@@ -41,14 +33,15 @@ private:
void updateChannels();
int getChannelAt(long long frequency);
+ void resetAllDemodulators();
+
ReBuffer<DemodulatorThreadIQData> buffers;
std::vector<liquid_float_complex> fpData;
std::vector<liquid_float_complex> dataOut;
std::vector<long long> chanCenters;
long long chanBw = 0;
- size_t nRunDemods;
- std::vector<DemodulatorInstance *> runDemods;
+ std::vector<DemodulatorInstancePtr> runDemods;
std::vector<int> demodChannel;
std::vector<int> demodChannelActive;
diff --git a/src/sdr/SoapySDRThread.cpp b/src/sdr/SoapySDRThread.cpp
index bc509e0..3ca5f7b 100644
--- a/src/sdr/SoapySDRThread.cpp
+++ b/src/sdr/SoapySDRThread.cpp
@@ -6,14 +6,17 @@
#include <vector>
#include "CubicSDR.h"
#include <string>
+#include <algorithm>
#include <SoapySDR/Logger.h>
+#include <chrono>
+#define TARGET_DISPLAY_FPS 60
SDRThread::SDRThread() : IOThread(), buffers("SDRThreadBuffers") {
- device = NULL;
+ device = nullptr;
- deviceConfig.store(NULL);
- deviceInfo.store(NULL);
+ deviceConfig.store(nullptr);
+ deviceInfo.store(nullptr);
sampleRate.store(DEFAULT_SAMPLE_RATE);
frequency.store(0);
@@ -25,6 +28,7 @@ SDRThread::SDRThread() : IOThread(), buffers("SDRThreadBuffers") {
rate_changed.store(false);
freq_changed.store(false);
offset_changed.store(false);
+ antenna_changed.store(false);
ppm_changed .store(false);
device_changed.store(false);
@@ -98,7 +102,7 @@ bool SDRThread::init() {
int streamMTU = device->getStreamMTU(stream);
mtuElems.store(streamMTU);
- std::cout << "Stream MTU: " << mtuElems.load() << std::endl << std::flush;
+ std::cout << "Device Stream MTU: " << mtuElems.load() << std::endl << std::flush;
deviceInfo.load()->setStreamArgs(currentStreamArgs);
deviceConfig.load()->setStreamOpts(currentStreamArgs);
@@ -130,11 +134,12 @@ bool SDRThread::init() {
device->setGainMode(SOAPY_SDR_RX,0,agc_mode.load());
numChannels.store(getOptimalChannelCount(sampleRate.load()));
- numElems.store(getOptimalElementCount(sampleRate.load(), 30));
+ numElems.store(getOptimalElementCount(sampleRate.load(), TARGET_DISPLAY_FPS));
+ //fallback if mtuElems was wrong.
if (!mtuElems.load()) {
mtuElems.store(numElems.load());
}
- inpBuffer.data.resize(numElems.load());
+
overflowBuffer.data.resize(mtuElems.load());
buffs[0] = malloc(mtuElems.load() * 4 * sizeof(float));
@@ -148,13 +153,14 @@ bool SDRThread::init() {
settingChanged.erase(settingChanged.begin(), settingChanged.end());
}
+ //apply settings.
{ //enter scoped-lock
std::lock_guard < std::mutex > lock(setting_busy);
for (settings_i = settingsInfo.begin(); settings_i != settingsInfo.end(); settings_i++) {
SoapySDR::ArgInfo setting = (*settings_i);
- if ((settingChanged.find(setting.key) != settingChanged.end()) && (settings.find(setting.key) != settings.end())) {
- device->writeSetting(setting.key, settings[setting.key]);
+ if ((settingChanged.find(setting.key) != settingChanged.end()) && (settings.find(setting.key) != settings.end())) {
+ device->writeSetting(setting.key, settings[setting.key]);
settingChanged[setting.key] = false;
} else {
settings[setting.key] = device->readSetting(setting.key);
@@ -168,7 +174,10 @@ bool SDRThread::init() {
updateSettings();
wxGetApp().sdrThreadNotify(SDRThread::SDR_THREAD_INITIALIZED, std::string("Device Initialized."));
-
+
+ //rebuild menu now that settings are really been applied.
+ wxGetApp().notifyMainUIOfDeviceChange(true);
+
return true;
}
@@ -178,7 +187,17 @@ void SDRThread::deinit() {
free(buffs[0]);
}
-void SDRThread::readStream(SDRThreadIQDataQueue* iqDataOutQueue) {
+void SDRThread::assureBufferMinSize(SDRThreadIQData * dataOut, size_t minSize) {
+
+ if (dataOut->data.size() < minSize) {
+ dataOut->data.resize(minSize);
+ }
+}
+
+//Called in an infinite loop, read SaopySDR device to build
+// a 'this.numElems' sized batch of samples (SDRThreadIQData) and push it into iqDataOutQueue.
+//this batch of samples is built to represent 1 frame / TARGET_DISPLAY_FPS.
+int SDRThread::readStream(SDRThreadIQDataQueuePtr iqDataOutQueue) {
int flags;
long long timeNs;
@@ -186,41 +205,69 @@ void SDRThread::readStream(SDRThreadIQDataQueue* iqDataOutQueue) {
int nElems = numElems.load();
int mtElems = mtuElems.load();
- //If overflow occured on the previous readStream(), transfer it in inpBuffer now
+ // Warning: if MTU > numElems, i.e if device MTU is too big w.r.t the sample rate, the TARGET_DISPLAY_FPS cannot
+ //be reached and the CubicSDR displays "slows down".
+ //To get back a TARGET_DISPLAY_FPS, the user need to adapt
+ //the SoapySDR Device to use smaller buffer sizes, because
+ // readStream() is suited to device MTU and cannot be really adapted dynamically.
+ //TODO: Add in doc the need to reduce SoapySDR device buffer length (if available) to restore higher fps.
+
+ //0. Retreive a new batch
+ SDRThreadIQDataPtr dataOut = buffers.getBuffer();
+
+ //resize to the target size immedialetly, to minimize later reallocs:
+ assureBufferMinSize(dataOut.get(), nElems);
+
+ //1.If overflow occured on the previous readStream(), transfer it in dataOut directly.
if (numOverflow > 0) {
- int n_overflow = numOverflow;
- if (n_overflow > nElems) {
- n_overflow = nElems;
- }
- memcpy(&inpBuffer.data[0], &overflowBuffer.data[0], n_overflow * sizeof(liquid_float_complex));
+ int n_overflow = std::min(numOverflow, nElems);
+
+ //safety
+ assureBufferMinSize(dataOut.get(), n_overflow);
+
+ ::memcpy(&dataOut->data[0], &overflowBuffer.data[0], n_overflow * sizeof(liquid_float_complex));
n_read = n_overflow;
+
+ //is still > 0 if MTU > nElements (low sample rate w.r.t the MTU !)
numOverflow -= n_overflow;
+
+ // std::cout << "SDRThread::readStream() 1.1 overflowBuffer not empty, collect the remaining " << n_overflow << " samples in it..." << std::endl;
- if (numOverflow) { // still some left..
- memmove(&overflowBuffer.data[0], &overflowBuffer.data[n_overflow], numOverflow * sizeof(liquid_float_complex));
+ if (numOverflow > 0) { // still some left, shift the remaining samples to the begining..
+ ::memmove(&overflowBuffer.data[0], &overflowBuffer.data[n_overflow], numOverflow * sizeof(liquid_float_complex));
+
+ // std::cout << "SDRThread::readStream() 1.2 overflowBuffer still not empty, compact the remaining " << numOverflow << " samples in it..." << std::endl;
}
- }
+ } //end if numOverflow > 0
- //attempt readStream() at most nElems, by mtElems-sized chunks, append inpBuffer.
+ int readStreamCode = 0;
+
+ //2. attempt readStream() at most nElems, by mtElems-sized chunks, append in dataOut->data directly.
while (n_read < nElems && !stopping) {
- int n_requested = nElems-n_read;
+ //Whatever the number of remaining samples needed to reach nElems, we always try to read a mtElems-size chunk,
+ //from which SoapySDR effectively returns n_stream_read.
int n_stream_read = device->readStream(stream, buffs, mtElems, flags, timeNs);
+
+ readStreamCode = n_stream_read;
//if the n_stream_read <= 0, bail out from reading.
if (n_stream_read == 0) {
- std::cout << "SDRThread::readStream(): read blocking..." << std::endl;
+ std::cout << "SDRThread::readStream(): 2. SoapySDR read blocking..." << std::endl;
break;
}
else if (n_stream_read < 0) {
- std::cout << "SDRThread::readStream(): read failed with code: " << n_stream_read << std::endl;
+ std::cout << "SDRThread::readStream(): 2. SoapySDR read failed with code: " << n_stream_read << std::endl;
break;
}
-
- //sucess read beyond nElems, with overflow
+
+ //sucess read beyond nElems, so with overflow:
if ((n_read + n_stream_read) > nElems) {
- //Copy at most n_requested CF32 into inpBuffer.data liquid_float_complex,
+ //n_requested is the exact number to reach nElems.
+ int n_requested = nElems-n_read;
+
+ //Copy at most n_requested CF32 into .data liquid_float_complex,
//starting at n_read position.
//inspired from SoapyRTLSDR code, this mysterious void** is indeed an array of CF32(real/imag) samples, indeed an array of
//float with the following layout [sample 1 real part , sample 1 imag part, sample 2 real part , sample 2 imag part,sample 3 real part , sample 3 imag part,...etc]
@@ -228,29 +275,70 @@ void SDRThread::readStream(SDRThreadIQDataQueue* iqDataOutQueue) {
//nor that the Re/Im layout of fields matches the float array order, assign liquid_float_complex field by field.
float *pp = (float *)buffs[0];
- for (int i = 0; i < n_requested; i++) {
- inpBuffer.data[n_read + i].real = pp[2 * i];
- inpBuffer.data[n_read + i].imag = pp[2 * i + 1];
- }
+ //safety
+ assureBufferMinSize(dataOut.get(), n_read + n_requested);
- numOverflow = n_stream_read-n_requested;
+ if (iq_swap.load()) {
+ for (int i = 0; i < n_requested; i++) {
+ dataOut->data[n_read + i].imag = pp[2 * i];
+ dataOut->data[n_read + i].real = pp[2 * i + 1];
+ }
+ } else {
+ for (int i = 0; i < n_requested; i++) {
+ dataOut->data[n_read + i].real = pp[2 * i];
+ dataOut->data[n_read + i].imag = pp[2 * i + 1];
+ }
+ }
- //shift of n_requested samples, each one made of 2 floats...
+ //shift of n_requested samples, each one made of 2 floats...
pp += n_requested * 2;
+
+ //numNewOverflow are in exess, they have to be added in the existing overflowBuffer.
+ int numNewOverflow = n_stream_read - n_requested;
+
//so push the remainder samples to overflowBuffer:
- for (int i = 0; i < numOverflow; i++) {
- overflowBuffer.data[i].real = pp[2 * i];
- overflowBuffer.data[i].imag = pp[2 * i + 1];
+ if (numNewOverflow > 0) {
+ // std::cout << "SDRThread::readStream(): 2. SoapySDR read make nElems overflow by " << numNewOverflow << " samples..." << std::endl;
+ }
+
+ //safety
+ assureBufferMinSize(&overflowBuffer, numOverflow + numNewOverflow);
+
+ if (iq_swap.load()) {
+
+ for (int i = 0; i < numNewOverflow; i++) {
+ overflowBuffer.data[numOverflow + i].imag = pp[2 * i];
+ overflowBuffer.data[numOverflow + i].real = pp[2 * i + 1];
+ }
+ }
+ else {
+ for (int i = 0; i < numNewOverflow; i++) {
+ overflowBuffer.data[numOverflow + i].real = pp[2 * i];
+ overflowBuffer.data[numOverflow + i].imag = pp[2 * i + 1];
+ }
}
+ numOverflow += numNewOverflow;
+
n_read += n_requested;
- } else if (n_stream_read > 0) {
-
+ } else if (n_stream_read > 0) { // no overflow, read the whole n_stream_read.
+
float *pp = (float *)buffs[0];
- for (int i = 0; i < n_stream_read; i++) {
- inpBuffer.data[n_read + i].real = pp[2 * i];
- inpBuffer.data[n_read + i].imag = pp[2 * i + 1];
+ //safety
+ assureBufferMinSize(dataOut.get(), n_read + n_stream_read);
+
+ if (iq_swap.load()) {
+ for (int i = 0; i < n_stream_read; i++) {
+ dataOut->data[n_read + i].imag = pp[2 * i];
+ dataOut->data[n_read + i].real = pp[2 * i + 1];
+ }
}
+ else {
+ for (int i = 0; i < n_stream_read; i++) {
+ dataOut->data[n_read + i].real = pp[2 * i];
+ dataOut->data[n_read + i].imag = pp[2 * i + 1];
+ }
+ }
n_read += n_stream_read;
} else {
@@ -258,19 +346,12 @@ void SDRThread::readStream(SDRThreadIQDataQueue* iqDataOutQueue) {
}
} //end while
+ //3. At that point, dataOut contains nElems (or less if a read has return an error), try to post in queue, else discard.
if (n_read > 0 && !stopping && !iqDataOutQueue->full()) {
- SDRThreadIQData *dataOut = buffers.getBuffer();
-
- if (iq_swap.load()) {
- dataOut->data.resize(n_read);
- for (int i = 0; i < n_read; i++) {
- dataOut->data[i].imag = inpBuffer.data[i].real;
- dataOut->data[i].real = inpBuffer.data[i].imag;
- }
- } else {
- dataOut->data.assign(inpBuffer.data.begin(), inpBuffer.data.begin()+n_read);
- }
+ //clamp result to the actual read size:
+ dataOut->data.resize(n_read);
+
dataOut->frequency = frequency.load();
dataOut->sampleRate = sampleRate.load();
dataOut->dcCorrected = hasHardwareDC.load();
@@ -278,32 +359,45 @@ void SDRThread::readStream(SDRThreadIQDataQueue* iqDataOutQueue) {
if (!iqDataOutQueue->try_push(dataOut)) {
//The rest of the system saturates,
- //finally the push didn't suceeded, recycle dataOut immediatly.
- dataOut->setRefCount(0);
+ //finally the push didn't suceeded.
- std::cout << "SDRThread::readStream(): iqDataOutQueue output queue is full, discard processing ! " << std::endl;
+ readStreamCode = -32;
+ std::cout << "SDRThread::readStream(): 3.2 iqDataOutQueue output queue is full, discard processing of the batch..." << std::endl;
//saturation, let a chance to the other threads to consume the existing samples
std::this_thread::yield();
}
}
+ else {
+ readStreamCode = -31;
+ std::cout << "SDRThread::readStream(): 3.1 iqDataOutQueue output queue is full, discard processing of the batch..." << std::endl;
+ //saturation, let a chance to the other threads to consume the existing samples
+ std::this_thread::yield();
+ }
+
+ return readStreamCode;
}
+
void SDRThread::readLoop() {
- SDRThreadIQDataQueue* iqDataOutQueue = static_cast<SDRThreadIQDataQueue*>( getOutputQueue("IQDataOutput"));
+
+ SDRThreadIQDataQueuePtr iqDataOutQueue = std::static_pointer_cast<SDRThreadIQDataQueue>( getOutputQueue("IQDataOutput"));
- if (iqDataOutQueue == NULL) {
+ if (iqDataOutQueue == nullptr) {
return;
}
updateGains();
-
+
while (!stopping.load()) {
+
updateSettings();
+
readStream(iqDataOutQueue);
- }
- buffers.purge();
+ } //End while
+
+ iqDataOutQueue->flush();
}
void SDRThread::updateGains() {
@@ -327,6 +421,13 @@ void SDRThread::updateSettings() {
if (!stream) {
return;
}
+
+ if (antenna_changed.load()) {
+
+ device->setAntenna(SOAPY_SDR_RX, 0, antennaName);
+
+ antenna_changed.store(false);
+ }
if (offset_changed.load()) {
if (!freq_changed.load()) {
@@ -337,6 +438,7 @@ void SDRThread::updateSettings() {
}
if (rate_changed.load()) {
+
device->setSampleRate(SOAPY_SDR_RX,0,sampleRate.load());
// TODO: explore bandwidth setting option to see if this is necessary for others
if (device->getDriverKey() == "bladeRF") {
@@ -344,17 +446,20 @@ void SDRThread::updateSettings() {
}
sampleRate.store(device->getSampleRate(SOAPY_SDR_RX,0));
numChannels.store(getOptimalChannelCount(sampleRate.load()));
- numElems.store(getOptimalElementCount(sampleRate.load(), 60));
+ numElems.store(getOptimalElementCount(sampleRate.load(), TARGET_DISPLAY_FPS));
int streamMTU = device->getStreamMTU(stream);
mtuElems.store(streamMTU);
+ //fallback if mtuElems was wrong
if (!mtuElems.load()) {
mtuElems.store(numElems.load());
}
- inpBuffer.data.resize(numElems.load());
+
overflowBuffer.data.resize(mtuElems.load());
free(buffs[0]);
buffs[0] = malloc(mtuElems.load() * 4 * sizeof(float));
+ //clear overflow buffer
numOverflow = 0;
+
rate_changed.store(false);
doUpdate = true;
}
@@ -385,13 +490,12 @@ void SDRThread::updateSettings() {
if (!agc_mode.load()) {
updateGains();
+ //re-apply the saved configuration gains:
DeviceConfig *devConfig = deviceConfig.load();
ConfigGains gains = devConfig->getGains();
- if (gains.size()) {
- for (ConfigGains::iterator gain_i = gains.begin(); gain_i != gains.end(); gain_i++) {
- setGain(gain_i->first, gain_i->second);
- }
+ for (ConfigGains::iterator gain_i = gains.begin(); gain_i != gains.end(); gain_i++) {
+ setGain(gain_i->first, gain_i->second);
}
}
doUpdate = true;
@@ -410,7 +514,6 @@ void SDRThread::updateSettings() {
gain_value_changed.store(false);
}
-
if (setting_value_changed.load()) {
std::lock_guard < std::mutex > lock(setting_busy);
@@ -444,7 +547,7 @@ void SDRThread::run() {
SDRDeviceInfo *activeDev = deviceInfo.load();
- if (activeDev != NULL) {
+ if (activeDev != nullptr) {
std::cout << "device init()" << std::endl;
if (!init()) {
std::cout << "SDR Thread stream init error." << std::endl;
@@ -464,6 +567,15 @@ void SDRThread::run() {
std::cout << "SDR thread done." << std::endl;
}
+void SDRThread::terminate() {
+ IOThread::terminate();
+
+ SDRThreadIQDataQueuePtr iqDataOutQueue = std::static_pointer_cast<SDRThreadIQDataQueue>(getOutputQueue("IQDataOutput"));
+
+ if (iqDataOutQueue != nullptr) {
+ iqDataOutQueue->flush();
+ }
+}
SDRDeviceInfo *SDRThread::getDevice() {
return deviceInfo.load();
@@ -538,6 +650,12 @@ void SDRThread::unlockFrequency() {
void SDRThread::setOffset(long long ofs) {
offset.store(ofs);
offset_changed.store(true);
+
+ DeviceConfig *devConfig = deviceConfig.load();
+ if (devConfig) {
+ devConfig->setOffset(ofs);
+ }
+
// std::cout << "Set offset: " << offset.load() << std::endl;
}
@@ -545,6 +663,20 @@ long long SDRThread::getOffset() {
return offset.load();
}
+void SDRThread::setAntenna(const std::string& name) {
+ antennaName = name;
+ antenna_changed.store(true);
+
+ DeviceConfig *devConfig = deviceConfig.load();
+ if (devConfig) {
+ devConfig->setAntennaName(antennaName);
+ }
+}
+
+std::string SDRThread::getAntenna() {
+ return antennaName;
+}
+
void SDRThread::setSampleRate(long rate) {
sampleRate.store(rate);
rate_changed = true;
@@ -561,6 +693,12 @@ long SDRThread::getSampleRate() {
void SDRThread::setPPM(int ppm) {
this->ppm.store(ppm);
ppm_changed.store(true);
+
+ DeviceConfig *devConfig = deviceConfig.load();
+ if (devConfig) {
+ devConfig->setPPM(ppm);
+ }
+
// std::cout << "Set PPM: " << this->ppm.load() << std::endl;
}
@@ -603,9 +741,9 @@ void SDRThread::setGain(std::string name, float value) {
float SDRThread::getGain(std::string name) {
std::lock_guard < std::mutex > lock(gain_busy);
- float val = gainValues[name];
-
- return val;
+ float val = gainValues[name];
+
+ return val;
}
void SDRThread::writeSetting(std::string name, std::string value) {
diff --git a/src/sdr/SoapySDRThread.h b/src/sdr/SoapySDRThread.h
index 2982ac1..c72c5a9 100644
--- a/src/sdr/SoapySDRThread.h
+++ b/src/sdr/SoapySDRThread.h
@@ -4,7 +4,7 @@
#pragma once
#include <atomic>
-
+#include <memory>
#include "ThreadBlockingQueue.h"
#include "DemodulatorMgr.h"
#include "SDRDeviceInfo.h"
@@ -15,8 +15,9 @@
#include <SoapySDR/Registry.hpp>
#include <SoapySDR/Device.hpp>
+#include <stddef.h>
-class SDRThreadIQData: public ReferenceCounter {
+class SDRThreadIQData {
public:
long long frequency;
long long sampleRate;
@@ -34,26 +35,33 @@ public:
}
- ~SDRThreadIQData() {
+ virtual ~SDRThreadIQData() {
}
};
-
-typedef ThreadBlockingQueue<SDRThreadIQData *> SDRThreadIQDataQueue;
+typedef std::shared_ptr<SDRThreadIQData> SDRThreadIQDataPtr;
+typedef ThreadBlockingQueue<SDRThreadIQDataPtr> SDRThreadIQDataQueue;
+typedef std::shared_ptr<SDRThreadIQDataQueue> SDRThreadIQDataQueuePtr;
class SDRThread : public IOThread {
private:
bool init();
void deinit();
- void readStream(SDRThreadIQDataQueue* iqDataOutQueue);
+
+ //returns the SoapyDevice readStream return value,
+ //i.e if >= 0 the numbre of samples read, else if < 0 an error code.
+ int readStream(SDRThreadIQDataQueuePtr iqDataOutQueue);
+
void readLoop();
public:
SDRThread();
~SDRThread();
- enum SDRThreadState { SDR_THREAD_MESSAGE, SDR_THREAD_INITIALIZED, SDR_THREAD_FAILED };
+
+ enum SDRThreadState { SDR_THREAD_MESSAGE, SDR_THREAD_INITIALIZED, SDR_THREAD_FAILED};
virtual void run();
+ virtual void terminate();
SDRDeviceInfo *getDevice();
void setDevice(SDRDeviceInfo *dev);
@@ -69,6 +77,9 @@ public:
void setOffset(long long ofs);
long long getOffset();
+
+ void setAntenna(const std::string& name);
+ std::string getAntenna();
void setSampleRate(long rate);
long getSampleRate();
@@ -99,7 +110,6 @@ protected:
SoapySDR::Device *device;
void *buffs[1];
ReBuffer<SDRThreadIQData> buffers;
- SDRThreadIQData inpBuffer;
SDRThreadIQData overflowBuffer;
int numOverflow;
std::atomic<DeviceConfig *> deviceConfig;
@@ -113,7 +123,8 @@ protected:
std::atomic_llong frequency, offset, lock_freq;
std::atomic_int ppm, numElems, mtuElems, numChannels;
std::atomic_bool hasPPM, hasHardwareDC;
- std::atomic_bool agc_mode, rate_changed, freq_changed, offset_changed,
+ std::string antennaName;
+ std::atomic_bool agc_mode, rate_changed, freq_changed, offset_changed, antenna_changed,
ppm_changed, device_changed, agc_mode_changed, gain_value_changed, setting_value_changed, frequency_locked, frequency_lock_init, iq_swap;
std::mutex gain_busy;
@@ -121,4 +132,7 @@ protected:
std::map<std::string, bool> gainChanged;
SoapySDR::Kwargs streamArgs;
+
+private:
+ void assureBufferMinSize(SDRThreadIQData * dataOut, size_t minSize);
};
diff --git a/src/ui/GLPanel.cpp b/src/ui/GLPanel.cpp
index 4963d0b..010fbbc 100644
--- a/src/ui/GLPanel.cpp
+++ b/src/ui/GLPanel.cpp
@@ -308,7 +308,7 @@ void GLPanel::calcTransform(mat4 transform_in) {
void GLPanel::draw() {
float min = -1.0, max = 1.0;
- glLoadMatrixf(transform);
+ glLoadMatrixf(transform.to_ptr());
if (fillType != GLPANEL_FILL_NONE && visible) {
glEnable(GL_BLEND);
@@ -378,7 +378,7 @@ void GLPanel::draw() {
}
// if (coord == GLPANEL_Y_UP) {
// }
- glLoadMatrixf(transform * mCoord);
+ glLoadMatrixf((transform * mCoord).to_ptr());
drawPanelContents();
}
}
diff --git a/src/ui/GLPanel.h b/src/ui/GLPanel.h
index 94cd7a6..1ab5152 100644
--- a/src/ui/GLPanel.h
+++ b/src/ui/GLPanel.h
@@ -63,6 +63,7 @@ public:
std::vector<GLPanel *> children;
GLPanel();
+ virtual ~GLPanel() {};
void setPosition(float x, float y);
diff --git a/src/util/GLFont.cpp b/src/util/GLFont.cpp
index 5acf124..924f9f8 100644
--- a/src/util/GLFont.cpp
+++ b/src/util/GLFont.cpp
@@ -3,6 +3,8 @@
#include "GLFont.h"
+#include <wx/string.h>
+
#include <iostream>
#include <fstream>
#include <algorithm>
@@ -12,14 +14,6 @@
#include "CoreFoundation/CoreFoundation.h"
#endif
-static std::wstring getExePath(void)
-{
- //get the dir path of the executable
- wxFileName exePath = wxFileName(wxStandardPaths::Get().GetExecutablePath());
-
- return std::wstring(exePath.GetPath().ToStdWstring());
-}
-
#ifndef RES_FOLDER
#define RES_FOLDER ""
#endif
@@ -253,11 +247,11 @@ void GLFont::loadFontOnce() {
//Re-compute the resource dir.
resourceFolder = fontDefFileName.GetPath();
- std::wstring fontDefFileNamePath = fontDefFileName.GetFullPath(wxPATH_NATIVE).ToStdWstring();
+ std::string fontDefFileNamePath = fontDefFileName.GetFullPath(wxPATH_NATIVE).ToStdString();
std::wifstream input;
- std::string inpFileStr(fontDefFileNamePath.begin(), fontDefFileNamePath.end());
- input.open(inpFileStr, std::ios::in);
+
+ input.open(fontDefFileNamePath, std::ios::in);
std::wstring op;
diff --git a/src/util/ThreadBlockingQueue.h b/src/util/ThreadBlockingQueue.h
index 47819d0..59fb224 100644
--- a/src/util/ThreadBlockingQueue.h
+++ b/src/util/ThreadBlockingQueue.h
@@ -25,6 +25,8 @@
class ThreadQueueBase {
};
+typedef std::shared_ptr<ThreadQueueBase> ThreadQueueBasePtr;
+
/** A thread-safe asynchronous blocking queue */
template<typename T>
class ThreadBlockingQueue : public ThreadQueueBase {
@@ -36,16 +38,15 @@ public:
/*! Create safe blocking queue. */
ThreadBlockingQueue() {
- //at least 1 (== Exchanger)
+ //at least 1 (== Java SynchronizedQueue)
m_max_num_items = MIN_ITEM_NB;
};
- //Copy constructor
- ThreadBlockingQueue(const ThreadBlockingQueue& sq) {
- std::lock_guard < std::mutex > lock(sq.m_mutex);
- m_queue = sq.m_queue;
- m_max_num_items = sq.m_max_num_items;
- }
+ //Forbid Copy construction.
+ ThreadBlockingQueue(const ThreadBlockingQueue& sq) = delete;
+
+ /*! Forbid copy assignment. */
+ ThreadBlockingQueue& operator=(const ThreadBlockingQueue& sq) = delete;
/*! Destroy safe queue. */
~ThreadBlockingQueue() {
@@ -74,10 +75,10 @@ public:
* \param[in] item An item.
* \param[in] timeout a max waiting timeout in microseconds for an item to be pushed.
* by default, = 0 means indefinite wait.
- * \param[in] errorMessage an error message written on std::cout in case of the timeout wait
+ * \param[in] errorMessage if != nullptr (is nullptr by default) an error message written on std::cout in case of the timeout wait
* \return true if an item was pushed into the queue, else a timeout has occured.
*/
- bool push(const value_type& item, std::uint64_t timeout = BLOCKING_INFINITE_TIMEOUT,const char* errorMessage = "") {
+ bool push(const value_type& item, std::uint64_t timeout = BLOCKING_INFINITE_TIMEOUT,const char* errorMessage = nullptr) {
std::unique_lock < std::mutex > lock(m_mutex);
if (timeout == BLOCKING_INFINITE_TIMEOUT) {
@@ -90,12 +91,15 @@ public:
return false;
}
else if (false == m_cond_not_full.wait_for(lock, std::chrono::microseconds(timeout),
- [this]() { return m_queue.size() < m_max_num_items; })) {
- std::thread::id currentThreadId = std::this_thread::get_id();
- std::cout << "WARNING: Thread 0x" << std::hex << currentThreadId << std::dec <<
- " (" << currentThreadId << ") executing {" << typeid(*this).name() << "}.push() has failed with timeout > " <<
- (timeout * 0.001) << " ms, message: " << errorMessage << std::endl;
- return false;
+ [this]() { return m_queue.size() < m_max_num_items; })) {
+
+ if (errorMessage != nullptr) {
+ std::thread::id currentThreadId = std::this_thread::get_id();
+ std::cout << "WARNING: Thread 0x" << std::hex << currentThreadId << std::dec <<
+ " (" << currentThreadId << ") executing {" << typeid(*this).name() << "}.push() has failed with timeout > " <<
+ (timeout * 0.001) << " ms, message: '" << errorMessage << "'" << std::endl << std::flush;
+ }
+ return false;
}
m_queue.push_back(item);
@@ -123,10 +127,10 @@ public:
/**
* Pops item from the queue. If the queue is empty, blocks for timeout microseconds, or until item becomes available.
* \param[in] timeout The number of microseconds to wait. O (default) means indefinite wait.
- * \param[in] errorMessage an error message written on std::cout in case of the timeout wait
+ * \param[in] errorMessage if != nullptr (is nullptr by default) an error message written on std::cout in case of the timeout wait
* \return true if get an item from the queue, false if no item is received before the timeout.
*/
- bool pop(value_type& item, std::uint64_t timeout = BLOCKING_INFINITE_TIMEOUT, const char* errorMessage = "") {
+ bool pop(value_type& item, std::uint64_t timeout = BLOCKING_INFINITE_TIMEOUT, const char* errorMessage = nullptr) {
std::unique_lock < std::mutex > lock(m_mutex);
if (timeout == BLOCKING_INFINITE_TIMEOUT) {
@@ -140,10 +144,13 @@ public:
}
else if (false == m_cond_not_empty.wait_for(lock, std::chrono::microseconds(timeout),
[this]() { return !m_queue.empty(); })) {
- std::thread::id currentThreadId = std::this_thread::get_id();
- std::cout << "WARNING: Thread 0x" << std::hex << currentThreadId << std::dec <<
- " (" << currentThreadId << ") executing {" << typeid(*this).name() << "}.pop() has failed with timeout > " <<
- (timeout * 0.001) << " ms, message: " << errorMessage << std::endl;
+
+ if (errorMessage != nullptr) {
+ std::thread::id currentThreadId = std::this_thread::get_id();
+ std::cout << "WARNING: Thread 0x" << std::hex << currentThreadId << std::dec <<
+ " (" << currentThreadId << ") executing {" << typeid(*this).name() << "}.pop() has failed with timeout > " <<
+ (timeout * 0.001) << " ms, message: '" << errorMessage << "'" << std::endl << std::flush;
+ }
return false;
}
@@ -208,57 +215,10 @@ public:
m_cond_not_full.notify_all();
}
- /**
- * Swaps the contents.
- * \param[out] sq The ThreadBlockingQueue to swap with 'this'.
- */
- void swap(ThreadBlockingQueue& sq) {
- if (this != &sq) {
- std::lock_guard < std::mutex > lock1(m_mutex);
- std::lock_guard < std::mutex > lock2(sq.m_mutex);
- m_queue.swap(sq.m_queue);
- std::swap(m_max_num_items, sq.m_max_num_items);
-
- if (!m_queue.empty()) {
- m_cond_not_empty.notify_all();
- }
-
- if (!sq.m_queue.empty()) {
- sq.m_cond_not_empty.notify_all();
- }
- if (!m_queue.full()) {
- m_cond_not_full.notify_all();
- }
-
- if (!sq.m_queue.full()) {
- sq.m_cond_not_full.notify_all();
- }
- }
- }
-
- /*! The copy assignment operator */
- ThreadBlockingQueue& operator=(const ThreadBlockingQueue& sq) {
- if (this != &sq) {
- std::lock_guard < std::mutex > lock1(m_mutex);
- std::lock_guard < std::mutex > lock2(sq.m_mutex);
-
- m_queue = sq.m_queue;
- m_max_num_items = sq.m_max_num_items;
-
- if (!m_queue.empty()) {
- m_cond_not_empty.notify_all();
- }
-
- if (!m_queue.full()) {
- m_cond_not_full.notify_all();
- }
- }
- return *this;
- }
private:
- //TODO: use a circular buffer structure ? (fixed array + modulo)
+
std::deque<T> m_queue;
mutable std::mutex m_mutex;
@@ -266,9 +226,3 @@ private:
std::condition_variable m_cond_not_full;
size_t m_max_num_items = MIN_ITEM_NB;
};
-
-/*! Swaps the contents of two ThreadBlockingQueue objects. (external operator) */
-template<typename T>
-void swap(ThreadBlockingQueue<T>& q1, ThreadBlockingQueue<T>& q2) {
- q1.swap(q2);
-}
diff --git a/src/util/ThreadQueue.cpp b/src/util/ThreadQueue.cpp
deleted file mode 100644
index 3597da5..0000000
--- a/src/util/ThreadQueue.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-// Copyright (c) Charles J. Cliffe
-// SPDX-License-Identifier: GPL-2.0+
-
-#include <ThreadQueue.h>
\ No newline at end of file
diff --git a/src/util/ThreadQueue.h b/src/util/ThreadQueue.h
deleted file mode 100644
index 1805b4b..0000000
--- a/src/util/ThreadQueue.h
+++ /dev/null
@@ -1,302 +0,0 @@
-// Copyright (c) Charles J. Cliffe
-// SPDX-License-Identifier: GPL-2.0+
-
-#pragma once
-
-/* Credit to Alfredo Pons / https://plus.google.com/109903449837592676231
- * Code from http://gnodebian.blogspot.com.es/2013/07/a-thread-safe-asynchronous-queue-in-c11.html
- *
- * Changes:
- * Charles J. Nov-19-2014
- * - Renamed SafeQueue -> ThreadQueue
- * Sonnier.V Feb-10-2017
- * - Simplified, various fixes
- */
-
-#include <deque>
-#include <list>
-#include <mutex>
-#include <thread>
-#include <cstdint>
-#include <condition_variable>
-
-class ThreadQueueBase {
-};
-
-/** A thread-safe asynchronous queue */
-template<typename T>
-class ThreadQueue : public ThreadQueueBase {
-
- typedef typename std::deque<T>::value_type value_type;
- typedef typename std::deque<T>::size_type size_type;
-
-public:
-
- /*! Create safe queue. */
- ThreadQueue() {
- m_max_num_items = 0;
- };
- ThreadQueue(ThreadQueue&& sq) {
- m_queue = std::move(sq.m_queue);
- m_max_num_items = sq.m_max_num_items;
- }
- ThreadQueue(const ThreadQueue& sq) {
- std::lock_guard < std::mutex > lock(sq.m_mutex);
- m_queue = sq.m_queue;
- m_max_num_items = sq.m_max_num_items;
- }
-
- /*! Destroy safe queue. */
- ~ThreadQueue() {
- std::lock_guard < std::mutex > lock(m_mutex);
- }
-
- /**
- * Sets the maximum number of items in the queue. Defaults is 0: No limit
- * \param[in] item An item.
- */
- void set_max_num_items(unsigned int max_num_items) {
- std::lock_guard < std::mutex > lock(m_mutex);
- m_max_num_items = max_num_items;
- }
-
- /**
- * Pushes the item into the queue.
- * \param[in] item An item.
- * \return true if an item was pushed into the queue
- */
- bool push(const value_type& item) {
- std::lock_guard < std::mutex > lock(m_mutex);
-
- if (m_max_num_items > 0 && m_queue.size() > m_max_num_items) {
- return false;
- }
-
- m_queue.push_back(item);
- m_cond_not_empty.notify_all();
- return true;
- }
-
- /**
- * Pushes the item into the queue.
- * \param[in] item An item.
- * \return true if an item was pushed into the queue
- */
- bool push(const value_type&& item) {
- std::lock_guard < std::mutex > lock(m_mutex);
-
- if (m_max_num_items > 0 && m_queue.size() > m_max_num_items) {
- return false;
- }
-
- m_queue.push_back(item);
- m_cond_not_empty.notify_all();
- return true;
- }
-
- /**
- * Pops item from the queue. If queue is empty, this function blocks until item becomes available.
- * \param[out] item The item.
- */
- void pop(value_type& item) {
- std::unique_lock < std::mutex > lock(m_mutex);
- m_cond_not_empty.wait(lock, [this]() // Lambda funct
- {
- return !m_queue.empty();
- });
- item = m_queue.front();
- m_queue.pop_front();
- }
-
- /**
- * Pops item from the queue using the contained type's move assignment operator, if it has one..
- * This method is identical to the pop() method if that type has no move assignment operator.
- * If queue is empty, this function blocks until item becomes available.
- * \param[out] item The item.
- */
- void move_pop(value_type& item) {
- std::unique_lock < std::mutex > lock(m_mutex);
- m_cond_not_empty.wait(lock, [this]() // Lambda funct
- {
- return !m_queue.empty();
- });
- item = std::move(m_queue.front());
- m_queue.pop_front();
- }
-
- /**
- * Tries to pop item from the queue.
- * \param[out] item The item.
- * \return False is returned if no item is available.
- */
- bool try_pop(value_type& item) {
- std::lock_guard < std::mutex > lock(m_mutex);
-
- if (m_queue.empty())
- return false;
-
- item = m_queue.front();
- m_queue.pop_front();
- return true;
- }
-
- /**
- * Tries to pop item from the queue using the contained type's move assignment operator, if it has one..
- * This method is identical to the try_pop() method if that type has no move assignment operator.
- * \param[out] item The item.
- * \return False is returned if no item is available.
- */
- bool try_move_pop(value_type& item) {
- std::lock_guard < std::mutex > lock(m_mutex);
-
- if (m_queue.empty())
- return false;
-
- item = std::move(m_queue.front());
- m_queue.pop_front();
- return true;
- }
-
- /**
- * Pops item from the queue. If the queue is empty, blocks for timeout microseconds, or until item becomes available.
- * \param[out] t An item.
- * \param[in] timeout The number of microseconds to wait.
- * \return true if get an item from the queue, false if no item is received before the timeout.
- */
- bool timeout_pop(value_type& item, std::uint64_t timeout) {
- std::unique_lock < std::mutex > lock(m_mutex);
-
- if (m_queue.empty()) {
- if (timeout == 0)
- return false;
-
- if (m_cond_not_empty.wait_for(lock, std::chrono::microseconds(timeout)) == std::cv_status::timeout)
- return false;
- }
-
- item = m_queue.front();
- m_queue.pop_front();
- return true;
- }
-
- /**
- * Pops item from the queue using the contained type's move assignment operator, if it has one..
- * If the queue is empty, blocks for timeout microseconds, or until item becomes available.
- * This method is identical to the try_pop() method if that type has no move assignment operator.
- * \param[out] t An item.
- * \param[in] timeout The number of microseconds to wait.
- * \return true if get an item from the queue, false if no item is received before the timeout.
- */
- bool timeout_move_pop(value_type& item, std::uint64_t timeout) {
- std::unique_lock < std::mutex > lock(m_mutex);
-
- if (m_queue.empty()) {
- if (timeout == 0)
- return false;
-
- if (m_cond_not_empty.wait_for(lock, std::chrono::microseconds(timeout)) == std::cv_status::timeout)
- return false;
- }
-
- item = std::move(m_queue.front());
- m_queue.pop_front();
- return true;
- }
-
- /**
- * Gets the number of items in the queue.
- * \return Number of items in the queue.
- */
- size_type size() const {
- std::lock_guard < std::mutex > lock(m_mutex);
- return m_queue.size();
- }
-
- /**
- * Check if the queue is empty.
- * \return true if queue is empty.
- */
- bool empty() const {
- std::lock_guard < std::mutex > lock(m_mutex);
- return m_queue.empty();
- }
-
- /**
- * Check if the queue is full.
- * \return true if queue is full.
- */
- bool full() const {
- std::lock_guard < std::mutex > lock(m_mutex);
- return (m_max_num_items != 0) && (m_queue.size() >= m_max_num_items);
- }
-
- /**
- * Remove any items in the queue.
- */
- void flush() {
- std::lock_guard < std::mutex > lock(m_mutex);
- m_queue.clear();
- }
-
- /**
- * Swaps the contents.
- * \param[out] sq The ThreadQueue to swap with 'this'.
- */
- void swap(ThreadQueue& sq) {
- if (this != &sq) {
- std::lock_guard < std::mutex > lock1(m_mutex);
- std::lock_guard < std::mutex > lock2(sq.m_mutex);
- m_queue.swap(sq.m_queue);
- std::swap(m_max_num_items, sq.m_max_num_items);
-
- if (!m_queue.empty())
- m_cond_not_empty.notify_all();
-
-
- if (!sq.m_queue.empty())
- sq.m_cond_not_empty.notify_all();
- }
- }
-
- /*! The copy assignment operator */
- ThreadQueue& operator=(const ThreadQueue& sq) {
- if (this != &sq) {
- std::lock_guard < std::mutex > lock1(m_mutex);
- std::lock_guard < std::mutex > lock2(sq.m_mutex);
-
- m_queue = sq.m_queue;
- m_max_num_items = sq.m_max_num_items;
-
- if (!m_queue.empty())
- m_cond_not_empty.notify_all();
- }
-
- return *this;
- }
-
- /*! The move assignment operator */
- ThreadQueue& operator=(ThreadQueue && sq) {
- std::lock_guard < std::mutex > lock(m_mutex);
- m_queue = std::move(sq.m_queue);
- m_max_num_items = sq.m_max_num_items;
-
- if (!m_queue.empty())
- m_cond_not_empty.notify_all();
-
- return *this;
- }
-
-private:
-
- std::deque<T> m_queue;
-
- mutable std::mutex m_mutex;
- std::condition_variable m_cond_not_empty;
- size_t m_max_num_items;
-};
-
-/*! Swaps the contents of two ThreadQueue objects. */
-template<typename T>
-void swap(ThreadQueue<T>& q1, ThreadQueue<T>& q2) {
- q1.swap(q2);
-}
diff --git a/src/util/Timer.cpp b/src/util/Timer.cpp
index d9d0b37..385681e 100644
--- a/src/util/Timer.cpp
+++ b/src/util/Timer.cpp
@@ -4,165 +4,175 @@
#include "Timer.h"
#ifdef _WIN32
- #include <windows.h>
- #include <mmsystem.h>
+ #include <windows.h>
#endif
#include <iostream>
Timer::Timer(void) : time_elapsed(0), system_milliseconds(0), start_time(0), end_time(0), last_update(0), num_updates(0), paused_time(0), offset(0), paused_state(false), lock_state(false), lock_rate(0)
{
+#ifdef _WIN32
+ // According to Microsoft, QueryPerformanceXXX API is perfectly
+ //fine for Windows 7+ systems, and use the highest appropriate counter.
+ //this only need to be done once.
+ ::QueryPerformanceFrequency(&win_frequency);
+#endif
}
void Timer::start(void)
{
- update();
- num_updates = 0;
- start_time = system_milliseconds;
- last_update = start_time;
- paused_state = false;
- lock_state = false;
- lock_rate = 0;
- paused_time = 0;
- offset = 0;
+ update();
+ num_updates = 0;
+ start_time = system_milliseconds;
+ last_update = start_time;
+ paused_state = false;
+ lock_state = false;
+ lock_rate = 0;
+ paused_time = 0;
+ offset = 0;
}
void Timer::stop(void)
{
- end_time = system_milliseconds;
+ end_time = system_milliseconds;
}
void Timer::reset(void)
{
- start();
+ start();
}
void Timer::lockFramerate(float f_rate)
{
- lock_rate = 1.0f/f_rate;
- lock_state = true;
+ lock_rate = 1.0f/f_rate;
+ lock_state = true;
}
void Timer::unlock()
{
- unsigned long msec_tmp = system_milliseconds;
-
- lock_state = false;
+ unsigned long msec_tmp = system_milliseconds;
+
+ lock_state = false;
- update();
-
- last_update = system_milliseconds-(unsigned long)lock_rate;
-
- offset += msec_tmp-system_milliseconds;
-
- lock_rate = 0;
+ update();
+
+ last_update = system_milliseconds-(unsigned long)lock_rate;
+
+ offset += msec_tmp-system_milliseconds;
+
+ lock_rate = 0;
}
bool Timer::locked()
{
- return lock_state;
+ return lock_state;
}
void Timer::update(void)
{
- num_updates++;
- last_update = system_milliseconds;
-
-
- if (lock_state)
- {
- system_milliseconds += (unsigned long)(lock_rate*1000.0);
- }
- else
- {
+ num_updates++;
+ last_update = system_milliseconds;
+
+
+ if (lock_state)
+ {
+ system_milliseconds += (unsigned long)(lock_rate*1000.0);
+ }
+ else
+ {
#ifdef _WIN32
- system_milliseconds = timeGetTime ();
+ //Use QuaryPerformanceCounter, imune to problems sometimes
+ //multimedia timers have.
+ LARGE_INTEGER win_current_count;
+ ::QueryPerformanceCounter(&win_current_count);
+
+ system_milliseconds = (unsigned long)(win_current_count.QuadPart * 1000.0 / win_frequency.QuadPart);
#else
- gettimeofday(&time_val,&time_zone);
+ gettimeofday(&time_val,&time_zone);
- system_milliseconds = (unsigned long)time_val.tv_usec;
- system_milliseconds /= 1000;
- system_milliseconds += (unsigned long)(time_val.tv_sec*1000);
+ system_milliseconds = (unsigned long)time_val.tv_usec;
+ system_milliseconds /= 1000;
+ system_milliseconds += (unsigned long)(time_val.tv_sec*1000);
#endif
- }
+ }
- if (paused_state) paused_time += system_milliseconds-last_update;
+ if (paused_state) paused_time += system_milliseconds-last_update;
- time_elapsed = system_milliseconds-start_time-paused_time+offset;
+ time_elapsed = system_milliseconds-start_time-paused_time+offset;
}
unsigned long Timer::getMilliseconds(void)
{
- return time_elapsed;
+ return time_elapsed;
}
double Timer::getSeconds(void)
{
- return ((double)getMilliseconds())/1000.0;
+ return ((double)getMilliseconds())/1000.0;
}
void Timer::setMilliseconds(unsigned long milliseconds_in)
{
- offset -= (system_milliseconds-start_time-paused_time+offset)-milliseconds_in;
+ offset -= (system_milliseconds-start_time-paused_time+offset)-milliseconds_in;
}
void Timer::setSeconds(double seconds_in)
{
- setMilliseconds((long)(seconds_in*1000.0));
+ setMilliseconds((long)(seconds_in*1000.0));
}
double Timer::lastUpdateSeconds(void)
{
- return ((double)lastUpdateMilliseconds())/1000.0;
+ return ((double)lastUpdateMilliseconds())/1000.0;
}
unsigned long Timer::lastUpdateMilliseconds(void)
{
- return system_milliseconds-last_update;
+ return system_milliseconds-last_update;
}
unsigned long Timer::totalMilliseconds()
{
- return system_milliseconds-start_time;
+ return system_milliseconds-start_time;
}
double Timer::totalSeconds(void)
{
- return totalMilliseconds()/1000.0;
+ return totalMilliseconds()/1000.0;
}
unsigned long Timer::getNumUpdates(void)
{
- return num_updates;
+ return num_updates;
}
void Timer::paused(bool pause_in)
{
- paused_state = pause_in;
+ paused_state = pause_in;
}
bool Timer::paused()
{
- return paused_state;
+ return paused_state;
}
void Timer::timerTestFunc() {
diff --git a/src/util/Timer.h b/src/util/Timer.h
index 95d3d15..b45b218 100644
--- a/src/util/Timer.h
+++ b/src/util/Timer.h
@@ -30,6 +30,8 @@ private:
#ifndef _WIN32
struct timeval time_val;
struct timezone time_zone;
+#else
+ LARGE_INTEGER win_frequency;
#endif
bool paused_state;
@@ -91,6 +93,7 @@ public:
* \return Total time elapsed since the timer start() to the last update() excluding time paused() in milliseconds
*/
unsigned long getMilliseconds(void);
+
/// Alias of getMilliseconds() which returns time in seconds
/**
* \return Total time elapsed since the timer start() to the last update() excluding time paused() in seconds
@@ -159,6 +162,7 @@ public:
*/
bool paused();
+
void timerTestFunc();
};
diff --git a/src/visual/GainCanvas.cpp b/src/visual/GainCanvas.cpp
index a5c0b56..70010cf 100644
--- a/src/visual/GainCanvas.cpp
+++ b/src/visual/GainCanvas.cpp
@@ -17,6 +17,7 @@
#include "CubicSDRDefs.h"
#include "AppFrame.h"
#include <algorithm>
+#include <cmath>
wxBEGIN_EVENT_TABLE(GainCanvas, wxGLCanvas) EVT_PAINT(GainCanvas::OnPaint)
EVT_IDLE(GainCanvas::OnIdle)
@@ -41,6 +42,8 @@ GainCanvas::GainCanvas(wxWindow *parent, std::vector<int> dispAttrs) :
startPos = spacing/2.0;
barHeight = 0.8f;
refreshCounter = 0;
+
+ userGainAsChanged = false;
}
GainCanvas::~GainCanvas() {
@@ -69,13 +72,40 @@ void GainCanvas::OnIdle(wxIdleEvent &event) {
} else {
event.Skip();
}
+
+ bool areGainsChangedHere = false;
for (auto gi : gainPanels) {
if (gi->getChanged()) {
- wxGetApp().setGain(gi->getName(), gi->getValue());
+ areGainsChangedHere = true;
+ // Gain only displays integer gain values, so set the applied gain
+ //value to exactly that.
+ wxGetApp().setGain(gi->getName(), (int)(gi->getValue()));
+ //A gain may be exposed as setting also so assure refresh of the menu also.
+ wxGetApp().notifyMainUIOfDeviceChange(false); //do not rebuild the gain UI
+
gi->setChanged(false);
}
}
+
+ //User input has changed the gain, so schedule an update of values
+ //in 150ms in the future, else the device may not have taken the value into account.
+ if (areGainsChangedHere) {
+ userGainAsChanged = true;
+ userGainAsChangedDelayTimer.start();
+ }
+ else {
+ userGainAsChangedDelayTimer.update();
+
+ if (!userGainAsChanged || (userGainAsChanged && userGainAsChangedDelayTimer.getMilliseconds() > 150)) {
+
+ if (updateGainValues()) {
+ Refresh();
+ }
+
+ userGainAsChanged = false;
+ }
+ }
}
void GainCanvas::SetLevel() {
@@ -83,11 +113,10 @@ void GainCanvas::SetLevel() {
for (auto gi : gainPanels) {
if (gi->isMeterHit(mpos)) {
- float value = gi->getMeterHitValue(mpos, *gi);
+ float value = gi->getMeterHitValue(mpos);
gi->setValue(value);
- gi->setChanged(true);
-
+ gi->setChanged(true);
break;
}
}
@@ -100,7 +129,7 @@ void GainCanvas::OnMouseMoved(wxMouseEvent& event) {
for (auto gi : gainPanels) {
if (gi->isMeterHit(mpos)) {
- float value = gi->getMeterHitValue(mpos, *gi);
+ float value = gi->getMeterHitValue(mpos);
gi->setHighlight(value);
gi->setHighlightVisible(true);
@@ -167,13 +196,12 @@ void GainCanvas::OnMouseEnterWindow(wxMouseEvent& event) {
#endif
}
-
-
void GainCanvas::setHelpTip(std::string tip) {
helpTip = tip;
}
void GainCanvas::updateGainUI() {
+
SDRDeviceInfo *devInfo = wxGetApp().getDevice();
//possible if we 'Refresh Devices' then devInfo becomes null
@@ -183,9 +211,14 @@ void GainCanvas::updateGainUI() {
}
DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(devInfo->getDeviceId());
-
+
+ //read the gains from the device.
+ //This may be wrong because the device is not started, or has yet
+ //to take into account a user gain change. Doesn't matter,
+ //UpdateGainValues() takes cares of updating the true value realtime.
gains = devInfo->getGains(SOAPY_SDR_RX, 0);
- SDRRangeMap::iterator gi;
+
+ SDRRangeMap::iterator gi;
numGains = gains.size();
float i = 0;
@@ -205,7 +238,7 @@ void GainCanvas::updateGainUI() {
bgPanel.removeChild(mDel);
delete mDel;
}
-
+
for (auto gi : gains) {
MeterPanel *mPanel = new MeterPanel(gi.first, gi.second.minimum(), gi.second.maximum(), devConfig->getGain(gi.first,wxGetApp().getGain(gi.first)));
@@ -217,10 +250,67 @@ void GainCanvas::updateGainUI() {
gainPanels.push_back(mPanel);
i++;
}
-
+
setThemeColors();
}
+// call this to refresh the gain values only, not the whole UI.
+bool GainCanvas::updateGainValues() {
+
+ bool isRefreshNeeded = false;
+
+ SDRDeviceInfo *devInfo = wxGetApp().getDevice();
+
+ //possible if we 'Refresh Devices' then devInfo becomes null
+ //until a new device is selected.
+ //also, do not attempt an update with the device is not started.
+ if (devInfo == nullptr || !devInfo->isActive()) {
+ return false;
+ }
+
+ DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(devInfo->getDeviceId());
+
+ gains = devInfo->getGains(SOAPY_SDR_RX, 0);
+ SDRRangeMap::iterator gi;
+
+ size_t numGainsToRefresh = std::min(gains.size(), gainPanels.size());
+ size_t panelIndex = 0;
+
+ //actually the order of gains iteration should be constant because map of string,
+ //and gainPanels were built in that order in updateGainUI()
+ for (auto gi : gains) {
+
+ if (panelIndex >= numGainsToRefresh) {
+ break;
+ }
+
+ // do not update if a change is already pending.
+ if (!gainPanels[panelIndex]->getChanged()) {
+
+ //read the actual gain from the device, round it
+ float actualRoundedGain = (float)std::round(devInfo->getCurrentGain(SOAPY_SDR_RX, 0, gi.first));
+
+ //do nothing if the difference is less than 1.0, since the panel do not show it anyway.
+ if ((int)actualRoundedGain != (int)(gainPanels[panelIndex]->getValue())) {
+
+ gainPanels[panelIndex]->setValue(actualRoundedGain);
+
+ //update the config with this value :
+ //a consequence of such updates is that the use setting
+ // is overriden by the current one in AGC mode.
+ //TODO: if it not desirable, do not update in AGC mode.
+ devConfig->setGain(gi.first, actualRoundedGain);
+
+ isRefreshNeeded = true;
+ }
+ } //end if no external change pending.
+
+ panelIndex++;
+ }
+
+ return isRefreshNeeded;
+}
+
void GainCanvas::setThemeColors() {
RGBA4f c1, c2;
diff --git a/src/visual/GainCanvas.h b/src/visual/GainCanvas.h
index 2e7a552..323724b 100644
--- a/src/visual/GainCanvas.h
+++ b/src/visual/GainCanvas.h
@@ -8,6 +8,7 @@
#include <vector>
#include <queue>
+#include <atomic>
#include "InteractiveCanvas.h"
#include "MouseTracker.h"
@@ -25,9 +26,13 @@ public:
void setHelpTip(std::string tip);
void updateGainUI();
- void setThemeColors();
+ void setThemeColors();
private:
+
+ // call this to refresh the gain values only, return true if refresh is needed
+ bool updateGainValues();
+
void OnPaint(wxPaintEvent& event);
void OnIdle(wxIdleEvent &event);
@@ -50,6 +55,9 @@ private:
float spacing, barWidth, startPos, barHeight, numGains;
int refreshCounter;
wxSize clientSize;
+
+ std::atomic_bool userGainAsChanged;
+ Timer userGainAsChangedDelayTimer;
//
wxDECLARE_EVENT_TABLE();
};
diff --git a/src/visual/InteractiveCanvas.h b/src/visual/InteractiveCanvas.h
index 3ffb173..091a9f6 100644
--- a/src/visual/InteractiveCanvas.h
+++ b/src/visual/InteractiveCanvas.h
@@ -13,7 +13,7 @@
class InteractiveCanvas: public wxGLCanvas {
public:
InteractiveCanvas(wxWindow *parent, std::vector<int> dispAttrs);
- ~InteractiveCanvas();
+ virtual ~InteractiveCanvas();
long long getFrequencyAt(float x);
long long getFrequencyAt(float x, long long iqCenterFreq, long long iqBandwidth);
diff --git a/src/visual/PrimaryGLContext.cpp b/src/visual/PrimaryGLContext.cpp
index 72f0be5..cd56408 100644
--- a/src/visual/PrimaryGLContext.cpp
+++ b/src/visual/PrimaryGLContext.cpp
@@ -62,7 +62,7 @@ PrimaryGLContext::PrimaryGLContext(wxGLCanvas *canvas, wxGLContext *sharedContex
//#endif
}
-void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, RGBA4f color, long long center_freq, long long srate, bool centerline) {
+void PrimaryGLContext::DrawDemodInfo(DemodulatorInstancePtr demod, RGBA4f color, long long center_freq, long long srate, bool centerline) {
if (!demod) {
return;
}
@@ -104,17 +104,26 @@ void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, RGBA4f color, l
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
bool soloMode = wxGetApp().getSoloMode();
+ bool isRecording = demod->isRecording();
bool isSolo = soloMode && demod == wxGetApp().getDemodMgr().getLastActiveDemodulator();
+ RGBA4f labelBg(0, 0, 0, 0.35f);
+
if (isSolo) {
- glColor4f(0.8f, 0.8f, 0, 0.35f);
+ labelBg.r = labelBg.g = 0.8f;
} else if (demod->isMuted()) {
- glColor4f(0.8f, 0, 0, 0.35f);
+ labelBg.r = 0.8f;
} else if (soloMode) {
- glColor4f(0.2f, 0, 0, 0.35f);
- } else {
- glColor4f(0, 0, 0, 0.35f);
+ labelBg.r = 0.2f;
+ }
+
+ // TODO: Better recording indicator... pulsating red circle?
+ if (isRecording) {
+ auto t = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
+ labelBg.g = sinf(2.0f * M_PI * (float(t) / 1000.0f)) * 0.25f + 0.75f;
}
+
+ glColor4f(labelBg.r, labelBg.g, labelBg.b, labelBg.a);
glBegin(GL_QUADS);
glVertex3f(uxPos - ofsLeft, hPos + labelHeight, 0.0);
@@ -156,18 +165,29 @@ void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, RGBA4f color, l
glColor4f(1.0, 1.0, 1.0, 0.8f);
- std::string demodLabel = demod->getLabel();
-
+ std::string demodLabel, demodPrefix;
+
+ if (demod->isDeltaLock()) {
+ demodPrefix.append("V");
+ }
+
+ if (isRecording) {
+ demodPrefix.append("R");
+ }
+
if (demod->isMuted()) {
- demodLabel = std::string("[M] ") + demodLabel;
+ demodPrefix.append("M");
} else if (isSolo) {
- demodLabel = std::string("[S] ") + demodLabel;
+ demodPrefix.append("S");
}
-
- if (demod->isDeltaLock()) {
- demodLabel.append(" [V]");
- }
-
+
+ // Set the prefix
+ if (!demodPrefix.empty()) {
+ demodLabel = "[" + demodPrefix + "] ";
+ }
+ // Append the default label
+ demodLabel.append(demod->getLabel());
+
if (demod->getDemodulatorType() == "USB") {
GLFont::getFont(16, GLFont::getScaleFactor()).drawString(demodLabel, uxPos, hPos, GLFont::GLFONT_ALIGN_LEFT, GLFont::GLFONT_ALIGN_CENTER, 0, 0, true);
} else if (demod->getDemodulatorType() == "LSB") {
@@ -287,7 +307,7 @@ void PrimaryGLContext::DrawFreqBwInfo(long long freq, int bw, RGBA4f color, long
glDisable(GL_BLEND);
}
-void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, RGBA4f color, long long center_freq, long long srate) {
+void PrimaryGLContext::DrawDemod(DemodulatorInstancePtr demod, RGBA4f color, long long center_freq, long long srate) {
if (!demod) {
return;
}
@@ -356,20 +376,14 @@ void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, RGBA4f color, long
glEnable(GL_BLEND);
- GLFont::Align demodAlign = GLFont::GLFONT_ALIGN_CENTER;
-
//Displayed string is wstring, so use wxString to do the heavy lifting of converting getDemodulatorType()...
wxString demodStr;
demodStr.assign(demod->getDemodulatorType());
- demodAlign = GLFont::GLFONT_ALIGN_CENTER;
-
if (demodStr == "LSB") {
- demodAlign = GLFont::GLFONT_ALIGN_RIGHT;
uxPos -= xOfs;
} else if (demodStr == "USB") {
- demodAlign = GLFont::GLFONT_ALIGN_LEFT;
uxPos += xOfs;
}
// advanced demodulators start here
@@ -415,7 +429,8 @@ void PrimaryGLContext::drawSingleDemodLabel(const std::wstring& demodStr, float
}
void PrimaryGLContext::DrawFreqSelector(float uxPos, RGBA4f color, float w, long long /* center_freq */, long long srate) {
- DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
+
+ DemodulatorInstancePtr demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
long long bw = 0;
diff --git a/src/visual/PrimaryGLContext.h b/src/visual/PrimaryGLContext.h
index f421def..a799e75 100644
--- a/src/visual/PrimaryGLContext.h
+++ b/src/visual/PrimaryGLContext.h
@@ -26,9 +26,9 @@ public:
void DrawFreqSelector(float uxPos, RGBA4f color, float w = 0, long long center_freq = -1, long long srate = 0);
void DrawRangeSelector(float uxPos1, float uxPos2, RGBA4f color);
- void DrawDemod(DemodulatorInstance *demod, RGBA4f color, long long center_freq = -1, long long srate = 0);
+ void DrawDemod(DemodulatorInstancePtr demod, RGBA4f color, long long center_freq = -1, long long srate = 0);
- void DrawDemodInfo(DemodulatorInstance *demod, RGBA4f color, long long center_freq = -1, long long srate = 0, bool centerline = false);
+ void DrawDemodInfo(DemodulatorInstancePtr demod, RGBA4f color, long long center_freq = -1, long long srate = 0, bool centerline = false);
void DrawFreqBwInfo(long long freq, int bw, RGBA4f color, long long center_freq = - 1, long long srate = 0, bool stack = false, bool centerline = false);
void setHoverAlpha(float hoverAlpha);
diff --git a/src/visual/ScopeCanvas.cpp b/src/visual/ScopeCanvas.cpp
index 5371513..1f8fc80 100644
--- a/src/visual/ScopeCanvas.cpp
+++ b/src/visual/ScopeCanvas.cpp
@@ -34,7 +34,7 @@ wxEND_EVENT_TABLE()
ScopeCanvas::ScopeCanvas(wxWindow *parent, std::vector<int> dispAttrs) : InteractiveCanvas(parent, dispAttrs), ppmMode(false), ctr(0), ctrTarget(0), dragAccel(0), helpTip("") {
glContext = new ScopeContext(this, &wxGetApp().GetContext(this));
- inputData.set_max_num_items(2);
+ inputData->set_max_num_items(2);
bgPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_Y);
bgPanel.setSize(1.0, 0.5f);
bgPanel.setPosition(0.0, -0.5f);
@@ -104,8 +104,8 @@ void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
wxPaintDC dc(this);
const wxSize ClientSize = GetClientSize();
- ScopeRenderData *avData;
- while (inputData.try_pop(avData)) {
+ ScopeRenderDataPtr avData;
+ while (inputData->try_pop(avData)) {
if (!avData->spectrum) {
@@ -113,7 +113,7 @@ void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
if (avData->waveform_points.size()) {
scopePanel.setPoints(avData->waveform_points);
}
- avData->decRefCount();
+
} else {
if (avData->waveform_points.size()) {
spectrumPanel.setPoints(avData->waveform_points);
@@ -124,8 +124,7 @@ void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
spectrumPanel.setFFTSize(avData->fft_size);
spectrumPanel.setShowDb(showDb);
}
-
- avData->decRefCount();
+
}
}
@@ -149,7 +148,7 @@ void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
- glLoadMatrixf(CubicVR::mat4::perspective(45.0, 1.0, 1.0, 1000.0));
+ glLoadMatrixf(CubicVR::mat4::perspective(45.0, 1.0, 1.0, 1000.0).to_ptr());
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
@@ -211,7 +210,7 @@ void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
spectrumPanel.drawChildren();
}
- glLoadMatrixf(scopePanel.transform);
+ glLoadMatrixf(scopePanel.transform.to_ptr());
if (!deviceName.empty()) {
glContext->DrawDeviceName(deviceName);
}
@@ -234,8 +233,8 @@ void ScopeCanvas::OnIdle(wxIdleEvent &event) {
event.RequestMore();
}
-ScopeRenderDataQueue *ScopeCanvas::getInputQueue() {
- return &inputData;
+ScopeRenderDataQueuePtr ScopeCanvas::getInputQueue() {
+ return inputData;
}
void ScopeCanvas::OnMouseMoved(wxMouseEvent& event) {
diff --git a/src/visual/ScopeCanvas.h b/src/visual/ScopeCanvas.h
index 7511437..bf16eb9 100644
--- a/src/visual/ScopeCanvas.h
+++ b/src/visual/ScopeCanvas.h
@@ -8,6 +8,7 @@
#include <vector>
#include <queue>
+#include <memory>
#include "ScopeContext.h"
#include "ScopeVisualProcessor.h"
@@ -42,7 +43,7 @@ public:
void setHelpTip(std::string tip);
- ScopeRenderDataQueue *getInputQueue();
+ ScopeRenderDataQueuePtr getInputQueue();
private:
void OnPaint(wxPaintEvent& event);
@@ -54,7 +55,7 @@ private:
void OnMouseEnterWindow(wxMouseEvent& event);
void OnMouseLeftWindow(wxMouseEvent& event);
- ScopeRenderDataQueue inputData;
+ ScopeRenderDataQueuePtr inputData = std::make_shared<ScopeRenderDataQueue>();
ScopePanel scopePanel;
GLPanel parentPanel;
SpectrumPanel spectrumPanel;
diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp
index 742bb2c..4630f9b 100644
--- a/src/visual/SpectrumCanvas.cpp
+++ b/src/visual/SpectrumCanvas.cpp
@@ -37,7 +37,7 @@ SpectrumCanvas::SpectrumCanvas(wxWindow *parent, std::vector<int> dispAttrs) :
glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this));
- visualDataQueue.set_max_num_items(1);
+ visualDataQueue->set_max_num_items(1);
SetCursor(wxCURSOR_SIZEWE);
scaleFactor = 1.0;
@@ -54,15 +54,14 @@ void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
wxPaintDC dc(this);
const wxSize ClientSize = GetClientSize();
- SpectrumVisualData *vData;
- if (visualDataQueue.try_pop(vData)) {
+ SpectrumVisualDataPtr vData;
+ if (visualDataQueue->try_pop(vData)) {
if (vData) {
spectrumPanel.setPoints(vData->spectrum_points);
spectrumPanel.setPeakPoints(vData->spectrum_hold_points);
spectrumPanel.setFloorValue(vData->fft_floor);
spectrumPanel.setCeilValue(vData->fft_ceiling);
- vData->decRefCount();
}
}
@@ -91,9 +90,8 @@ void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
glLoadIdentity();
- std::vector<DemodulatorInstance *> &demods = wxGetApp().getDemodMgr().getDemodulators();
-
- DemodulatorInstance *activeDemodulator = wxGetApp().getDemodMgr().getActiveDemodulator();
+ auto demods = wxGetApp().getDemodMgr().getDemodulators();
+ auto activeDemodulator = wxGetApp().getDemodMgr().getActiveDemodulator();
for (int i = 0, iMax = demods.size(); i < iMax; i++) {
if (!demods[i]->isActive()) {
@@ -113,7 +111,7 @@ void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
freq = roundf((float)freq/(float)snap)*snap;
}
- DemodulatorInstance *lastActiveDemodulator = wxGetApp().getDemodMgr().getLastActiveDemodulator();
+ auto lastActiveDemodulator = wxGetApp().getDemodMgr().getLastActiveDemodulator();
bool isNew = (((waterfallCanvas->isShiftDown() || (lastActiveDemodulator && !lastActiveDemodulator->isActive())) && lastActiveDemodulator) || (!lastActiveDemodulator));
@@ -287,8 +285,8 @@ void SpectrumCanvas::attachWaterfallCanvas(WaterfallCanvas* canvas_in) {
waterfallCanvas = canvas_in;
}
-SpectrumVisualDataQueue *SpectrumCanvas::getVisualDataQueue() {
- return &visualDataQueue;
+SpectrumVisualDataQueuePtr SpectrumCanvas::getVisualDataQueue() {
+ return visualDataQueue;
}
void SpectrumCanvas::OnMouseRightDown(wxMouseEvent& event) {
diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h
index 68374aa..580d917 100644
--- a/src/visual/SpectrumCanvas.h
+++ b/src/visual/SpectrumCanvas.h
@@ -5,6 +5,7 @@
#include <vector>
#include <queue>
+#include <memory>
#include "InteractiveCanvas.h"
#include "PrimaryGLContext.h"
@@ -44,7 +45,7 @@ public:
void setScaleFactorEnabled(bool en);
void setFFTSize(int fftSize);
- SpectrumVisualDataQueue *getVisualDataQueue();
+ SpectrumVisualDataQueuePtr getVisualDataQueue();
private:
void OnPaint(wxPaintEvent& event);
@@ -70,7 +71,7 @@ private:
int bwChange;
bool resetScaleFactor, scaleFactorEnabled;
- SpectrumVisualDataQueue visualDataQueue;
+ SpectrumVisualDataQueuePtr visualDataQueue = std::make_shared<SpectrumVisualDataQueue>();
// event table
wxDECLARE_EVENT_TABLE();
diff --git a/src/visual/TuningCanvas.cpp b/src/visual/TuningCanvas.cpp
index 9ee6b3e..66ebabe 100644
--- a/src/visual/TuningCanvas.cpp
+++ b/src/visual/TuningCanvas.cpp
@@ -61,10 +61,11 @@ TuningCanvas::~TuningCanvas() {
}
bool TuningCanvas::changed() {
- DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
+
+ auto activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
long long current_freq = 0;
- if (activeDemod != NULL) {
+ if (activeDemod != nullptr) {
freq = activeDemod->getFrequency();
}
long long current_bw = wxGetApp().getDemodMgr().getLastBandwidth();
@@ -92,10 +93,10 @@ void TuningCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
glContext->DrawBegin();
- DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
+ auto activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
freq = 0;
- if (activeDemod != NULL) {
+ if (activeDemod != nullptr) {
freq = activeDemod->getFrequency();
}
bw = wxGetApp().getDemodMgr().getLastBandwidth();
@@ -170,7 +171,7 @@ void TuningCanvas::StepTuner(ActiveState state, int exponent, bool up) {
amount *= 2;
}
- DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
+ auto activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
if (state == TUNING_HOVER_FREQ && activeDemod) {
long long freq = activeDemod->getFrequency();
long long diff = abs(wxGetApp().getFrequency() - freq);
@@ -245,6 +246,7 @@ void TuningCanvas::StepTuner(ActiveState state, int exponent, bool up) {
}
wxGetApp().setPPM(currentPPM);
+ wxGetApp().notifyMainUIOfDeviceChange();
}
}
@@ -329,7 +331,7 @@ void TuningCanvas::OnMouseMoved(wxMouseEvent& event) {
if (hoverState == TUNING_HOVER_BW || hoverState == TUNING_HOVER_FREQ) {
wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getLastActiveDemodulator());
} else {
- wxGetApp().getDemodMgr().setActiveDemodulator(NULL);
+ wxGetApp().getDemodMgr().setActiveDemodulator(nullptr);
}
}
@@ -399,7 +401,7 @@ void TuningCanvas::OnMouseLeftWindow(wxMouseEvent& event) {
SetCursor(wxCURSOR_CROSS);
hoverIndex = 0;
hoverState = TUNING_HOVER_NONE;
- wxGetApp().getDemodMgr().setActiveDemodulator(NULL);
+ wxGetApp().getDemodMgr().setActiveDemodulator(nullptr);
if (currentPPM != lastPPM) {
wxGetApp().saveConfig();
diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp
index b75ca92..8efd1e5 100644
--- a/src/visual/WaterfallCanvas.cpp
+++ b/src/visual/WaterfallCanvas.cpp
@@ -24,6 +24,8 @@
#include <wx/numformatter.h>
+#include "DemodulatorThread.h"
+
wxBEGIN_EVENT_TABLE(WaterfallCanvas, wxGLCanvas)
EVT_PAINT(WaterfallCanvas::OnPaint)
EVT_IDLE(WaterfallCanvas::OnIdle)
@@ -97,16 +99,16 @@ void WaterfallCanvas::processInputQueue() {
if (linesPerSecond) {
if (lpsIndex >= targetVis) {
while (lpsIndex >= targetVis) {
- SpectrumVisualData *vData;
+ SpectrumVisualDataPtr vData;
- if (visualDataQueue.try_pop(vData)) {
+ if (visualDataQueue->try_pop(vData)) {
if (vData) {
if (vData->spectrum_points.size() == fft_size * 2) {
waterfallPanel.setPoints(vData->spectrum_points);
}
waterfallPanel.step();
- vData->decRefCount();
+
updated = true;
}
lpsIndex-=targetVis;
@@ -263,10 +265,10 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
waterfallPanel.calcTransform(CubicVR::mat4::identity());
waterfallPanel.draw();
- std::vector<DemodulatorInstance *> &demods = wxGetApp().getDemodMgr().getDemodulators();
+ auto demods = wxGetApp().getDemodMgr().getDemodulators();
- DemodulatorInstance *activeDemodulator = wxGetApp().getDemodMgr().getActiveDemodulator();
- DemodulatorInstance *lastActiveDemodulator = wxGetApp().getDemodMgr().getLastActiveDemodulator();
+ auto activeDemodulator = wxGetApp().getDemodMgr().getActiveDemodulator();
+ auto lastActiveDemodulator = wxGetApp().getDemodMgr().getLastActiveDemodulator();
bool isNew = shiftDown
|| (wxGetApp().getDemodMgr().getLastActiveDemodulator() && !wxGetApp().getDemodMgr().getLastActiveDemodulator()->isActive());
@@ -307,9 +309,9 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
}
} else {
if (lastActiveDemodulator) {
- glContext->DrawDemod(lastActiveDemodulator, ((isNew && activeDemodulator == NULL) || (activeDemodulator != NULL))?currentTheme->waterfallHighlight:currentTheme->waterfallDestroy, currentCenterFreq, currentBandwidth);
+ glContext->DrawDemod(lastActiveDemodulator, ((isNew && activeDemodulator == nullptr) || (activeDemodulator != nullptr))?currentTheme->waterfallHighlight:currentTheme->waterfallDestroy, currentCenterFreq, currentBandwidth);
}
- if (activeDemodulator == NULL) {
+ if (activeDemodulator == nullptr) {
glContext->DrawFreqSelector(mouseTracker.getMouseX(), ((isNew && lastActiveDemodulator) || (!lastActiveDemodulator) )?currentTheme->waterfallNew:currentTheme->waterfallHover, 0, currentCenterFreq, currentBandwidth);
} else {
glContext->DrawDemod(activeDemodulator, currentTheme->waterfallHover, currentCenterFreq, currentBandwidth);
@@ -390,7 +392,7 @@ void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) {
void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
InteractiveCanvas::OnKeyDown(event);
- DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator();
+ auto activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator();
long long originalFreq = getCenterFrequency();
long long freq = originalFreq;
@@ -490,9 +492,9 @@ void WaterfallCanvas::OnIdle(wxIdleEvent &event) {
void WaterfallCanvas::updateHoverState() {
long long freqPos = getFrequencyAt(mouseTracker.getMouseX());
- std::vector<DemodulatorInstance *> demodsHover = wxGetApp().getDemodMgr().getDemodulatorsAt(freqPos, 15000);
+ auto demodsHover = wxGetApp().getDemodMgr().getDemodulatorsAt(freqPos, 15000);
- wxGetApp().getDemodMgr().setActiveDemodulator(NULL);
+ wxGetApp().getDemodMgr().setActiveDemodulator(nullptr);
if (altDown) {
nextDragState = WF_DRAG_RANGE;
@@ -506,10 +508,10 @@ void WaterfallCanvas::updateHoverState() {
} else if (demodsHover.size() && !shiftDown) {
long near_dist = getBandwidth();
- DemodulatorInstance *activeDemodulator = NULL;
+ DemodulatorInstancePtr activeDemodulator = nullptr;
for (int i = 0, iMax = demodsHover.size(); i < iMax; i++) {
- DemodulatorInstance *demod = demodsHover[i];
+ auto demod = demodsHover[i];
long long freqDiff = demod->getFrequency() - freqPos;
long halfBw = (demod->getBandwidth() / 2);
long long currentBw = getBandwidth();
@@ -537,7 +539,7 @@ void WaterfallCanvas::updateHoverState() {
}
}
- if (activeDemodulator == NULL) {
+ if (activeDemodulator == nullptr) {
nextDragState = WF_DRAG_NONE;
SetCursor(wxCURSOR_CROSS);
return;
@@ -563,35 +565,35 @@ void WaterfallCanvas::updateHoverState() {
mouseTracker.setVertDragLock(true);
mouseTracker.setHorizDragLock(false);
- setStatusText("Click and drag to change demodulator bandwidth. SPACE or numeric key for direct frequency input. [, ] to nudge, M for mute, D to delete, C to center, E to edit label.");
+ setStatusText("Drag to change bandwidth. SPACE or 0-9 for direct frequency input. [, ] to nudge, M for mute, D to delete, C to center, E to edit label, R to record.");
} else {
SetCursor(wxCURSOR_SIZING);
nextDragState = WF_DRAG_FREQUENCY;
mouseTracker.setVertDragLock(true);
mouseTracker.setHorizDragLock(false);
- setStatusText("Click and drag to change demodulator frequency; SPACE or numeric key for direct input. [, ] to nudge, M for mute, D to delete, C to center, E to edit label.");
+ setStatusText("Drag to change frequency; SPACE or 0-9 for direct input. [, ] to nudge, M for mute, D to delete, C to center, E to edit label, R to record.");
}
}
else {
SetCursor(wxCURSOR_CROSS);
nextDragState = WF_DRAG_NONE;
if (shiftDown) {
- setStatusText("Click to create a new demodulator or hold ALT to drag range, SPACE or numeric key for direct center frequency input.");
+ setStatusText("Click to create a new demodulator or hold ALT to drag new range.");
}
else {
setStatusText(
- "Click to set active demodulator frequency or hold ALT to drag range; hold SHIFT to create new. Right drag or wheel to Zoom. Arrow keys to navigate/zoom, C to center.");
+ "Click to set demodulator frequency or hold ALT to drag range; hold SHIFT to create new. Right drag or wheel to Zoom. Arrow keys to navigate/zoom, C to center. Shift-R record/stop all.");
}
}
}
void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) {
InteractiveCanvas::OnMouseMoved(event);
- DemodulatorInstance *demod = wxGetApp().getDemodMgr().getActiveDemodulator();
+ auto demod = wxGetApp().getDemodMgr().getActiveDemodulator();
if (mouseTracker.mouseDown()) {
- if (demod == NULL) {
+ if (demod == nullptr) {
return;
}
if (dragState == WF_DRAG_BANDWIDTH_LEFT || dragState == WF_DRAG_BANDWIDTH_RIGHT) {
@@ -650,7 +652,7 @@ void WaterfallCanvas::OnMouseDown(wxMouseEvent& event) {
wxGetApp().getDemodMgr().updateLastState();
if (dragState && dragState != WF_DRAG_RANGE) {
- DemodulatorInstance *demod = wxGetApp().getDemodMgr().getActiveDemodulator();
+ auto demod = wxGetApp().getDemodMgr().getActiveDemodulator();
if (demod) {
dragOfs = (long long) (mouseTracker.getMouseX() * (float) getBandwidth()) + getCenterFrequency() - (getBandwidth() / 2) - demod->getFrequency();
dragBW = demod->getBandwidth();
@@ -670,14 +672,14 @@ void WaterfallCanvas::OnMouseReleased(wxMouseEvent& event) {
InteractiveCanvas::OnMouseReleased(event);
wxGetApp().getDemodMgr().updateLastState();
- bool isNew = shiftDown || (wxGetApp().getDemodMgr().getLastActiveDemodulator() == NULL)
+ bool isNew = shiftDown || (wxGetApp().getDemodMgr().getLastActiveDemodulator() == nullptr)
|| (wxGetApp().getDemodMgr().getLastActiveDemodulator() && !wxGetApp().getDemodMgr().getLastActiveDemodulator()->isActive());
mouseTracker.setVertDragLock(false);
mouseTracker.setHorizDragLock(false);
- DemodulatorInstance *demod = isNew?NULL:wxGetApp().getDemodMgr().getLastActiveDemodulator();
- DemodulatorInstance *activeDemod = isNew?NULL:wxGetApp().getDemodMgr().getActiveDemodulator();
+ DemodulatorInstancePtr demod = isNew?nullptr:wxGetApp().getDemodMgr().getLastActiveDemodulator();
+ DemodulatorInstancePtr activeDemod = isNew?nullptr:wxGetApp().getDemodMgr().getActiveDemodulator();
DemodulatorMgr *mgr = &wxGetApp().getDemodMgr();
@@ -727,7 +729,7 @@ void WaterfallCanvas::OnMouseReleased(wxMouseEvent& event) {
demod->writeModemSettings(mgr->getLastModemSettings(mgr->getLastDemodulatorType()));
demod->run();
- wxGetApp().bindDemodulator(demod);
+ wxGetApp().notifyDemodulatorsChanged();
DemodulatorThread::releaseSquelchLock(nullptr);
}
@@ -827,10 +829,10 @@ void WaterfallCanvas::OnMouseReleased(wxMouseEvent& event) {
demod->run();
- wxGetApp().bindDemodulator(demod);
+ wxGetApp().notifyDemodulatorsChanged();
}
- if (demod == NULL) {
+ if (demod == nullptr) {
dragState = WF_DRAG_NONE;
return;
}
@@ -850,7 +852,7 @@ void WaterfallCanvas::OnMouseReleased(wxMouseEvent& event) {
void WaterfallCanvas::OnMouseLeftWindow(wxMouseEvent& event) {
InteractiveCanvas::OnMouseLeftWindow(event);
SetCursor(wxCURSOR_CROSS);
- wxGetApp().getDemodMgr().setActiveDemodulator(NULL);
+ wxGetApp().getDemodMgr().setActiveDemodulator(nullptr);
mouseZoom = 1.0;
}
@@ -880,8 +882,8 @@ void WaterfallCanvas::OnMouseRightReleased(wxMouseEvent& event) {
mouseZoom = 1.0;
}
-SpectrumVisualDataQueue *WaterfallCanvas::getVisualDataQueue() {
- return &visualDataQueue;
+SpectrumVisualDataQueuePtr WaterfallCanvas::getVisualDataQueue() {
+ return visualDataQueue;
}
void WaterfallCanvas::updateCenterFrequency(long long freq) {
@@ -915,13 +917,7 @@ void WaterfallCanvas::setLinesPerSecond(int lps) {
linesPerSecond = lps;
//empty all
- SpectrumVisualData *vData;
- while (visualDataQueue.try_pop(vData)) {
-
- if (vData) {
- vData->decRefCount();
- }
- }
+ visualDataQueue->flush();
}
void WaterfallCanvas::setMinBandwidth(int min) {
diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h
index 4f3ed31..21dea27 100644
--- a/src/visual/WaterfallCanvas.h
+++ b/src/visual/WaterfallCanvas.h
@@ -8,7 +8,7 @@
#include <vector>
#include <queue>
-
+#include <memory>
#include "InteractiveCanvas.h"
#include "MouseTracker.h"
#include "SpectrumCanvas.h"
@@ -31,7 +31,7 @@ public:
void attachSpectrumCanvas(SpectrumCanvas *canvas_in);
void processInputQueue();
- SpectrumVisualDataQueue *getVisualDataQueue();
+ SpectrumVisualDataQueuePtr getVisualDataQueue();
void setLinesPerSecond(int lps);
void setMinBandwidth(int min);
@@ -88,7 +88,8 @@ private:
float scaleMove;
int dragBW;
- SpectrumVisualDataQueue visualDataQueue;
+ SpectrumVisualDataQueuePtr visualDataQueue = std::make_shared<SpectrumVisualDataQueue>();
+
Timer gTimer;
double lpsIndex;
bool preBuf;
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-hamradio/cubicsdr.git
More information about the pkg-hamradio-commits
mailing list