Merge pull request #1542 from srcejon/android

MainWindow updates for Android and example configurations
This commit is contained in:
Edouard Griffiths 2022-12-21 09:24:12 +01:00 committed by GitHub
commit 53c1fdb877
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 1357 additions and 461 deletions

View File

@ -368,6 +368,106 @@ elseif (WIN32)
"${EXTERNAL_LIBRARY_FOLDER}/libusb/MS64/dll"
"${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/bin"
)
elseif(ANDROID)
set(EXTERNAL_LIBRARY_FOLDER "${CMAKE_SOURCE_DIR}/external/android")
set(FFTW3F_FOUND ON CACHE INTERNAL "")
set(FFTW3F_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/fftw-3/include" CACHE INTERNAL "")
set(FFTW3F_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/fftw-3/lib/libfftw3f.a" CACHE INTERNAL "")
set(OPUS_FOUND ON CACHE INTERNAL "")
set(OPUS_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/libopus/include" CACHE INTERNAL "")
set(OPUS_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/libopus/lib/libopus.a" CACHE INTERNAL "")
set(CODEC2_FOUND ON CACHE INTERNAL "")
set(CODEC2_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/codec2/include" CACHE INTERNAL "")
set(CODEC2_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/codec2/lib/libcodec2.a" CACHE INTERNAL "")
set(APT_FOUND ON CACHE INTERNAL "")
set(APT_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/aptdec/include/apt" CACHE INTERNAL "")
set(APT_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/aptdec/lib/libaptstatic.a" CACHE INTERNAL "")
set(SGP4_FOUND ON CACHE INTERNAL "")
set(SGP4_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/sgp4/include/libsgp4" CACHE INTERNAL "")
set(SGP4_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/sgp4/lib/libsgp4.a" CACHE INTERNAL "")
set(ZLIB_FOUND ON CACHE INTERNAL "")
set(ZLIB_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/zlib/include" CACHE INTERNAL "")
set(ZLIB_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/zlib/lib/libz.a" CACHE INTERNAL "")
set(FAAD_FOUND ON CACHE INTERNAL "")
set(FAAD_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/faad2/include" CACHE INTERNAL "")
set(FAAD_LIBRARY "${EXTERNAL_LIBRARY_FOLDER}/faad2/lib/liblibfaadstatic.a" CACHE INTERNAL "")
set(DAB_FOUND ON CACHE INTERNAL "")
set(DAB_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/libdab/include/dab_lib" CACHE INTERNAL "")
set(DAB_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/libdab/lib/libdab_lib.so" CACHE INTERNAL "")
set(LIBMBE_FOUND ON CACHE INTERNAL "")
set(LIBMBE_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/mbelib/include/" CACHE INTERNAL "")
set(LIBMBE_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/mbelib/lib/libmbe.a" CACHE INTERNAL "")
set(LIBDSDCC_FOUND ON CACHE INTERNAL "")
set(LIBDSDCC_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/dsdcc/include/" CACHE INTERNAL "")
set(LIBDSDCC_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/dsdcc/lib/libdsdcc.so" CACHE INTERNAL "")
set(FFMPEG_FOUND ON CACHE INTERNAL "")
set(FFMEG_SKIP_CHECK ON CACHE INTERNAL "")
set(FFMPEG_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/include" CACHE INTERNAL "")
set(FFMPEG_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/bin" CACHE INTERNAL "")
set(AVCODEC_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/include" CACHE INTERNAL "")
set(AVCODEC_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libavcodec.so" CACHE INTERNAL "")
set(AVFORMAT_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/include" CACHE INTERNAL "")
set(AVFORMAT_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libavformat.so" CACHE INTERNAL "")
set(AVUTIL_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/include" CACHE INTERNAL "")
set(AVUTIL_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libavutil.so" CACHE INTERNAL "")
set(SWRESAMPLE_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/include" CACHE INTERNAL "")
set(SWRESAMPLE_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libswresample.so" CACHE INTERNAL "")
set(SWSCALE_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/include" CACHE INTERNAL "")
set(SWSCALE_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libswscale.so" CACHE INTERNAL "")
set(LIBUSB_FOUND ON CACHE INTERNAL "")
set(LIBUSB_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/libusb/include/" CACHE INTERNAL "")
set(LIBUSB_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/libusb/lib/libusb1.0.so" CACHE INTERNAL "")
set(LIBAIRSPYHF_FOUND ON CACHE INTERNAL "")
set(LIBAIRSPYHF_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/airspyhf/include/" CACHE INTERNAL "")
set(LIBAIRSPYHF_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/airspyhf/lib/libairspyhf.a" CACHE INTERNAL "")
set(LIBAIRSPY_FOUND ON CACHE INTERNAL "")
set(LIBAIRSPY_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/airspy/include/" CACHE INTERNAL "")
set(LIBAIRSPY_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/airspy/lib/libairspy.a" CACHE INTERNAL "")
set(LIBRTLSDR_FOUND ON CACHE INTERNAL "")
set(LIBRTLSDR_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/librtlsdr/include/" CACHE INTERNAL "")
set(LIBRTLSDR_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/librtlsdr/lib/librtlsdr.a" CACHE INTERNAL "")
set(LIMESUITE_FOUND ON CACHE INTERNAL "")
set(LIMESUITE_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/limesuite/include/" CACHE INTERNAL "")
set(LIMESUITE_LIBRARY "${EXTERNAL_LIBRARY_FOLDER}/limesuite/lib/libLimeSuite.a" CACHE INTERNAL "")
set(ANDROID_EXTRA_LIBS
${Qt6_DIR}/../../../android_arm64_v8a/lib/libQt6Charts_arm64-v8a.so
${Qt6_DIR}/../../../android_arm64_v8a/lib/libQt6Concurrent_arm64-v8a.so
${Qt6_DIR}/../../../android_arm64_v8a/lib/libQt6MultimediaWidgets_arm64-v8a.so
${Qt6_DIR}/../../../android_arm64_v8a/lib/libQt6SerialPort_arm64-v8a.so
${Qt6_DIR}/../../../android_arm64_v8a/lib/libQt6TextToSpeech_arm64-v8a.so
${EXTERNAL_LIBRARY_FOLDER}/android_openssl/latest/arm64/libssl_1_1.so
${EXTERNAL_LIBRARY_FOLDER}/android_openssl/latest/arm64/libcrypto_1_1.so
${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libavcodec.so
${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libavdevice.so
${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libavfilter.so
${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libavformat.so
${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libavutil.so
${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libswresample.so
${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libswscale.so
${EXTERNAL_LIBRARY_FOLDER}/ffmpeg/lib/libc++_shared.so
${EXTERNAL_LIBRARY_FOLDER}/libdab/lib/libdab_lib.so
${EXTERNAL_LIBRARY_FOLDER}/dsdcc/lib/libdsdcc.so
${EXTERNAL_LIBRARY_FOLDER}/libusb/lib/libunrooted_android.so
${EXTERNAL_LIBRARY_FOLDER}/libusb/lib/libusb1.0.so
CACHE INTERNAL ""
)
endif()
# When building a bundle on MacOS, we want to keep build paths in the library
@ -440,35 +540,62 @@ endif()
# for the server we don't need OpenGL/Qt Quick components
if (BUILD_GUI)
find_package(OpenGL REQUIRED)
if (ENABLE_QT6)
find_package(Qt6
REQUIRED COMPONENTS
OpenGL
OpenGLWidgets
Quick
QuickWidgets
OPTIONAL_COMPONENTS
WebEngineQuick
WebEngineCore
WebEngineWidgets
TextToSpeech)
if(ANDROID)
# WebEngine not supported on Android
if (ENABLE_QT6)
find_package(Qt6
REQUIRED COMPONENTS
OpenGL
OpenGLWidgets
Quick
QuickWidgets
TextToSpeech
Svg)
else()
find_package(Qt5
REQUIRED COMPONENTS
OpenGL
Quick
QuickWidgets
Location
TextToSpeech
Svg
AndroidExtras)
endif()
else()
find_package(Qt5
REQUIRED COMPONENTS
OpenGL
Quick
QuickWidgets
Location
TextToSpeech
WebEngine
WebEngineCore
WebEngineWidgets)
find_package(OpenGL REQUIRED)
if (ENABLE_QT6)
find_package(Qt6
REQUIRED COMPONENTS
OpenGL
OpenGLWidgets
Quick
QuickWidgets
OPTIONAL_COMPONENTS
WebEngineQuick
WebEngineCore
WebEngineWidgets
TextToSpeech)
else()
find_package(Qt5
REQUIRED COMPONENTS
OpenGL
Quick
QuickWidgets
Location
TextToSpeech
OPTIONAL_COMPONENTS
WebEngine
WebEngineCore
WebEngineWidgets)
endif()
endif()
endif()
# other requirements
find_package(PkgConfig REQUIRED)
if(NOT ANDROID)
find_package(PkgConfig REQUIRED)
endif()
if(APPLE)
find_package(ICONV)
@ -506,7 +633,9 @@ if(ENABLE_EXTERNAL_LIBRARIES)
else()
find_package(Boost REQUIRED)
find_package(FFTW3F REQUIRED)
find_package(LibUSB REQUIRED) # used by so many packages
if(NOT ANDROID)
find_package(LibUSB REQUIRED) # used by so many packages
endif()
find_package(OpenCV OPTIONAL_COMPONENTS core highgui imgproc imgcodecs videoio) # channeltx/modatv
find_package(LibSigMF) # SigMF recording files support
find_package(ZLIB) # For DAB
@ -514,7 +643,7 @@ else()
find_package(Codec2)
find_package(CM256cc)
find_package(LibMbe)
find_package(SerialDV REQUIRED)
find_package(SerialDV)
find_package(LibDSDcc)
find_package(Sgp4)
find_package(AptDec)
@ -608,8 +737,12 @@ include_directories(
${CMAKE_SOURCE_DIR}/sdrsrv
${CMAKE_SOURCE_DIR}/sdrbench
${CMAKE_SOURCE_DIR}/logging
${OPENGL_INCLUDE_DIR}
)
if(NOT ANDROID)
include_directories(
${OPENGL_INCLUDE_DIR}
)
endif()
# Set up optional make target for developer doc
find_package(Doxygen)
@ -669,20 +802,61 @@ if (BUILD_GUI)
set(sdrangel_SOURCES
app/main.cpp
sdrgui/resources/sdrangel.rc
settings/settings.qrc
)
add_executable(${CMAKE_PROJECT_NAME}
${sdrangel_SOURCES}
)
if(ANDROID AND NOT ENABLE_QT6)
add_library(${CMAKE_PROJECT_NAME} SHARED ${sdrangel_SOURCES})
elseif(ANDROID)
qt_add_executable(${CMAKE_PROJECT_NAME} ${sdrangel_SOURCES})
set_property(TARGET ${CMAKE_PROJECT_NAME} PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_SOURCE_DIR}/android/qt6)
set_property(TARGET ${CMAKE_PROJECT_NAME} PROPERTY QT_ANDROID_EXTRA_PLUGINS ${CMAKE_BINARY_DIR}/lib/plugins)
set_property(TARGET ${CMAKE_PROJECT_NAME} PROPERTY QT_ANDROID_EXTRA_LIBS ${ANDROID_EXTRA_LIBS})
else()
add_executable(${CMAKE_PROJECT_NAME} ${sdrangel_SOURCES})
endif()
target_link_libraries(${CMAKE_PROJECT_NAME}
${OPENGL_LIBRARIES}
Qt::Widgets
Qt::Multimedia
sdrbase
sdrgui
logging
)
if(ANDROID)
if(ENABLE_QT6)
target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC
${OPENGL_LIBRARIES}
Qt::Widgets
Qt::Multimedia
Qt::Svg
Qt::QmlModels
Qt::Quick
sdrbase
sdrgui
logging
android
log
)
else()
target_link_libraries(${CMAKE_PROJECT_NAME}
${OPENGL_LIBRARIES}
Qt::Widgets
Qt::Multimedia
Qt::Svg
Qt::QmlModels
Qt::Quick
Qt::AndroidExtras
sdrbase
sdrgui
logging
android
log
)
endif()
else()
target_link_libraries(${CMAKE_PROJECT_NAME}
${OPENGL_LIBRARIES}
Qt::Widgets
Qt::Multimedia
sdrbase
sdrgui
logging
)
endif()
if(WIN32)
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES

View File

@ -26,6 +26,9 @@
#include <QGLFormat>
#include <QSurfaceFormat>
#endif
#ifdef ANDROID
#include "util/android.h"
#endif
#include "loggerwithfile.h"
#include "mainwindow.h"
@ -49,6 +52,9 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) && (QT_VERSION <= QT_VERSION_CHECK(6, 0, 0))
QApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
#endif
#ifndef ANDROID
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs); // Don't use on Android, otherwise we can't access files on internal storage
#endif
QApplication a(argc, argv);
@ -111,6 +117,20 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo
#endif
#endif
#ifdef ANDROID
// Default sized sliders can be hard to move using touch GUIs, so increase szie
// FIXME: How can we do a double border around the handle, as Fusion style seems to use?
// Dialog borders are hard to see as is (perhaps as Android doesn't have a title bar), so use same color as for MDI
qApp->setStyleSheet("QSlider {min-height: 20px; } "
"QSlider::groove:horizontal { border: 1px solid #2e2e2e; height: 1px; background: #444444; margin: 1px 0;}"
"QSlider::handle:horizontal { background: #585858; border: 1px double #676767; width: 16px; margin: -8px 0px; border-radius: 3px;}"
"QSlider::sub-page {background: #ff8c00; border: 1px solid #2e2e2e;border-top-right-radius: 0px;border-bottom-right-radius: 0px;border-top-left-radius: 5px;border-bottom-left-radius: 5px;}"
"QSlider::add-page {background: #444444; border: 1px solid #2e2e2e;border-top-right-radius: 5px;border-bottom-right-radius: 5px;border-top-left-radius: 0px;border-bottom-left-radius: 0px;}"
"QDialog { border: 1px solid #ff8c00;}"
);
#endif
MainParser parser;
parser.parse(*qApp);
@ -160,10 +180,18 @@ int main(int argc, char* argv[])
QSurfaceFormat::setDefaultFormat(sfc);
#endif
qtwebapp::LoggerWithFile *logger = new qtwebapp::LoggerWithFile(qApp);
#ifdef ANDROID
qtwebapp::LoggerWithFile *logger = nullptr;
qInstallMessageHandler(Android::messageHandler);
#else
qtwebapp::LoggerWithFile *logger = new qtwebapp::LoggerWithFile(qApp);
logger->installMsgHandler();
#endif
int res = runQtApplication(argc, argv, logger);
delete logger;
qWarning("SDRangel quit.");
return res;
}

View File

@ -315,10 +315,11 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
d.readBlob(60, &m_geometryBytes);
d.readBool(61, &m_hidden, false);
d.readString(62, &m_checkWXAPIKey, "");
#ifdef LINUX
d.readString(63, &m_mapProvider, "mapboxgl");
#else
d.readString(63, &m_mapProvider, "osm");
#ifdef LINUX
if (m_mapProvider == "osm") {
m_mapProvider = "mapboxgl";
}
#endif
for (int i = 0; i < ADSBDEMOD_COLUMNS; i++) {

View File

@ -183,10 +183,11 @@ bool MapSettings::deserialize(const QByteArray& data)
QByteArray blob;
d.readBool(1, &m_displayNames, true);
#ifdef LINUX
d.readString(2, &m_mapProvider, "mapboxgl");
#else
d.readString(2, &m_mapProvider, "osm");
#ifdef LINUX
if (m_mapProvider == "osm") {
m_mapProvider = "mapboxgl";
}
#endif
d.readString(3, &m_mapBoxAPIKey, "");
d.readString(4, &m_mapBoxStyles, "");

View File

@ -1,6 +1,7 @@
project (sdrbase)
if(WIN32)
# This should probably be in ../CMakeLists.txt with the other variables like it
set(OPUS_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/external/windows/libopus/include")
set(OPUS_LIBRARIES "${CMAKE_SOURCE_DIR}/external/windows/libopus/lib/x64/libopus.lib")
endif()
@ -45,10 +46,11 @@ if (LIBSIGMF_FOUND AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(sdrbase_LIBSIGMF_LIB ${LIBSIGMF_LIBRARIES})
endif()
# serialdv now required
add_definitions(-DDSD_USE_SERIALDV)
include_directories(${LIBSERIALDV_INCLUDE_DIR})
set(sdrbase_SERIALDV_LIB ${LIBSERIALDV_LIBRARY})
if (LIBSERIALDV_FOUND)
add_definitions(-DDSD_USE_SERIALDV)
include_directories(${LIBSERIALDV_INCLUDE_DIR})
set(sdrbase_SERIALDV_LIB ${LIBSERIALDV_LIBRARY})
endif()
set(sdrbase_SOURCES
${sdrbase_SOURCES}
@ -168,6 +170,7 @@ set(sdrbase_SOURCES
settings/rollupstate.cpp
util/ais.cpp
util/android.cpp
util/aviationweather.cpp
util/ax25.cpp
util/aprs.cpp
@ -388,6 +391,7 @@ set(sdrbase_HEADERS
settings/rollupstate.h
util/ais.h
util/android.h
util/aviationweather.h
util/ax25.h
util/aprs.h
@ -491,6 +495,7 @@ target_link_libraries(sdrbase
Qt::Core
Qt::Multimedia
Qt::WebSockets
Qt::Positioning
httpserver
logging
qrtplib
@ -499,8 +504,15 @@ target_link_libraries(sdrbase
if (Qt6_FOUND)
target_link_libraries(sdrbase
Qt::Core5Compat
Qt::CorePrivate
)
endif()
if(ANDROID AND NOT ENABLE_QT6)
target_link_libraries(sdrbase Qt::AndroidExtras)
endif()
if(ANDROID)
target_link_libraries(sdrbase log)
endif()
install(TARGETS sdrbase DESTINATION ${INSTALL_LIB_DIR})

View File

@ -17,8 +17,6 @@
#include <QGlobalStatic>
#include "plugin/pluginmanager.h"
#include "deviceenumerator.h"
Q_GLOBAL_STATIC(DeviceEnumerator, deviceEnumerator)
@ -246,89 +244,101 @@ bool DeviceEnumerator::isMIMOEnumerated(const QString& deviceHwId, int deviceSeq
return false;
}
void DeviceEnumerator::enumerateRxDevices(PluginManager *pluginManager)
void DeviceEnumerator::enumerateDevices(PluginAPI::SamplingDeviceRegistrations& deviceRegistrations, DevicesEnumeration& enumeration, PluginInterface::SamplingDevice::StreamType type)
{
m_rxEnumeration.clear();
PluginAPI::SamplingDeviceRegistrations& rxDeviceRegistrations = pluginManager->getSourceDeviceRegistrations();
int index = 0;
DevicesEnumeration tempEnumeration;
PluginInterface::OriginDevices originDevices;
QStringList originDevicesHwIds;
for (int i = 0; i < rxDeviceRegistrations.count(); i++)
for (int i = 0; i < deviceRegistrations.count(); i++)
{
qDebug("DeviceEnumerator::enumerateRxDevices: %s", qPrintable(rxDeviceRegistrations[i].m_deviceId));
rxDeviceRegistrations[i].m_plugin->enumOriginDevices(m_originDevicesHwIds, m_originDevices);
PluginInterface::SamplingDevices samplingDevices = rxDeviceRegistrations[i].m_plugin->enumSampleSources(m_originDevices);
qDebug("DeviceEnumerator::enumerateDevices: %s", qPrintable(deviceRegistrations[i].m_deviceId));
deviceRegistrations[i].m_plugin->enumOriginDevices(originDevicesHwIds, originDevices);
PluginInterface::SamplingDevices samplingDevices;
if (type == PluginInterface::SamplingDevice::StreamSingleRx) {
samplingDevices = deviceRegistrations[i].m_plugin->enumSampleSources(originDevices);
} else if (type == PluginInterface::SamplingDevice::StreamSingleTx) {
samplingDevices = deviceRegistrations[i].m_plugin->enumSampleSinks(originDevices);
} else {
samplingDevices = deviceRegistrations[i].m_plugin->enumSampleMIMO(originDevices);
}
for (int j = 0; j < samplingDevices.count(); j++)
{
m_rxEnumeration.push_back(
tempEnumeration.push_back(
DeviceEnumeration(
samplingDevices[j],
rxDeviceRegistrations[i].m_plugin,
index
deviceRegistrations[i].m_plugin,
0 // Index will be set when adding to enumeration below
)
);
index++;
}
}
// We don't remove devices that are no-longer found from the enumeration list, in case their
// index is referenced in some other object (E.g. DeviceGUI)
// Instead we mark as removed. If later re-added, then we re-use the same index
for (DevicesEnumeration::iterator it = enumeration.begin(); it != enumeration.end(); ++it)
{
bool found = false;
for (DevicesEnumeration::iterator it2 = tempEnumeration.begin(); it2 != tempEnumeration.end(); ++it2)
{
if (it->m_samplingDevice == it2->m_samplingDevice)
{
found = true;
break;
}
}
if (!found) {
it->m_samplingDevice.removed = true;
}
}
// Add new entries and update existing (in case re-added or sequence number has changed)
int index = enumeration.size();
for (DevicesEnumeration::iterator it = tempEnumeration.begin(); it != tempEnumeration.end(); ++it)
{
bool found = false;
for (DevicesEnumeration::iterator it2 = enumeration.begin(); it2 != enumeration.end(); ++it2)
{
if (it->m_samplingDevice == it2->m_samplingDevice)
{
it2->m_samplingDevice.removed = false;
it2->m_samplingDevice.displayedName = it->m_samplingDevice.displayedName;
found = true;
break;
}
}
if (!found)
{
it->m_index = index++;
enumeration.push_back(*it);
}
}
}
void DeviceEnumerator::enumerateRxDevices(PluginManager *pluginManager)
{
enumerateDevices(pluginManager->getSourceDeviceRegistrations(), m_rxEnumeration, PluginInterface::SamplingDevice::StreamSingleRx);
}
void DeviceEnumerator::enumerateTxDevices(PluginManager *pluginManager)
{
m_txEnumeration.clear();
PluginAPI::SamplingDeviceRegistrations& txDeviceRegistrations = pluginManager->getSinkDeviceRegistrations();
int index = 0;
for (int i = 0; i < txDeviceRegistrations.count(); i++)
{
qDebug("DeviceEnumerator::enumerateTxDevices: %s", qPrintable(txDeviceRegistrations[i].m_deviceId));
txDeviceRegistrations[i].m_plugin->enumOriginDevices(m_originDevicesHwIds, m_originDevices);
PluginInterface::SamplingDevices samplingDevices = txDeviceRegistrations[i].m_plugin->enumSampleSinks(m_originDevices);
for (int j = 0; j < samplingDevices.count(); j++)
{
m_txEnumeration.push_back(
DeviceEnumeration(
samplingDevices[j],
txDeviceRegistrations[i].m_plugin,
index
)
);
index++;
}
}
enumerateDevices(pluginManager->getSinkDeviceRegistrations(), m_txEnumeration, PluginInterface::SamplingDevice::StreamSingleTx);
}
void DeviceEnumerator::enumerateMIMODevices(PluginManager *pluginManager)
{
m_mimoEnumeration.clear();
PluginAPI::SamplingDeviceRegistrations& mimoDeviceRegistrations = pluginManager->getMIMODeviceRegistrations();
int index = 0;
for (int i = 0; i < mimoDeviceRegistrations.count(); i++)
{
qDebug("DeviceEnumerator::enumerateMIMODevices: %s", qPrintable(mimoDeviceRegistrations[i].m_deviceId));
mimoDeviceRegistrations[i].m_plugin->enumOriginDevices(m_originDevicesHwIds, m_originDevices);
PluginInterface::SamplingDevices samplingDevices = mimoDeviceRegistrations[i].m_plugin->enumSampleMIMO(m_originDevices);
for (int j = 0; j < samplingDevices.count(); j++)
{
m_mimoEnumeration.push_back(
DeviceEnumeration(
samplingDevices[j],
mimoDeviceRegistrations[i].m_plugin,
index
)
);
index++;
}
}
enumerateDevices(pluginManager->getMIMODeviceRegistrations(), m_mimoEnumeration, PluginInterface::SamplingDevice::StreamMIMO);
}
void DeviceEnumerator::listRxDeviceNames(QList<QString>& list, std::vector<int>& indexes) const
{
for (DevicesEnumeration::const_iterator it = m_rxEnumeration.begin(); it != m_rxEnumeration.end(); ++it)
{
if ((it->m_samplingDevice.claimed < 0) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice))
if (((it->m_samplingDevice.claimed < 0) && (!it->m_samplingDevice.removed)) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice))
{
list.append(it->m_samplingDevice.displayedName);
indexes.push_back(it->m_index);
@ -340,7 +350,7 @@ void DeviceEnumerator::listTxDeviceNames(QList<QString>& list, std::vector<int>&
{
for (DevicesEnumeration::const_iterator it = m_txEnumeration.begin(); it != m_txEnumeration.end(); ++it)
{
if ((it->m_samplingDevice.claimed < 0) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice))
if (((it->m_samplingDevice.claimed < 0) && (!it->m_samplingDevice.removed)) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice))
{
list.append(it->m_samplingDevice.displayedName);
indexes.push_back(it->m_index);
@ -352,7 +362,7 @@ void DeviceEnumerator::listMIMODeviceNames(QList<QString>& list, std::vector<int
{
for (DevicesEnumeration::const_iterator it = m_mimoEnumeration.begin(); it != m_mimoEnumeration.end(); ++it)
{
if ((it->m_samplingDevice.claimed < 0) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice))
if (((it->m_samplingDevice.claimed < 0) && (!it->m_samplingDevice.removed)) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice))
{
list.append(it->m_samplingDevice.displayedName);
indexes.push_back(it->m_index);

View File

@ -21,6 +21,7 @@
#include <vector>
#include "plugin/plugininterface.h"
#include "plugin/pluginmanager.h"
#include "device/deviceuserargs.h"
#include "export.h"
@ -85,9 +86,8 @@ private:
DevicesEnumeration m_rxEnumeration;
DevicesEnumeration m_txEnumeration;
DevicesEnumeration m_mimoEnumeration;
PluginInterface::OriginDevices m_originDevices;
QStringList m_originDevicesHwIds;
void enumerateDevices(PluginAPI::SamplingDeviceRegistrations& deviceRegistrations, DevicesEnumeration& enumeration, PluginInterface::SamplingDevice::StreamType type);
PluginInterface *getRxRegisteredPlugin(PluginManager *pluginManager, const QString& deviceHwId);
PluginInterface *getTxRegisteredPlugin(PluginManager *pluginManager, const QString& deviceHwId);
PluginInterface *getMIMORegisteredPlugin(PluginManager *pluginManager, const QString& deviceHwId);

View File

@ -18,6 +18,8 @@
#include <QGlobalStatic>
#include <QCoreApplication>
#include <QString>
#include <QDebug>
#include <QGeoPositionInfoSource>
#include "loggerwithfile.h"
#include "dsp/dsptypes.h"
@ -71,6 +73,8 @@ MainCore::MainCore()
m_masterTimer.start(50);
m_startMsecsSinceEpoch = QDateTime::currentMSecsSinceEpoch();
m_masterElapsedTimer.start();
// Position can take a while to determine, so we start updates at program startup
initPosition();
}
MainCore::~MainCore()
@ -335,3 +339,36 @@ void MainCore::debugMaps()
feIt.value()->getIndex(), feIt.key()->getIndexInFeatureSet(), qPrintable(feIt.key()->getURI()), qPrintable(feIt.key()->getName()));
}
}
void MainCore::initPosition()
{
m_positionSource = QGeoPositionInfoSource::createDefaultSource(this);
if (m_positionSource)
{
connect(m_positionSource, &QGeoPositionInfoSource::positionUpdated, this, &MainCore::positionUpdated);
m_positionSource->startUpdates();
}
else
{
qDebug() << "MainCore::initPosition: No position source.";
}
}
const QGeoPositionInfo& MainCore::getPosition() const
{
return m_position;
}
void MainCore::positionUpdated(const QGeoPositionInfo &info)
{
if (info.isValid())
{
m_position = info;
if (m_settings.getAutoUpdatePosition())
{
m_settings.setLatitude(m_position.coordinate().latitude());
m_settings.setLongitude(m_position.coordinate().longitude());
m_settings.setAltitude(m_position.coordinate().altitude());
}
}
}

View File

@ -25,6 +25,7 @@
#include <QElapsedTimer>
#include <QDateTime>
#include <QObject>
#include <QGeoPositionInfo>
#include "export.h"
#include "settings/mainsettings.h"
@ -38,6 +39,7 @@ class FeatureSet;
class Feature;
class PluginManager;
class MessageQueue;
class QGeoPositionInfoSource;
namespace qtwebapp {
class LoggerWithFile;
@ -846,7 +848,7 @@ public:
qint64 getStartMsecsSinceEpoch() const { return m_startMsecsSinceEpoch; } //!< Epoch timestamp in millisecodns close to elapsed timer start
const MainSettings& getSettings() const { return m_settings; }
MessageQueue *getMainMessageQueue() { return m_mainMessageQueue; }
const PluginManager *getPluginManager() const { return m_pluginManager; }
PluginManager *getPluginManager() const { return m_pluginManager; }
std::vector<DeviceSet*>& getDeviceSets() { return m_deviceSets; }
std::vector<FeatureSet*>& getFeatureeSets() { return m_featureSets; }
void setLoggingOptions();
@ -875,6 +877,8 @@ public:
// pipes
MessagePipes& getMessagePipes() { return m_messagePipes; }
DataPipes& getDataPipes() { return m_dataPipes; }
// Position
const QGeoPositionInfo& getPosition() const;
friend class MainServer;
friend class MainWindow;
@ -883,6 +887,9 @@ public:
friend class DeviceSetPresetsDialog;
friend class ConfigurationsDialog;
public slots:
void positionUpdated(const QGeoPositionInfo &info);
signals:
void deviceSetAdded(int index, DeviceAPI *device);
void deviceChanged(int index);
@ -912,7 +919,10 @@ private:
PluginManager* m_pluginManager;
MessagePipes m_messagePipes;
DataPipes m_dataPipes;
QGeoPositionInfoSource *m_positionSource;
QGeoPositionInfo m_position;
void initPosition();
void debugMaps();
};

View File

@ -66,6 +66,7 @@ public:
int deviceNbItems; //!< Number of items (or streams) in the device. >1 for composite devices.
int deviceItemIndex; //!< For composite devices this is the Rx or Tx stream index. -1 if not initialized
int claimed; //!< This is the device set index if claimed else -1
bool removed; //!< Set if device has been removed
SamplingDevice(const QString& _displayedName,
const QString& _hardwareId,
@ -85,8 +86,17 @@ public:
streamType(_streamType),
deviceNbItems(_deviceNbItems),
deviceItemIndex(_deviceItemIndex),
claimed(-1)
claimed(-1),
removed(false)
{ }
bool operator==(const SamplingDevice& rhs) const
{
return displayedName == rhs.displayedName
&& hardwareId == rhs.hardwareId
&& id == rhs.id
&& serial == rhs.serial;
}
};
typedef QList<SamplingDevice> SamplingDevices;

View File

@ -151,6 +151,12 @@ public:
emit preferenceChanged(Preferences::Altitude);
}
bool getAutoUpdatePosition() const { return m_preferences.getAutoUpdatePosition(); }
void setAutoUpdatePosition(bool autoUpdatePosition)
{
m_preferences.setAutoUpdatePosition(autoUpdatePosition);
}
QtMsgType getConsoleMinLogLevel() const { return m_preferences.getConsoleMinLogLevel(); }
void setConsoleMinLogLevel(const QtMsgType& minLogLevel)
{

View File

@ -17,6 +17,7 @@ void Preferences::resetToDefaults()
m_latitude = 49.012423; // Set an interesting location so map doesn't open up in the middle of the ocean
m_longitude = 8.418125;
m_altitude = 0.0f;
m_autoUpdatePosition = true;
m_useLogFile = false;
m_logFileName = "sdrangel.log";
m_consoleMinLogLevel = QtDebugMsg;
@ -41,6 +42,7 @@ QByteArray Preferences::serialize() const
s.writeFloat((int) Altitude, m_altitude);
s.writeS32((int) SourceItemIndex, m_sourceItemIndex);
s.writeS32((int) Multisampling, m_multisampling);
s.writeBool((int) AutoUpdatePosition, m_autoUpdatePosition);
return s.final();
}
@ -95,9 +97,11 @@ bool Preferences::deserialize(const QByteArray& data)
}
d.readS32((int) Multisampling, &m_multisampling, 0);
d.readBool((int) AutoUpdatePosition, &m_autoUpdatePosition, true);
return true;
} else
}
else
{
resetToDefaults();
return false;

View File

@ -22,7 +22,8 @@ public:
StationName,
Altitude,
SourceItemIndex,
Multisampling
Multisampling,
AutoUpdatePosition
};
Preferences();
@ -60,6 +61,9 @@ public:
float getAltitude() const { return m_altitude; }
void setAltitude(float altitude) { m_altitude = altitude; }
bool getAutoUpdatePosition() const { return m_autoUpdatePosition; }
void setAutoUpdatePosition(bool autoUpdatePosition) { m_autoUpdatePosition = autoUpdatePosition; }
QtMsgType getConsoleMinLogLevel() const { return m_consoleMinLogLevel; }
void setConsoleMinLogLevel(const QtMsgType& minLogLevel) { m_consoleMinLogLevel = minLogLevel; }
@ -87,6 +91,7 @@ protected:
float m_latitude; //!< Position of the station
float m_longitude;
float m_altitude; //!< Altitude in metres
bool m_autoUpdatePosition; //!< Automatically update position from GPS
QtMsgType m_consoleMinLogLevel;
QtMsgType m_fileMinLogLevel;

213
sdrbase/util/android.cpp Normal file
View File

@ -0,0 +1,213 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifdef ANDROID
#include <QDebug>
#include <android/log.h>
#include "android.h"
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
#include <QtCore/private/qandroidextras_p.h>
#include <QJniObject>
#include <QJniEnvironment>
void Android::sendIntent()
{
QJniObject url = QJniObject::fromString("iqsrc://-f 1090000000 -p 1234 -s 2000000 -a 127.0.0.1 -g 100");
QJniObject intent = QJniObject::callStaticObjectMethod("android/content/Intent", "parseUri", "(Ljava/lang/String;I)Landroid/content/Intent;", url.object<jstring>(), 0x00000001); // Creates Intent(ACTION_VIEW, url)
QtAndroidPrivate::startActivity(intent, 0, [](int requestCode, int resultCode, const QJniObject &data) {
(void) data;
qDebug() << "MainCore::sendIntent " << requestCode << resultCode;
});
}
QStringList Android::listUSBDeviceSerials(int vid, int pid)
{
QStringList serials;
QJniEnvironment jniEnv;
QJniObject activity = QJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid())
{
QJniObject serialsObj = activity.callObjectMethod("listUSBDeviceSerials", "(II)[Ljava/lang/String;", vid, pid);
int serialsLen = jniEnv->GetArrayLength(serialsObj.object<jarray>());
for (int i = 0; i < serialsLen; i++)
{
QJniObject arrayElement = jniEnv->GetObjectArrayElement(serialsObj.object<jobjectArray>(), i);
QString serial = arrayElement.toString();
serials.append(serial);
}
}
return serials;
}
int Android::openUSBDevice(const QString &serial)
{
int fd = -1;
QJniObject activity = QJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid())
{
QJniObject serialsObj = QJniObject::fromString(serial);
fd = activity.callMethod<jint>("openUSBDevice", "(Ljava/lang/String;)I", serialsObj.object<jstring>());
}
return fd;
}
void Android::closeUSBDevice(int fd)
{
if (fd >= 0)
{
QJniObject activity = QJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid()) {
activity.callMethod<void>("closeUSBDevice", "(I)V", fd);
} else {
qCritical() << "MainCore::closeUSBDevice: activity is not valid.";
}
}
}
void Android::moveTaskToBack()
{
QJniObject activity = QJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid()) {
activity.callMethod<jboolean>("moveTaskToBack", "(Z)Z", true);
}
}
#else // QT_VERSION
#include <QtAndroid>
#include <QAndroidIntent>
#include <QAndroidJniObject>
#include <QAndroidJniEnvironment>
#include <QAndroidActivityResultReceiver>
void Android::sendIntent()
{
QAndroidJniObject url = QAndroidJniObject::fromString("iqsrc://-f 1090000000 -p 1234 -s 2000000 -a 127.0.0.1 -g 100");
QAndroidJniObject intent = QAndroidJniObject::callStaticObjectMethod("android/content/Intent", "parseUri", "(Ljava/lang/String;I)Landroid/content/Intent;", url.object<jstring>(), 0x00000001); // Creates Intent(ACTION_VIEW, url)
QtAndroid::startActivity(intent, 0, [](int requestCode, int resultCode, const QAndroidJniObject &data) {
(void) data;
qDebug() << "MainCore::sendIntent " << requestCode << resultCode;
});
}
QStringList Android::listUSBDeviceSerials(int vid, int pid)
{
QStringList serials;
QAndroidJniEnvironment jniEnv;
QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid())
{
QAndroidJniObject serialsObj = activity.callObjectMethod("listUSBDeviceSerials", "(II)[Ljava/lang/String;", vid, pid);
int serialsLen = jniEnv->GetArrayLength(serialsObj.object<jarray>());
for (int i = 0; i < serialsLen; i++)
{
QAndroidJniObject arrayElement = jniEnv->GetObjectArrayElement(serialsObj.object<jobjectArray>(), i);
QString serial = arrayElement.toString();
serials.append(serial);
}
}
return serials;
}
int Android::openUSBDevice(const QString &serial)
{
int fd = -1;
QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid())
{
QAndroidJniObject serialsObj = QAndroidJniObject::fromString(serial);
fd = activity.callMethod<jint>("openUSBDevice", "(Ljava/lang/String;)I", serialsObj.object<jstring>());
}
return fd;
}
void Android::closeUSBDevice(int fd)
{
if (fd >= 0)
{
QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid()) {
activity.callMethod<void>("closeUSBDevice", "(I)V", fd);
} else {
qCritical() << "MainCore::closeUSBDevice: activity is not valid.";
}
}
}
void Android::moveTaskToBack()
{
QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
if (activity.isValid()) {
activity.callMethod<jboolean>("moveTaskToBack", "(Z)Z", true);
}
}
#endif // QT6
// Redirect qDebug/qWarning to Android log, so we can view remotely with adb
void Android::messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
{
QString report = msg;
if (context.file && !QString(context.file).isEmpty())
{
report += " in file ";
report += QString(context.file);
report += " line ";
report += QString::number(context.line);
}
if (context.function && !QString(context.function).isEmpty())
{
report += +" function ";
report += QString(context.function);
}
const char * const local = report.toLocal8Bit().constData();
const char * const applicationName = "sdrangel";
switch (type)
{
case QtDebugMsg:
__android_log_write(ANDROID_LOG_DEBUG, applicationName, local);
break;
case QtInfoMsg:
__android_log_write(ANDROID_LOG_INFO, applicationName, local);
break;
case QtWarningMsg:
__android_log_write(ANDROID_LOG_WARN, applicationName, local);
break;
case QtCriticalMsg:
__android_log_write(ANDROID_LOG_ERROR, applicationName, local);
break;
case QtFatalMsg:
default:
__android_log_write(ANDROID_LOG_FATAL, applicationName, local);
abort();
}
}
#endif // ANDROID

44
sdrbase/util/android.h Normal file
View File

@ -0,0 +1,44 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRBASE_ANDROID_H_
#define SDRBASE_ANDROID_H_
#ifdef ANDROID
#include <QtGlobal>
#include <QString>
#include "export.h"
// Android specific functions
class SDRBASE_API Android
{
public:
static void sendIntent();
static QStringList listUSBDeviceSerials(int vid, int pid);
static int openUSBDevice(const QString &serial);
static void closeUSBDevice(int fd);
static void moveTaskToBack();
static void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg);
};
#endif // ANDROID
#endif // SDRBASE_ANDROID_H_

View File

@ -27,11 +27,26 @@
#include "settings/configuration.h"
ConfigurationsDialog::ConfigurationsDialog(QWidget* parent) :
ConfigurationsDialog::ConfigurationsDialog(bool openOnly, QWidget* parent) :
QDialog(parent),
ui(new Ui::ConfigurationsDialog)
{
ui->setupUi(this);
if (openOnly)
{
ui->buttonBox->setStandardButtons(QDialogButtonBox::Close | QDialogButtonBox::Open);
ui->configurationDelete->setVisible(false);
ui->configurationEdit->setVisible(false);
ui->configurationExport->setVisible(false);
ui->configurationImport->setVisible(false);
ui->configurationLoad->setVisible(false);
ui->configurationSave->setVisible(false);
ui->configurationUpdate->setVisible(false);
}
else
{
ui->description->setVisible(false);
}
}
ConfigurationsDialog::~ConfigurationsDialog()
@ -354,9 +369,7 @@ void ConfigurationsDialog::on_configurationExport_clicked()
tr("Open preset export file"),
".",
tr("Configuration export files (*.cfgx)"),
0,
QFileDialog::DontUseNativeDialog
);
0);
if (fileName != "")
{
@ -404,8 +417,7 @@ void ConfigurationsDialog::on_configurationImport_clicked()
tr("Open preset export file"),
".",
tr("Preset export files (*.cfgx)"),
0,
QFileDialog::DontUseNativeDialog
0
);
if (fileName != "")
@ -497,3 +509,9 @@ void ConfigurationsDialog::deleteConfigurationGroup(const QString& groupName)
}
}
}
void ConfigurationsDialog::accept()
{
on_configurationLoad_clicked();
QDialog::accept();
}

View File

@ -38,7 +38,7 @@ namespace Ui {
class SDRGUI_API ConfigurationsDialog : public QDialog {
Q_OBJECT
public:
explicit ConfigurationsDialog(QWidget* parent = nullptr);
explicit ConfigurationsDialog(bool openOnly, QWidget* parent = nullptr);
~ConfigurationsDialog();
void setConfigurations(QList<Configuration*>* configurations) { m_configurations = configurations; }
void populateTree();
@ -71,6 +71,7 @@ private slots:
void on_configurationLoad_clicked();
void on_configurationTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void on_configurationTree_itemActivated(QTreeWidgetItem *item, int column);
void accept() override;
signals:
void saveConfiguration(Configuration*);

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>392</width>
<height>414</height>
<width>410</width>
<height>424</height>
</rect>
</property>
<property name="font">
@ -19,218 +19,207 @@
<property name="windowTitle">
<string>Configurations</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>40</x>
<y>380</y>
<width>341</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
<widget class="QWidget" name="widget" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>10</y>
<width>392</width>
<height>310</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeWidget" name="configurationsTree">
<property name="toolTip">
<string>List of configurations</string>
</property>
<property name="indentation">
<number>5</number>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
<property name="columnCount">
<number>1</number>
</property>
<attribute name="headerMinimumSectionSize">
<number>5</number>
</attribute>
<column>
<property name="text">
<string>Description</string>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="description">
<property name="text">
<string>Select configuration to load:</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="configurationsTree">
<property name="toolTip">
<string>List of configurations</string>
</property>
<property name="indentation">
<number>5</number>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
<property name="columnCount">
<number>1</number>
</property>
<attribute name="headerMinimumSectionSize">
<number>5</number>
</attribute>
<column>
<property name="text">
<string>Description</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="configurationsControl">
<item>
<widget class="QToolButton" name="configurationSave">
<property name="toolTip">
<string>Save current workspaces configuration as new configuration</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>330</y>
<width>392</width>
<height>34</height>
</rect>
</property>
<layout class="QHBoxLayout" name="configurationsControl">
<item>
<widget class="QToolButton" name="configurationSave">
<property name="toolTip">
<string>Save current workspaces configuration as new configuration</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/create.png</normaloff>:/create.png</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="configurationUpdate">
<property name="toolTip">
<string>Update selected configuration with current workspaces configuration</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/recycle.png</normaloff>:/recycle.png</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="configurationEdit">
<property name="toolTip">
<string>Edit configuration details</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/edit.png</normaloff>:/edit.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="configurationExport">
<property name="toolTip">
<string>Export current configuration to file</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/export.png</normaloff>:/export.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="configurationImport">
<property name="toolTip">
<string>Import configuration from file into current group</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/import.png</normaloff>:/import.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="configurationDelete">
<property name="toolTip">
<string>Delete selected configuration</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/bin.png</normaloff>:/bin.png</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="configurationLoad">
<property name="toolTip">
<string>Load selected configuration</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/load.png</normaloff>:/load.png</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/create.png</normaloff>:/create.png</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="configurationUpdate">
<property name="toolTip">
<string>Update selected configuration with current workspaces configuration</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/recycle.png</normaloff>:/recycle.png</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="configurationEdit">
<property name="toolTip">
<string>Edit configuration details</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/edit.png</normaloff>:/edit.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="configurationExport">
<property name="toolTip">
<string>Export current configuration to file</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/export.png</normaloff>:/export.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="configurationImport">
<property name="toolTip">
<string>Import configuration from file into current group</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/import.png</normaloff>:/import.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="configurationDelete">
<property name="toolTip">
<string>Delete selected configuration</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/bin.png</normaloff>:/bin.png</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="configurationLoad">
<property name="toolTip">
<string>Load selected configuration</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/load.png</normaloff>:/load.png</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../resources/res.qrc"/>

View File

@ -292,8 +292,7 @@ void DeviceSetPresetsDialog::on_presetExport_clicked()
tr("Open preset export file"),
".",
tr("Preset export files (*.prex)"),
0,
QFileDialog::DontUseNativeDialog
0
);
if (fileName != "")
@ -342,8 +341,7 @@ void DeviceSetPresetsDialog::on_presetImport_clicked()
tr("Open preset export file"),
".",
tr("Preset export files (*.prex)"),
0,
QFileDialog::DontUseNativeDialog
0
);
if (fileName != "")

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>324</width>
<height>229</height>
<height>201</height>
</rect>
</property>
<property name="font">
@ -19,44 +19,24 @@
<property name="windowTitle">
<string>My Position</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>My Station Position</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="2" column="0">
<widget class="QLabel" name="latitudeLabel">
<property name="text">
<string>Latitude</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="longitudeLabel">
<property name="text">
<string>Longitude</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="longitudeSpinBox">
<property name="toolTip">
<string>Longitude in decimal degrees</string>
</property>
<property name="decimals">
<number>6</number>
</property>
<property name="minimum">
<double>-180.000000000000000</double>
</property>
<property name="maximum">
<double>180.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="latitudeSpinBox">
<property name="toolTip">
<string>Latitude in decimal degrees</string>
@ -72,24 +52,14 @@
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="altitudeSpinBox">
<property name="toolTip">
<string>Altitude in metres</string>
</property>
<property name="maximum">
<number>50000</number>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="altitudeLabel">
<item row="1" column="0">
<widget class="QLabel" name="latitudeLabel">
<property name="text">
<string>Altitude (m)</string>
<string>Latitude</string>
</property>
</widget>
</item>
<item row="0" column="1">
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="name">
<property name="toolTip">
<string>Enter the name of your station</string>
@ -99,6 +69,20 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="altitudeLabel">
<property name="text">
<string>Altitude (m)</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="longitudeLabel">
<property name="text">
<string>Longitude</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="nameLabel">
<property name="text">
@ -106,19 +90,66 @@
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="autoUpdatePosition">
<property name="toolTip">
<string>Automatically update position using GPS (when available)</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="longitudeSpinBox">
<property name="toolTip">
<string>Longitude in decimal degrees</string>
</property>
<property name="decimals">
<number>6</number>
</property>
<property name="minimum">
<double>-180.000000000000000</double>
</property>
<property name="maximum">
<double>180.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="autoUpdatePositionLabel">
<property name="text">
<string>Auto-update from GPS</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="gps">
<property name="toolTip">
<string>Set position using GPS (if available)</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/gps.png</normaloff>:/gps.png</iconset>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="altitudeSpinBox">
<property name="toolTip">
<string>Altitude in metres</string>
</property>
<property name="maximum">
<number>50000</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
@ -127,7 +158,9 @@
<tabstop>longitudeSpinBox</tabstop>
<tabstop>altitudeSpinBox</tabstop>
</tabstops>
<resources/>
<resources>
<include location="../resources/res.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>

View File

@ -2,9 +2,6 @@
// Copyright (C) 2016 F4EXB //
// written by Edouard Griffiths //
// //
// OpenGL interface modernization. //
// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
@ -21,7 +18,9 @@
#include "gui/mypositiondialog.h"
#include "ui_myposdialog.h"
#include "maincore.h"
#include <QGeoCoordinate>
MyPositionDialog::MyPositionDialog(MainSettings& mainSettings, QWidget* parent) :
QDialog(parent),
@ -33,6 +32,7 @@ MyPositionDialog::MyPositionDialog(MainSettings& mainSettings, QWidget* parent)
ui->latitudeSpinBox->setValue(m_mainSettings.getLatitude());
ui->longitudeSpinBox->setValue(m_mainSettings.getLongitude());
ui->altitudeSpinBox->setValue(m_mainSettings.getAltitude());
ui->autoUpdatePosition->setChecked(m_mainSettings.getAutoUpdatePosition());
}
MyPositionDialog::~MyPositionDialog()
@ -46,5 +46,22 @@ void MyPositionDialog::accept()
m_mainSettings.setLatitude(ui->latitudeSpinBox->value());
m_mainSettings.setLongitude(ui->longitudeSpinBox->value());
m_mainSettings.setAltitude(ui->altitudeSpinBox->value());
m_mainSettings.setAutoUpdatePosition(ui->autoUpdatePosition->isChecked());
QDialog::accept();
}
void MyPositionDialog::on_gps_clicked()
{
const QGeoPositionInfo& position = MainCore::instance()->getPosition();
if (position.isValid())
{
QGeoCoordinate coord = position.coordinate();
ui->latitudeSpinBox->setValue(coord.latitude());
ui->longitudeSpinBox->setValue(coord.longitude());
ui->altitudeSpinBox->setValue(coord.altitude());
}
else
{
qDebug() << "MyPositionDialog::on_gps_clicked: Position is not valid.";
}
}

View File

@ -2,9 +2,6 @@
// Copyright (C) 2016 F4EXB //
// written by Edouard Griffiths //
// //
// OpenGL interface modernization. //
// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
@ -43,6 +40,7 @@ private:
private slots:
void accept();
void on_gps_clicked();
};
#endif /* SDRBASE_GUI_MYPOSITIONDIALOG_H_ */

View File

@ -22,7 +22,7 @@
#include "samplingdevicedialog.h"
#include "ui_samplingdevicedialog.h"
#include "device/deviceenumerator.h"
#include "maincore.h"
SamplingDeviceDialog::SamplingDeviceDialog(int deviceType, QWidget* parent) :
QDialog(parent),
@ -32,19 +32,7 @@ SamplingDeviceDialog::SamplingDeviceDialog(int deviceType, QWidget* parent) :
m_hasChanged(false)
{
ui->setupUi(this);
QList<QString> deviceDisplayNames;
if (m_deviceType == 0) { // Single Rx
DeviceEnumerator::instance()->listRxDeviceNames(deviceDisplayNames, m_deviceIndexes);
} else if (m_deviceType == 1) { // Single Tx
DeviceEnumerator::instance()->listTxDeviceNames(deviceDisplayNames, m_deviceIndexes);
} else if (m_deviceType == 2) { // MIMO
DeviceEnumerator::instance()->listMIMODeviceNames(deviceDisplayNames, m_deviceIndexes);
}
QStringList devicesNamesList(deviceDisplayNames);
ui->deviceSelect->addItems(devicesNamesList);
on_refreshDevices_clicked();
}
SamplingDeviceDialog::~SamplingDeviceDialog()
@ -58,6 +46,23 @@ int SamplingDeviceDialog::exec()
return QDialog::exec();
}
void SamplingDeviceDialog::displayDevices()
{
QList<QString> deviceDisplayNames;
m_deviceIndexes.clear();
if (m_deviceType == 0) { // Single Rx
DeviceEnumerator::instance()->listRxDeviceNames(deviceDisplayNames, m_deviceIndexes);
} else if (m_deviceType == 1) { // Single Tx
DeviceEnumerator::instance()->listTxDeviceNames(deviceDisplayNames, m_deviceIndexes);
} else if (m_deviceType == 2) { // MIMO
DeviceEnumerator::instance()->listMIMODeviceNames(deviceDisplayNames, m_deviceIndexes);
}
ui->deviceSelect->clear();
ui->deviceSelect->addItems(deviceDisplayNames);
}
void SamplingDeviceDialog::setSelectedDeviceIndex(int deviceIndex)
{
ui->deviceSelect->blockSignals(true);
@ -77,6 +82,21 @@ void SamplingDeviceDialog::on_deviceSelect_currentIndexChanged(int index)
m_hasChanged = true;
}
void SamplingDeviceDialog::on_refreshDevices_clicked()
{
PluginManager *pluginManager = MainCore::instance()->getPluginManager();
if (m_deviceType == 0) {
DeviceEnumerator::instance()->enumerateRxDevices(pluginManager);
} else if (m_deviceType == 1) {
DeviceEnumerator::instance()->enumerateTxDevices(pluginManager);
} else if (m_deviceType == 2) {
DeviceEnumerator::instance()->enumerateMIMODevices(pluginManager);
}
displayDevices();
}
void SamplingDeviceDialog::accept()
{
m_selectedDeviceIndex = m_deviceIndexes[ui->deviceSelect->currentIndex()];

View File

@ -50,8 +50,11 @@ private:
std::vector<int> m_deviceIndexes;
bool m_hasChanged;
void displayDevices();
private slots:
void on_deviceSelect_currentIndexChanged(int index);
void on_refreshDevices_clicked();
void accept();
void reject();
};

View File

@ -31,8 +31,8 @@
<property name="title">
<string>Select from list</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="deviceSelect">
<property name="font">
<font>
@ -42,6 +42,20 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="refreshDevices">
<property name="toolTip">
<string>Refresh list of devices</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/recycle.png</normaloff>:/recycle.png</iconset>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -66,7 +80,9 @@
<tabstops>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
<resources>
<include location="../resources/res.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>

View File

@ -501,7 +501,7 @@ void Workspace::stackVerticalSubWindows()
// Calculate spare vertical space, to be shared between non-fixed windows
int spareSpacePerWindow;
if (requiresVScrollBar) {
if (requiresVScrollBar || (nonFixedWindows == 0)) {
spareSpacePerWindow = 0;
} else {
spareSpacePerWindow = (mdiSize.height() - minHeight) / nonFixedWindows;

View File

@ -18,7 +18,9 @@
#include <QInputDialog>
#include <QMessageBox>
#include <QProgressDialog>
#include <QLabel>
#include <QToolButton>
#include <QFile>
#include <QFileInfo>
#include <QFileDialog>
@ -31,10 +33,11 @@
#include <QStandardPaths>
#include <QDesktopServices>
#include <QProcess>
#include <QDirIterator>
#include <QAction>
#include <QMenuBar>
#include <QStatusBar>
#include <QScreen>
#include "device/devicegui.h"
#include "device/deviceapi.h"
@ -68,6 +71,8 @@
#include "gui/devicesetpresetsdialog.h"
#include "gui/commandsdialog.h"
#include "gui/configurationsdialog.h"
#include "gui/dialogpositioner.h"
#include "gui/welcomedialog.h"
#include "dsp/dspengine.h"
#include "dsp/spectrumvis.h"
#include "dsp/dspcommands.h"
@ -85,6 +90,9 @@
#include "webapi/webapiserver.h"
#include "webapi/webapiadapter.h"
#include "commands/command.h"
#ifdef ANDROID
#include "util/android.h"
#endif
#include "mainwindow.h"
@ -107,9 +115,17 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
m_mainCore(MainCore::instance()),
m_dspEngine(DSPEngine::instance()),
m_lastEngineState(DeviceAPI::StNotStarted),
m_dateTimeWidget(nullptr),
m_showSystemWidget(nullptr),
m_commandKeyReceiver(nullptr),
m_fftWisdomProcess(nullptr)
{
#ifdef ANDROID
bool showWelcome = true;
#else
bool showWelcome = false;
#endif
qDebug() << "MainWindow::MainWindow: start";
setWindowTitle("SDRangel");
@ -134,10 +150,21 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
splash->showStatusMessage("starting...", Qt::white);
setWindowIcon(QIcon(":/sdrangel_icon.png"));
createMenuBar();
createStatusBar();
#ifndef ANDROID
// To save screen space on Android, don't have menu bar. Instead menus are accessed via toolbar button
createMenuBar(nullptr);
createStatusBar();
#endif
#ifdef ANDROID
if (screen()->isLandscape(screen()->primaryOrientation())) {
setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::West);
} else {
setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::South);
}
#else
setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::West);
#endif
setTabPosition(Qt::RightDockWidgetArea, QTabWidget::East);
setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
@ -146,6 +173,12 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleMessages()), Qt::QueuedConnection);
connect(screen(), &QScreen::orientationChanged, this, &MainWindow::orientationChanged);
screen()->setOrientationUpdateMask(Qt::PortraitOrientation
| Qt::LandscapeOrientation
| Qt::InvertedPortraitOrientation
| Qt::InvertedLandscapeOrientation);
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(1000);
@ -209,6 +242,16 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
{
qDebug() << "MainWindow::MainWindow: no or empty current configuration, creating empty workspace...";
addWorkspace();
// If no configurations, load some basic examples
if (m_mainCore->m_settings.getConfigurations()->size() == 0) {
loadDefaultConfigurations();
}
}
else
{
// Only show welcome dialog first time program is run
showWelcome = false;
}
}
@ -240,6 +283,16 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
restoreGeometry(qUncompress(QByteArray::fromBase64(s.value("mainWindowGeometry").toByteArray())));
restoreState(qUncompress(QByteArray::fromBase64(s.value("mainWindowState").toByteArray())));
if (showWelcome)
{
// Show welcome dialog
WelcomeDialog welcomeDialog(this);
new DialogPositioner(&welcomeDialog, true);
welcomeDialog.exec();
// Show configurations
openConfigurationDialog(true);
}
qDebug() << "MainWindow::MainWindow: end";
}
@ -340,6 +393,11 @@ void MainWindow::sampleSourceAdd(Workspace *deviceWorkspace, Workspace *spectrum
deviceWorkspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI);
spectrumWorkspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI);
emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI);
#ifdef ANDROID
// Seemingly needed on some versions of Android, otherwise the new windows aren't always displayed??
deviceWorkspace->repaint();
#endif
}
void MainWindow::sampleSourceCreate(
@ -1165,7 +1223,9 @@ void MainWindow::loadSettings()
m_mainCore->m_settings.load();
m_mainCore->m_settings.sortPresets();
m_mainCore->m_settings.sortCommands();
m_mainCore->setLoggingOptions();
if (m_mainCore->m_logger) {
m_mainCore->setLoggingOptions();
}
}
void MainWindow::loadDeviceSetPresetSettings(const Preset* preset, int deviceSetIndex)
@ -1230,6 +1290,59 @@ void MainWindow::saveFeatureSetPresetSettings(FeatureSetPreset* preset, int feat
featureUI->saveFeatureSetSettings(preset);
}
void MainWindow::loadDefaultConfigurations()
{
QDirIterator configurationsIt(":configurations", QDirIterator::Subdirectories);
while (configurationsIt.hasNext())
{
QString group = configurationsIt.next();
QDirIterator groupIt(group, {"*.cfgx"}, QDir::Files);
while (groupIt.hasNext())
{
QFile file(groupIt.next());
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QByteArray base64Str;
QTextStream instream(&file);
instream >> base64Str;
file.close();
Configuration* configuration = MainCore::instance()->m_settings.newConfiguration("", "");
configuration->deserialize(QByteArray::fromBase64(base64Str));
}
else
{
qDebug() << "MainWindow::loadDefaultConfigurations: Failed to open configuration " << file.fileName();
}
}
}
QDirIterator presetIt(":presets", QDirIterator::Subdirectories);
while (presetIt.hasNext())
{
QString group = presetIt.next();
QDirIterator groupIt(group, {"*.prex"}, QDir::Files);
while (groupIt.hasNext())
{
QFile file(groupIt.next());
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QByteArray base64Str;
QTextStream instream(&file);
instream >> base64Str;
file.close();
Preset* preset = MainCore::instance()->m_settings.newPreset("", "");
preset->deserialize(QByteArray::fromBase64(base64Str));
}
else
{
qDebug() << "MainWindow::loadDefaultConfigurations: Failed to open preset " << file.fileName();
}
}
}
}
void MainWindow::loadConfiguration(const Configuration *configuration, bool fromDialog)
{
qDebug("MainWindow::loadConfiguration: configuration [%s | %s] %d workspace(s) - %d device set(s) - %d feature(s)",
@ -1240,23 +1353,26 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
configuration->getFeatureSetPreset().getFeatureCount()
);
QMessageBox *waitBox = nullptr;
QProgressDialog *waitBox = nullptr;
if (fromDialog)
{
waitBox = new QMessageBox(this);
waitBox->setStandardButtons(QMessageBox::NoButton);
waitBox->setWindowModality(Qt::NonModal);
waitBox->setIcon(QMessageBox::Information);
waitBox->setText("Loading configuration ");
waitBox->setInformativeText("Deleting existing...");
waitBox->setWindowTitle("Please wait");
waitBox->show();
waitBox->raise();
waitBox = new QProgressDialog("Loading configuration...", "", 0, 100, this);
waitBox->setWindowModality(Qt::WindowModal);
waitBox->setAttribute(Qt::WA_DeleteOnClose, true);
waitBox->setMinimumDuration(0);
waitBox->setCancelButton(nullptr);
waitBox->setValue(1);
QApplication::processEvents();
}
// Wipe out everything first
if (waitBox)
{
waitBox->setLabelText("Deleting existing...");
waitBox->setValue(5);
QApplication::processEvents();
}
// Device sets
while (m_deviceUIs.size() > 0) {
removeLastDeviceSet();
@ -1276,6 +1392,7 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
{
addWorkspace();
m_workspaces[i]->setAutoStackOption(configuration->getWorkspaceAutoStackOptions()[i]);
m_workspaces[i]->setTabSubWindowsOption(configuration->getWorkspaceTabSubWindowsOptions()[i]);
}
if (m_workspaces.size() <= 0) { // cannot go further if there are no workspaces
@ -1283,8 +1400,11 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
}
// Device sets
if (waitBox) {
waitBox->setInformativeText("Loading device sets...");
if (waitBox)
{
waitBox->setLabelText("Loading device sets...");
waitBox->setValue(25);
QApplication::processEvents();
}
const QList<Preset>& deviceSetPresets = configuration->getDeviceSetPresets();
@ -1354,11 +1474,20 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
m_deviceUIs.back()->m_deviceGUI->restoreGeometry(deviceSetPreset.getDeviceGeometry());
m_deviceUIs.back()->m_mainSpectrumGUI->restoreGeometry(deviceSetPreset.getSpectrumGeometry());
m_deviceUIs.back()->loadDeviceSetSettings(&deviceSetPreset, m_pluginManager->getPluginAPI(), &m_workspaces, nullptr);
if (waitBox)
{
waitBox->setValue(waitBox->value() + 50/deviceSetPresets.size());
QApplication::processEvents();
}
}
// Features
if (waitBox) {
waitBox->setInformativeText("Loading device sets...");
if (waitBox)
{
waitBox->setLabelText("Loading feature sets...");
waitBox->setValue(75);
QApplication::processEvents();
}
m_featureUIs[0]->loadFeatureSetSettings(
@ -1381,8 +1510,11 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
}
// Lastly restore workspaces geometry
if (waitBox) {
waitBox->setInformativeText("Finalizing...");
if (waitBox)
{
waitBox->setValue(90);
waitBox->setLabelText("Finalizing...");
QApplication::processEvents();
}
for (int i = 0; i < configuration->getNumberOfWorkspaceGeometries(); i++)
@ -1390,12 +1522,16 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from
m_workspaces[i]->restoreGeometry(configuration->getWorkspaceGeometries()[i]);
m_workspaces[i]->restoreGeometry(configuration->getWorkspaceGeometries()[i]);
m_workspaces[i]->adjustSubWindowsAfterRestore();
#ifdef ANDROID
// On Android, workspaces seem to be restored to 0,20, rather than 0,0
m_workspaces[i]->move(m_workspaces[i]->pos().x(), 0);
// Need to call updateGeometry, otherwise sometimes the layout is corrupted
m_workspaces[i]->updateGeometry();
#endif
}
if (waitBox)
{
waitBox->close();
delete waitBox;
if (waitBox) {
waitBox->setValue(100);
}
}
@ -1430,6 +1566,7 @@ void MainWindow::saveConfiguration(Configuration *configuration)
{
configuration->getWorkspaceGeometries().push_back(workspace->saveGeometry());
configuration->getWorkspaceAutoStackOptions().push_back(workspace->getAutoStackOption());
configuration->getWorkspaceTabSubWindowsOptions().push_back(workspace->getTabSubWindowsOption());
}
}
@ -1461,24 +1598,46 @@ QString MainWindow::openGLVersion()
}
}
void MainWindow::createMenuBar()
void MainWindow::createMenuBar(QToolButton *button)
{
QMenuBar *menuBar = this->menuBar();
QMenu *fileMenu, *viewMenu, *workspacesMenu, *preferencesMenu, *helpMenu;
if (button == nullptr)
{
QMenuBar *menuBar = this->menuBar();
fileMenu = menuBar->addMenu("&File");
viewMenu = menuBar->addMenu("&View");
workspacesMenu = menuBar->addMenu("&Workspaces");
preferencesMenu = menuBar->addMenu("&Preferences");
helpMenu = menuBar->addMenu("&Help");
}
else
{
QMenu *menu = new QMenu();
fileMenu = new QMenu("&File");
menu->addMenu(fileMenu);
viewMenu = new QMenu("&View");
menu->addMenu(viewMenu);
workspacesMenu = new QMenu("&Workspaces");
menu->addMenu(workspacesMenu);
preferencesMenu = new QMenu("&Preferences");
menu->addMenu(preferencesMenu);
helpMenu = new QMenu("&Help");
menu->addMenu(helpMenu);
button->setMenu(menu);
}
QMenu *fileMenu = menuBar->addMenu("&File");
QAction *exitAction = fileMenu->addAction("E&xit");
exitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
exitAction->setToolTip("Exit");
QObject::connect(exitAction, &QAction::triggered, this, &QMainWindow::close);
QMenu *viewMenu = menuBar->addMenu("&View");
QAction *fullscreenAction = viewMenu->addAction("&Fullscreen");
fullscreenAction->setShortcut(QKeySequence(Qt::Key_F11));
fullscreenAction->setToolTip("Toggle fullscreen view");
fullscreenAction->setCheckable(true);
QObject::connect(fullscreenAction, &QAction::triggered, this, &MainWindow::on_action_View_Fullscreen_toggled);
QMenu *workspacesMenu = menuBar->addMenu("&Workspaces");
QAction *newWorkspaceAction = workspacesMenu->addAction("&New");
newWorkspaceAction->setToolTip("Add a new workspace");
QObject::connect(newWorkspaceAction, &QAction::triggered, this, &MainWindow::addWorkspace);
@ -1489,7 +1648,6 @@ void MainWindow::createMenuBar()
removeEmptyWorkspacesAction->setToolTip("Remove empty workspaces");
QObject::connect(removeEmptyWorkspacesAction, &QAction::triggered, this, &MainWindow::removeEmptyWorkspaces);
QMenu *preferencesMenu = menuBar->addMenu("&Preferences");
QAction *configurationsAction = preferencesMenu->addAction("&Configurations...");
configurationsAction->setToolTip("Manage configurations");
QObject::connect(configurationsAction, &QAction::triggered, this, &MainWindow::on_action_Configurations_triggered);
@ -1519,7 +1677,6 @@ void MainWindow::createMenuBar()
saveAllAction->setToolTip("Save all current settings");
QObject::connect(saveAllAction, &QAction::triggered, this, &MainWindow::on_action_saveAll_triggered);
QMenu *helpMenu = menuBar->addMenu("&Help");
QAction *quickStartAction = helpMenu->addAction("&Quick start...");
quickStartAction->setToolTip("Instructions for quick start");
QObject::connect(quickStartAction, &QAction::triggered, this, &MainWindow::on_action_Quick_Start_triggered);
@ -1882,7 +2039,11 @@ void MainWindow::handleWorkspaceVisibility(Workspace *workspace, bool visibility
void MainWindow::addWorkspace()
{
int workspaceIndex = m_workspaces.size();
m_workspaces.push_back(new Workspace(workspaceIndex));
Workspace *workspace = new Workspace(workspaceIndex);
m_workspaces.push_back(workspace);
if (workspace->getMenuButton()) {
createMenuBar(workspace->getMenuButton());
}
QStringList featureNames;
m_pluginManager->listFeatures(featureNames);
m_workspaces.back()->addAvailableFeatures(featureNames);
@ -1923,6 +2084,13 @@ void MainWindow::addWorkspace()
&MainWindow::openFeaturePresetsDialog
);
QObject::connect(
m_workspaces.back(),
&Workspace::configurationPresetsDialogRequested,
this,
&MainWindow::on_action_Configurations_triggered
);
QObject::connect(
m_workspaces.back(),
&Workspace::startAllDevices,
@ -1951,7 +2119,7 @@ void MainWindow::addWorkspace()
// tabBars.back()->setStyleSheet("QTabBar::tab:selected { background: rgb(128,70,0); }"); // change text color so it is visible
// }
}
}
}
void MainWindow::viewAllWorkspaces()
{
@ -2006,6 +2174,13 @@ void MainWindow::removeEmptyWorkspaces()
}
}
}
#ifdef ANDROID
// Need at least one workspace on Android, as no menus without
if (m_workspaces.size() == 0) {
addWorkspace();
}
#endif
}
void MainWindow::on_action_View_Fullscreen_toggled(bool checked)
@ -2063,7 +2238,12 @@ void MainWindow::on_action_Loaded_Plugins_triggered()
void MainWindow::on_action_Configurations_triggered()
{
ConfigurationsDialog dialog(this);
openConfigurationDialog(false);
}
void MainWindow::openConfigurationDialog(bool openOnly)
{
ConfigurationsDialog dialog(openOnly, this);
dialog.setConfigurations(m_mainCore->m_settings.getConfigurations());
dialog.populateTree();
QObject::connect(
@ -2078,24 +2258,28 @@ void MainWindow::on_action_Configurations_triggered()
this,
[=](const Configuration* configuration) { this->loadConfiguration(configuration, true); }
);
new DialogPositioner(&dialog, true);
dialog.exec();
}
void MainWindow::on_action_Audio_triggered()
{
AudioDialogX audioDialog(m_dspEngine->getAudioDeviceManager(), this);
new DialogPositioner(&audioDialog, true);
audioDialog.exec();
}
void MainWindow::on_action_Graphics_triggered()
{
GraphicsDialog graphicsDialog(m_mainCore->m_settings, this);
new DialogPositioner(&graphicsDialog, true);
graphicsDialog.exec();
}
void MainWindow::on_action_Logging_triggered()
{
LoggingDialog loggingDialog(m_mainCore->m_settings, this);
new DialogPositioner(&loggingDialog, true);
loggingDialog.exec();
m_mainCore->setLoggingOptions();
}
@ -2103,6 +2287,7 @@ void MainWindow::on_action_Logging_triggered()
void MainWindow::on_action_My_Position_triggered()
{
MyPositionDialog myPositionDialog(m_mainCore->m_settings, this);
new DialogPositioner(&myPositionDialog, true);
myPositionDialog.exec();
}
@ -2110,6 +2295,7 @@ void MainWindow::on_action_DeviceUserArguments_triggered()
{
qDebug("MainWindow::on_action_DeviceUserArguments_triggered");
DeviceUserArgsDialog deviceUserArgsDialog(DeviceEnumerator::instance(), m_mainCore->m_settings.getDeviceUserArgs(), this);
new DialogPositioner(&deviceUserArgsDialog, true);
deviceUserArgsDialog.exec();
}
@ -2121,6 +2307,7 @@ void MainWindow::on_action_commands_triggered()
commandsDialog.setApiPort(m_apiServer->getPort());
commandsDialog.setCommandKeyReceiver(m_commandKeyReceiver);
commandsDialog.populateTree();
new DialogPositioner(&commandsDialog, true);
commandsDialog.exec();
}
@ -2140,6 +2327,7 @@ void MainWindow::on_action_FFT_triggered()
this,
SLOT(fftWisdomProcessFinished(int, QProcess::ExitStatus)));
FFTWisdomDialog fftWisdomDialog(m_fftWisdomProcess, this);
new DialogPositioner(&fftWisdomDialog, true);
if (fftWisdomDialog.exec() == QDialog::Rejected)
{
@ -2790,6 +2978,7 @@ void MainWindow::openFeaturePresetsDialog(QPoint p, Workspace *workspace)
dialog.setWorkspaces(&m_workspaces);
dialog.populateTree();
dialog.move(p);
new DialogPositioner(&dialog, true);
dialog.exec();
if (dialog.wasPresetLoaded())
@ -2820,6 +3009,7 @@ void MainWindow::openDeviceSetPresetsDialog(QPoint p, DeviceGUI *deviceGUI)
dialog.setWorkspaces(&m_workspaces);
dialog.populateTree((int) deviceGUI->getDeviceType());
dialog.move(p);
new DialogPositioner(&dialog, true);
dialog.exec();
}
@ -2840,7 +3030,9 @@ void MainWindow::on_action_About_triggered()
void MainWindow::updateStatus()
{
m_dateTimeWidget->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss t"));
if (m_dateTimeWidget) {
m_dateTimeWidget->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss t"));
}
}
void MainWindow::commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifiers, bool release)
@ -2862,3 +3054,30 @@ void MainWindow::commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifie
}
}
}
void MainWindow::keyPressEvent(QKeyEvent* event)
{
#ifdef ANDROID
if (event->key() == Qt::Key_Back)
{
// On Android, we don't want to exit when back key is pressed, just run in the background
Android::moveTaskToBack();
}
else
#endif
{
QMainWindow::keyPressEvent(event);
}
}
void MainWindow::orientationChanged(Qt::ScreenOrientation orientation)
{
#ifdef ANDROID
// Adjust workspace tab position, to leave max space for MDI windows
if ((orientation == Qt::LandscapeOrientation) || (orientation == Qt::InvertedLandscapeOrientation)) {
setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::West);
} else {
setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::South);
}
#endif
}

View File

@ -34,6 +34,7 @@
class QLabel;
class QTreeWidgetItem;
class QDir;
class QToolButton;
class DSPEngine;
class DSPDeviceSourceEngine;
@ -136,7 +137,7 @@ private:
void saveFeatureSetPresetSettings(FeatureSetPreset* preset, int featureSetIndex);
QString openGLVersion();
void createMenuBar();
void createMenuBar(QToolButton *button);
void createStatusBar();
void closeEvent(QCloseEvent*);
void applySettings();
@ -171,6 +172,9 @@ private:
bool handleMessage(const Message& cmd);
protected:
virtual void keyPressEvent(QKeyEvent* event) override;
private slots:
void handleMessages();
void handleWorkspaceVisibility(Workspace *workspace, bool visibility);
@ -194,6 +198,8 @@ private slots:
void addWorkspace();
void viewAllWorkspaces();
void removeEmptyWorkspaces();
void openConfigurationDialog(bool openOnly);
void loadDefaultConfigurations();
void loadConfiguration(const Configuration *configuration, bool fromDialog = false);
void saveConfiguration(Configuration *configuration);
void sampleSourceAdd(Workspace *deviceWorkspace, Workspace *spectrumWorkspace, int deviceIndex);
@ -215,6 +221,7 @@ private slots:
void openDeviceSetPresetsDialog(QPoint p, DeviceGUI *deviceGUI);
void commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifiers, bool release);
void fftWisdomProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void orientationChanged(Qt::ScreenOrientation orientation);
};
#endif // INCLUDE_MAINWINDOW_H

View File

@ -0,0 +1 @@
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

View File

@ -0,0 +1 @@
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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
kAABAXABB1JUTCBTRFJwAgNEQUKAAxuQAAEBcAEHZGVmYXVsdHACB25vIG5hbWUAZAAAZAEBgGVCAdnQywADAAAAAAAAAAAAAAAAAZoAAAMlAAAAAAAAAAAAAAGaAAADJQAAAAAAAAAAAZsAAAAAAAAAAAAAAZoAAAMlAMgBAYHJBB2QAAEBcAEHZGVmYXVsdHACB25vIG5hbWUwAwQNcx2AgAQAgAXikAABAQABAgQAAAIAAAMBBEAEBAAAAABABQRCjAAAYAYBAWAHAQFgCAEAYAkBAAAKAQFgCwEBAA0BEgAOAQEADwEeYBABAQARATJAEgQ/KPXDABMBAQAUAQpgFQEAcBYJMTI3LjAuMC4xEBcCIrdgGAEAYBkBAQAaATJgGwEAABwBAmAdAQAAHgBgHwEAACABA3AhBUFuZ2VsACIBAgAjAAAkAicQACUCJxAAJgInEAAnAQVgKgEBACsBBQAsAQEALQEBAC4AYC8BAGAwAQAAZAAAbgCAKAQAAAAAgCkEAAAAAGAGAQEABwBgCAEBgAlCAdnQywADAAAAABOIAAAAAAAAFQMAAAFuAAATiAAAAAAAABUDAAABbv////8AAAAAAZsAABOIAAAAAAAAFQMAAAFuAAoAgAtCAdnQywADAAAAABOIAAAAAAAAFPMAAAEEAAATiAAAAAAAABTzAAABBP////8AAAAAAZsAABOIAAAAAAAAFPMAAAEEAAwAcA0cc2RyYW5nZWwuc2FtcGxlc291cmNlLnJ0bHNkcnAOCDAwMDAwMDAxAA8AABAAABQBAXAYHHNkcmFuZ2VsLnNhbXBsZXNvdXJjZS5ydGxzZHJwGQgwMDAwMDAwMQAaAIAbXpAAAQEAAgIBpAADABAEAGAFAQBgBgEAAAcBAgAIAx9AAGAJAQBgCgEAYAsBAGAMAQAgDQAQDgMmJaBgDwEAYBABAHARCTEyNy4wLjAuMRASAiK4EBMAYBQBAWAVAQAAyAEBcMkZc2RyYW5nZWwuY2hhbm5lbC5kYWJkZW1vZIHKAZWQAAEBAAEAAAIAcAMAQAQESbufQEAFBECgAABgBgEAcAcVU3lzdGVtIGRlZmF1bHQgZGV2aWNlgAgjkAABAQABABACBP9NaRlwAw9EQUIgRGVtb2R1bGF0b3IABwAQCQT/TWkZcAoPREFCIERlbW9kdWxhdG9yYAsBAHAMCTEyNy4wLjAuMRANAiK4EA4AEA8AgBCeAAAA/wAAAAAAAAAEAAAAIgBzAGUAdAB0AGkAbgBnAHMAQwBvAG4AdABhAGkAbgBlAHIAAAABAAAAIABwAHIAbwBnAHIAYQBtAEMAbwBuAHQAYQBpAG4AZQByAAAAAQAAABwAYwB1AHIAcgBlAG4AdABQAHIAbwBnAHIAYQBtAAAAAQAAABQAcwB0AGEAdABpAHMAdABpAGMAcwAAAAEAEQCAEkIB2dDLAAMAAAAAAAAAAAAAAAABmgAAAvwAAAAAAAAAAP////7////+AAAAAAIAAAABmwAAAAAAAAAAAAABmgAAAvxgEwEAAGQAAGUBAQBmAQIAyAH/AMkB/wDKAf8EASwBAWQBLQEABAGQAQFkAZEBAQ==

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
kAABAXABB1JUTCBTRFJwAgdOT0FBIDE4MAMECDgI0IAEAIEFAWmQAAEBAAECBAAAAgAAAwEEQAQEAAAAAEAFBEKMAABgBgEBYAcBAWAIAQBgCQEAAAoBAWALAQEADQESAA4BAQAPAR5gEAEBABEBMkASBD8o9cMAEwEBABQBCmAVAQBwFgkxMjcuMC4wLjEQFwIit2AYAQBgGQEBABoBMmAbAQAAHAECYB0BAAAeAGAfAQAAIAEDcCEFQW5nZWwAIgECACMAACQCJxAAJQInEAAmAicQACcBBWAqAQEAKwEFACwBAQAtAQEALgBgLwEAYDABAABkAABuAIAoiwAAAAMAAAAACDOcAAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADUAAAAACDgQoAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADgAAAAACCuswAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADmAKQQAAAAAYAYBAQAHAGAIAQGACUIB2dDLAAMAAAAAE4gAAAAAAAAVAwAAAW4AABOIAAAAAAAAFQMAAAFu/////wAAAAABmwAAE4gAAAAAAAAVAwAAAW4ACgCAC0IB2dDLAAMAAAAAAAAAAAAAAAABfwAAARAAAAAAAAAAFAAAAWsAAAEYAAAAAAIAAAABmwAAAAAAAAAAAAABfwAAARAADABwDRxzZHJhbmdlbC5zYW1wbGVzb3VyY2UucnRsc2RycA4IMDAwMDAwMDEADwAAEAAAFAEBcBgcc2RyYW5nZWwuc2FtcGxlc291cmNlLnJ0bHNkcnAZCDAwMDAwMDAxABoAgBtfkAABAQACAgGkAAMAEAQBA2AFAQBgBgEAAAcBAgAIAw9CQGAJAQBgCgEAYAsBAGAMAQAgDQAQDgMmJaBgDwEAYBABAHARCTEyNy4wLjAuMRASAiK4EBMAYBQBAWAVAQAAyAEBcMkZc2RyYW5nZWwuY2hhbm5lbC5hcHRkZW1vZIHKAYmQAAEBAAECVfAAAgBAAwRHHEAAQAQERoTQAGAFAQBgBgEBYAcBAGAIAQBgCQEAYAoBAAALAGAMAQBgDQEBcA4DQWxsYA8BAHAQAAARAgDIYBIBAAATARSAFCWQAAEBAAECVfAQAgT/2HCpcAMPQVBUIERlbW9kdWxhdG9yAAcAEBUE/9hwqXAWD0FQVCBEZW1vZHVsYXRvcmAXAQBwGAkxMjcuMC4wLjEQGQIiuBAaABAbAIAcWgAAAP8AAAAAAAAAAgAAACIAcwBlAHQAdABpAG4AZwBzAEMAbwBuAHQAYQBpAG4AZQByAAAAAQAAABwAaQBtAGEAZwBlAEMAbwBuAHQAYQBpAG4AZQByAAAAAWAdAQFgHgEAAB8BZAAgAgDIcCEAACIAACMBCgAkARRAJQQAAAAAQCYEAAAAAAAnAIAoQgHZ0MsAAwAAAAATiAAAAAAAABUiAAADBwAAE4gAAAAAAAAVIgAAAwf/////AAAAAAGbAAATiAAAAAAAABUiAAADB2ApAQA=

View File

@ -0,0 +1 @@
kAABAXABB1JUTCBTRFJwAgdOT0FBIDE5MAMECCuk8IAEAIEFAWmQAAEBAAECBAAAAgAAAwEEQAQEAAAAAEAFBEKMAABgBgEBYAcBAWAIAQBgCQEAAAoBAWALAQEADQESAA4BAQAPAR5gEAEBABEBMkASBD8o9cMAEwEBABQBCmAVAQBwFgkxMjcuMC4wLjEQFwIit2AYAQBgGQEBABoBMmAbAQAAHAECYB0BAAAeAGAfAQAAIAEDcCEFQW5nZWwAIgECACMAACQCJxAAJQInEAAmAicQACcBBWAqAQEAKwEFACwBAQAtAQEALgBgLwEAYDABAABkAABuAIAoiwAAAAMAAAAACDOcAAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADUAAAAACDgQoAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADgAAAAACCuswAAAnEAB//8rKzY2//8AAAAAAAMAAAAOAE4ATwBBAEEAIAAxADmAKQQAAAAAYAYBAQAHAGAIAQGACUIB2dDLAAMAAAAAE4gAAAAAAAAVAwAAAW4AABOIAAAAAAAAFQMAAAFu/////wAAAAABmwAAE4gAAAAAAAAVAwAAAW4ACgCAC0IB2dDLAAMAAAAAAAAAAAAAAAABfwAAARAAAAAAAAAAFAAAAWsAAAEYAAAAAAIAAAABmwAAAAAAAAAAAAABfwAAARAADABwDRxzZHJhbmdlbC5zYW1wbGVzb3VyY2UucnRsc2RycA4IMDAwMDAwMDEADwAAEAAAFAEBcBgcc2RyYW5nZWwuc2FtcGxlc291cmNlLnJ0bHNkcnAZCDAwMDAwMDAxABoAgBtfkAABAQACAgGkAAMAEAQBA2AFAQBgBgEAAAcBAgAIAw9CQGAJAQBgCgEAYAsBAGAMAQAgDQAQDgMmJaBgDwEAYBABAHARCTEyNy4wLjAuMRASAiK4EBMAYBQBAWAVAQAAyAEBcMkZc2RyYW5nZWwuY2hhbm5lbC5hcHRkZW1vZIHKAYmQAAEBAAECVfAAAgBAAwRHHEAAQAQERoTQAGAFAQBgBgEBYAcBAGAIAQBgCQEAYAoBAAALAGAMAQBgDQEBcA4DQWxsYA8BAHAQAAARAgDIYBIBAAATARSAFCWQAAEBAAECVfAQAgT/2HCpcAMPQVBUIERlbW9kdWxhdG9yAAcAEBUE/9hwqXAWD0FQVCBEZW1vZHVsYXRvcmAXAQBwGAkxMjcuMC4wLjEQGQIiuBAaABAbAIAcWgAAAP8AAAAAAAAAAgAAACIAcwBlAHQAdABpAG4AZwBzAEMAbwBuAHQAYQBpAG4AZQByAAAAAQAAABwAaQBtAGEAZwBlAEMAbwBuAHQAYQBpAG4AZQByAAAAAWAdAQFgHgEAAB8BZAAgAgDIcCEAACIAACMBCgAkARRAJQQAAAAAQCYEAAAAAAAnAIAoQgHZ0MsAAwAAAAATiAAAAAAAABUiAAADBwAAE4gAAAAAAAAVIgAAAwf/////AAAAAAGbAAATiAAAAAAAABUiAAADB2ApAQA=

13
settings/settings.qrc Normal file
View File

@ -0,0 +1,13 @@
<RCC>
<qresource prefix="/">
<file>configurations/rtlsdr/adsb.cfgx</file>
<file>configurations/rtlsdr/ais.cfgx</file>
<file>configurations/rtlsdr/apt.cfgx</file>
<file>configurations/rtlsdr/dab.cfgx</file>
<file>configurations/rtlsdr/isspacket.cfgx</file>
<file>configurations/rtlsdr/radiosonde.cfgx</file>
<file>configurations/airspyhf/radioclockeu.cfgx</file>
<file>presets/rtlsdr/noaa18.prex</file>
<file>presets/rtlsdr/noaa19.prex</file>
</qresource>
</RCC>