diff --git a/.gitignore b/.gitignore index 142816ce..adc758bc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ build/ *.wav .DS_Store sdrpp_v0.2.5_beta_x64 -sdrpp_v0.2.5_beta_x64_new_wf \ No newline at end of file +sdrpp_v0.2.5_beta_x64_new_wf +root_dev/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b2bcc8d..fb0596e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,19 +1,44 @@ cmake_minimum_required(VERSION 3.13) project(sdrpp) +option(OPT_BUILD_RTL_TCP_SOURCE "Build RTL-TCP Source Module (no dependencies required)" ON) +option(OPT_BUILD_SPYSERVER_SOURCE "Build SpyServer Source Module (no dependencies required)" ON) +option(OPT_BUILD_SOAPY_SOURCE "Build SoapySDR Source Module (Depedencies: soapysdr)" ON) +option(OPT_BUILD_AIRSPYHF_SOURCE "Build Airspy HF+ Source Module (Depedencies: libairspyhf)" ON) +option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Depedencies: libiio, libad9361)" ON) +option(OPT_BUILD_AUDIO_SINK "Build Audio Sink Module (Depedencies: portaudio)" ON) + # Core of SDR++ add_subdirectory("core") -# Cross platform modules +# Base modules add_subdirectory("radio") add_subdirectory("recorder") -add_subdirectory("soapy_source") -add_subdirectory("airspyhf_source") -#add_subdirectory("file_source") + +# Source modules +if (OPT_BUILD_RTL_TCP_SOURCE) add_subdirectory("rtl_tcp_source") -add_subdirectory("audio_sink") -#add_subdirectory("rx888_source") +endif (OPT_BUILD_RTL_TCP_SOURCE) + +if (OPT_BUILD_SPYSERVER_SOURCE) +add_subdirectory("spyserver_source") +endif (OPT_BUILD_SPYSERVER_SOURCE) + +if (OPT_BUILD_SOAPY_SOURCE) +add_subdirectory("soapy_source") +endif (OPT_BUILD_SOAPY_SOURCE) + +if (OPT_BUILD_AIRSPYHF_SOURCE) +add_subdirectory("airspyhf_source") +endif (OPT_BUILD_AIRSPYHF_SOURCE) + +if (OPT_BUILD_PLUTOSDR_SOURCE) add_subdirectory("plutosdr_source") +endif (OPT_BUILD_PLUTOSDR_SOURCE) + +if (OPT_BUILD_AUDIO_SINK) +add_subdirectory("audio_sink") +endif (OPT_BUILD_AUDIO_SINK) if (MSVC) set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc") diff --git a/airspyhf_source/src/main.cpp b/airspyhf_source/src/main.cpp index 45a388ba..42493c8c 100644 --- a/airspyhf_source/src/main.cpp +++ b/airspyhf_source/src/main.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -17,6 +18,8 @@ SDRPP_MOD_INFO { /* Max instances */ 1 }; +//ConfigManager config; + const char* AGG_MODES_STR = "Off\0Low\0High\0"; class AirspyHFSourceModule : public ModuleManager::Instance { @@ -36,7 +39,10 @@ public: handler.stream = &stream; refresh(); - selectFirst(); + + // config.aquire(); + // std::string serString = config.conf["device"]; + // config.release(); sigpath::sourceManager.registerSource("Airspy HF+", &handler); } @@ -92,9 +98,10 @@ public: std::string str = buf; if (serial == str) { selectBySerial(devList[i]); - break; + return; } } + selectFirst(); } void selectBySerial(uint64_t serial) { @@ -287,7 +294,12 @@ private: }; MOD_EXPORT void _INIT_() { - // Do your one time init here +// config.setPath(ROOT_DIR "/airspyhf_config.json"); +// json defConf; +// defConf["device"] = ""; +// defConf["devices"] = json::object(); +// config.load(defConf); +// config.enableAutoSave(); } MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { @@ -299,5 +311,6 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { } MOD_EXPORT void _END_() { - // Do your one shutdown here + // config.disableAutoSave(); + // config.save(); } \ No newline at end of file diff --git a/core/src/config.h b/core/src/config.h index f6933eb9..277d7a44 100644 --- a/core/src/config.h +++ b/core/src/config.h @@ -6,20 +6,6 @@ using nlohmann::json; -#define DEV_BUILD - -#define SDRPP_RESOURCE_DIR "/usr/local/" - -#ifndef ROOT_DIR -#ifdef DEV_BUILD -#define ROOT_DIR "../root_dev" -#elif _WIN32 -#define ROOT_DIR "." -#else -#define ROOT_DIR "/etc/sdrpp" -#endif -#endif - class ConfigManager { public: ConfigManager(); @@ -32,20 +18,11 @@ public: void aquire(); void release(bool changed = false); - // static void setResourceDir(std::string path); - // static std::string getResourceDir(); - - // static void setConfigDir(std::string path); - // static std::string getConfigDir(); - json conf; private: static void autoSaveWorker(ConfigManager* _this); - //static std::string resDir; - //static std::string configDir; - std::string path = ""; bool changed = false; bool autoSaveEnabled = false; diff --git a/core/src/core.cpp b/core/src/core.cpp index 041a893c..a6533600 100644 --- a/core/src/core.cpp +++ b/core/src/core.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -63,16 +64,19 @@ duk_ret_t test_func(duk_context *ctx) { } // main -int sdrpp_main() { +int sdrpp_main(int argc, char *argv[]) { #ifdef _WIN32 //FreeConsole(); // ConfigManager::setResourceDir("./res"); // ConfigManager::setConfigDir("."); #endif - spdlog::info("SDR++ v" VERSION_STR); + // Load default options and parse command line + options::loadDefaults(); + if (!options::parse(argc, argv)) { return -1; } + // ======== DEFAULT CONFIG ======== json defConfig; defConfig["bandColors"]["amateur"] = "#FF0000FF"; @@ -114,8 +118,8 @@ int sdrpp_main() { defConfig["windowSize"]["w"] = 1280; // Load config - spdlog::info("Loading config"); - core::configManager.setPath(ROOT_DIR "/config.json"); + spdlog::info("Loading config {0}"); + core::configManager.setPath(options::opts.root + "/config.json"); core::configManager.load(defConfig); core::configManager.enableAutoSave(); @@ -153,7 +157,7 @@ int sdrpp_main() { // Load app icon GLFWimage icons[10]; - icons[0].pixels = stbi_load(((std::string)(ROOT_DIR "/res/icons/sdrpp.png")).c_str(), &icons[0].width, &icons[0].height, 0, 4); + icons[0].pixels = stbi_load(((std::string)(options::opts.root + "/res/icons/sdrpp.png")).c_str(), &icons[0].width, &icons[0].height, 0, 4); icons[1].pixels = (unsigned char*)malloc(16 * 16 * 4); icons[1].width = icons[1].height = 16; icons[2].pixels = (unsigned char*)malloc(24 * 24 * 4); icons[2].width = icons[2].height = 24; icons[3].pixels = (unsigned char*)malloc(32 * 32 * 4); icons[3].width = icons[3].height = 32; @@ -204,11 +208,11 @@ int sdrpp_main() { LoadingScreen::show("Loading band plans"); spdlog::info("Loading band plans"); - bandplan::loadFromDir(ROOT_DIR "/bandplans"); + bandplan::loadFromDir(options::opts.root + "/bandplans"); LoadingScreen::show("Loading band plan colors"); spdlog::info("Loading band plans color table"); - bandplan::loadColorTable(ROOT_DIR "/band_colors.json"); + bandplan::loadColorTable(options::opts.root + "/band_colors.json"); windowInit(); diff --git a/core/src/core.h b/core/src/core.h index d775193c..6a0dbde2 100644 --- a/core/src/core.h +++ b/core/src/core.h @@ -12,4 +12,4 @@ namespace core { void setInputSampleRate(double samplerate); }; -int sdrpp_main(); \ No newline at end of file +int sdrpp_main(int argc, char *argv[]); \ No newline at end of file diff --git a/core/src/gui/icons.cpp b/core/src/gui/icons.cpp index 72447e46..a2f0f979 100644 --- a/core/src/gui/icons.cpp +++ b/core/src/gui/icons.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #define STB_IMAGE_IMPLEMENTATION #include @@ -31,13 +32,13 @@ namespace icons { } void load() { - LOGO = (ImTextureID)(uintptr_t)loadTexture(ROOT_DIR "/res/icons/sdrpp.png"); - PLAY = (ImTextureID)(uintptr_t)loadTexture(ROOT_DIR "/res/icons/play.png"); - STOP = (ImTextureID)(uintptr_t)loadTexture(ROOT_DIR "/res/icons/stop.png"); - MENU = (ImTextureID)(uintptr_t)loadTexture(ROOT_DIR "/res/icons/menu.png"); - MUTED = (ImTextureID)(uintptr_t)loadTexture(ROOT_DIR "/res/icons/muted.png"); - UNMUTED = (ImTextureID)(uintptr_t)loadTexture(ROOT_DIR "/res/icons/unmuted.png"); - NORMAL_TUNING = (ImTextureID)(uintptr_t)loadTexture(ROOT_DIR "/res/icons/normal_tuning.png"); - CENTER_TUNING = (ImTextureID)(uintptr_t)loadTexture(ROOT_DIR "/res/icons/center_tuning.png"); + LOGO = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/sdrpp.png"); + PLAY = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/play.png"); + STOP = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/stop.png"); + MENU = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/menu.png"); + MUTED = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/muted.png"); + UNMUTED = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/unmuted.png"); + NORMAL_TUNING = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/normal_tuning.png"); + CENTER_TUNING = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/center_tuning.png"); } } \ No newline at end of file diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index e0e95de4..efde3a91 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -30,6 +30,7 @@ #include #include #include +#include // const int FFTSizes[] = { // 65536, @@ -143,8 +144,8 @@ void windowInit() { spdlog::info("Loading modules"); // Load modules from /module directory - if (std::filesystem::is_directory(ROOT_DIR "/modules")) { - for (const auto & file : std::filesystem::directory_iterator(ROOT_DIR "/modules")) { + if (std::filesystem::is_directory(options::opts.root + "/modules")) { + for (const auto & file : std::filesystem::directory_iterator(options::opts.root + "/modules")) { std::string path = file.path().generic_string(); if (file.path().extension().generic_string() != SDRPP_MOD_EXTENTSION) { continue; @@ -595,7 +596,6 @@ void drawWindow() { gui::waterfall.setWaterfallMin(fftMin); gui::waterfall.setWaterfallMax(fftMax); - ImGui::End(); if (showCredits) { diff --git a/core/src/gui/style.cpp b/core/src/gui/style.cpp index c22dd806..35fbca89 100644 --- a/core/src/gui/style.cpp +++ b/core/src/gui/style.cpp @@ -2,6 +2,7 @@ #include #include #include +#include namespace style { ImFont* baseFont; @@ -16,9 +17,9 @@ namespace style { ImGui::GetStyle().PopupRounding = 0.0f; ImGui::GetStyle().ScrollbarRounding = 0.0f; - baseFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(ROOT_DIR "/res/fonts/Roboto-Medium.ttf")).c_str(), 16.0f); - bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(ROOT_DIR "/res/fonts/Roboto-Medium.ttf")).c_str(), 42.0f); - hugeFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(ROOT_DIR "/res/fonts/Roboto-Medium.ttf")).c_str(), 128.0f); + baseFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 16.0f); + bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 42.0f); + hugeFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 128.0f); ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); @@ -36,9 +37,9 @@ namespace style { ImGui::GetStyle().PopupRounding = 0.0f; ImGui::GetStyle().ScrollbarRounding = 0.0f; - baseFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(ROOT_DIR "/res/fonts/Roboto-Medium.ttf")).c_str(), 16.0f); - bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(ROOT_DIR "/res/fonts/Roboto-Medium.ttf")).c_str(), 42.0f); - hugeFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(ROOT_DIR "/res/fonts/Roboto-Medium.ttf")).c_str(), 128.0f); + baseFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 16.0f); + bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 42.0f); + hugeFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 128.0f); ImGui::StyleColorsDark(); diff --git a/core/src/options.cpp b/core/src/options.cpp index 53b8ab4d..6e8ba810 100644 --- a/core/src/options.cpp +++ b/core/src/options.cpp @@ -1,7 +1,29 @@ #include +#include namespace options { - void parse(char** argv, int argc) { - + CMDLineOptions opts; + + void loadDefaults() { +#ifdef _WIN32 + opts.root = "."; +#else + opts.root = "~/.sdrpp/"; +#endif + } + + bool parse(int argc, char *argv[]) { + for (int i = 1; i < argc; i++) { + char* arg = argv[i]; + if (!strcmp(arg, "-r") || !strcmp(arg, "--root")) { + if (i == argc - 1) { return false; } + opts.root = argv[++i]; + } + else { + spdlog::error("Invalid command line option: {0}", arg); + return false; + } + } + return true; } } \ No newline at end of file diff --git a/core/src/options.h b/core/src/options.h index b8087748..aec4d71e 100644 --- a/core/src/options.h +++ b/core/src/options.h @@ -1,13 +1,14 @@ #pragma once #include +#include namespace options { struct CMDLineOptions { std::string root; - bool help; }; - CMDLineOptions opts; + SDRPP_EXPORT CMDLineOptions opts; - void parse(char** argv, int argc); -} + void loadDefaults(); + bool parse(int argc, char *argv[]); +} \ No newline at end of file diff --git a/create_root.bat b/create_root.bat new file mode 100644 index 00000000..b2762497 --- /dev/null +++ b/create_root.bat @@ -0,0 +1,4 @@ +echo OFF + +mkdir root_dev +Xcopy root root_dev /E /H /C /I \ No newline at end of file diff --git a/create_root.sh b/create_root.sh new file mode 100644 index 00000000..7fd024fe --- /dev/null +++ b/create_root.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +cp -r root root_dev diff --git a/plutosdr_source/src/main.cpp b/plutosdr_source/src/main.cpp index f0851300..f4dcad91 100644 --- a/plutosdr_source/src/main.cpp +++ b/plutosdr_source/src/main.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -262,7 +263,7 @@ MOD_EXPORT void _INIT_() { defConf["sampleRate"] = 4000000.0f; defConf["gainMode"] = 0; defConf["gain"] = 0.0f; - config.setPath(ROOT_DIR "/plutosdr_source_config.json"); + config.setPath(options::opts.root + "/plutosdr_source_config.json"); config.load(defConf); config.enableAutoSave(); } diff --git a/prepare_root.bat b/prepare_root.bat deleted file mode 100644 index dea93998..00000000 --- a/prepare_root.bat +++ /dev/null @@ -1,6 +0,0 @@ -echo OFF -copy /b/v/y build\Release\* root\ -copy /b/v/y build\radio\Release\radio.dll root\modules\radio.dll -copy /b/v/y build\recorder\Release\recorder.dll root\modules\recorder.dll -copy /b/v/y build\rtl_tcp_source\Release\rtl_tcp_source.dll root\modules\rtl_tcp_source.dll -copy /b/v/y build\soapy\Release\soapy.dll root\modules\soapy.dll \ No newline at end of file diff --git a/prepare_root.sh b/prepare_root.sh deleted file mode 100755 index 25212ac1..00000000 --- a/prepare_root.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -cp build/*/*.so root/modules diff --git a/radio/src/main.cpp b/radio/src/main.cpp index 250b9903..626b79d9 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -197,7 +198,7 @@ private: MOD_EXPORT void _INIT_() { json def = json({}); - config.setPath(ROOT_DIR "/radio_config.json"); + config.setPath(options::opts.root + "/radio_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/recorder/src/main.cpp b/recorder/src/main.cpp index 9ad50fde..a6b375b0 100644 --- a/recorder/src/main.cpp +++ b/recorder/src/main.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -44,7 +45,7 @@ void sampleRateChanged(void* ctx, double sampleRate, int blockSize) { } std::string expandString(std::string input) { - input = std::regex_replace(input, std::regex("%ROOT%"), ROOT_DIR); + input = std::regex_replace(input, std::regex("%ROOT%"), options::opts.root); return std::regex_replace(input, std::regex("//"), "/"); } @@ -306,7 +307,7 @@ struct RecorderContext_t { MOD_EXPORT void _INIT_() { json def = json({}); - config.setPath(ROOT_DIR "/recorder_config.json"); + config.setPath(options::opts.root + "/recorder_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/root/config.json b/root/config.json deleted file mode 100644 index 2a7a2e93..00000000 --- a/root/config.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "audio": { - "Radio": { - "device": "CABLE Input (VB-Audio Virtual Cable)", - "sampleRate": 48000.0, - "volume": 0.42578125 - }, - "Radio 1": { - "device": "CABLE-A Input (VB-Audio Cable A)", - "sampleRate": 48000.0, - "volume": 1.0 - }, - "Radio 2": { - "device": "CABLE Input (VB-Audio Virtual Cable)", - "sampleRate": 48000.0, - "volume": 1.0 - } - }, - "bandPlan": "General", - "bandPlanEnabled": true, - "fftHeight": 300, - "frequency": 98391106, - "max": 0.0, - "maximized": false, - "menuOrder": [ - "Source", - "Radio", - "Recorder", - "Audio", - "Scripting", - "Band Plan", - "Display" - ], - "menuWidth": 300, - "min": -72.05882263183594, - "showWaterfall": true, - "source": "", - "sourceSettings": {}, - "windowSize": { - "h": 720, - "w": 1280 - } -} \ No newline at end of file diff --git a/root/module_list.json b/root/module_list.json deleted file mode 100644 index 6e593fcc..00000000 --- a/root/module_list.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Radio": "./modules/radio.dll", - "Recorder": "./modules/recorder.dll", - "Soapy": "./modules/soapy.dll", - "RTLTCPSource": "./modules/rtl_tcp_source.dll" -} diff --git a/root/radio_config.json b/root/radio_config.json deleted file mode 100644 index c6ea5a59..00000000 --- a/root/radio_config.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Radio 1": { - "demodulator":1 - } -} \ No newline at end of file diff --git a/root/res/icons/center_tuning.png b/root/res/icons/center_tuning.png new file mode 100644 index 00000000..00bf8482 Binary files /dev/null and b/root/res/icons/center_tuning.png differ diff --git a/root/res/icons/menu.png b/root/res/icons/menu.png index 317cfdee..b830b5b5 100644 Binary files a/root/res/icons/menu.png and b/root/res/icons/menu.png differ diff --git a/root/res/icons/muted.png b/root/res/icons/muted.png new file mode 100644 index 00000000..e7a1ad55 Binary files /dev/null and b/root/res/icons/muted.png differ diff --git a/root/res/icons/normal_tuning.png b/root/res/icons/normal_tuning.png new file mode 100644 index 00000000..76d171e2 Binary files /dev/null and b/root/res/icons/normal_tuning.png differ diff --git a/root/res/icons/play.png b/root/res/icons/play.png index 93684dd2..a5bc44df 100644 Binary files a/root/res/icons/play.png and b/root/res/icons/play.png differ diff --git a/root/res/icons/stop.png b/root/res/icons/stop.png index 366651bf..6fac0cbc 100644 Binary files a/root/res/icons/stop.png and b/root/res/icons/stop.png differ diff --git a/root/res/icons/unmuted.png b/root/res/icons/unmuted.png new file mode 100644 index 00000000..64fcb987 Binary files /dev/null and b/root/res/icons/unmuted.png differ diff --git a/root/soapy_source_config.json b/root/soapy_source_config.json deleted file mode 100644 index 376198a8..00000000 --- a/root/soapy_source_config.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "device": "", - "devices": {} -} \ No newline at end of file diff --git a/root_dev/config.json b/root_dev/config.json index d5e1d5fb..ab2e2e27 100644 --- a/root_dev/config.json +++ b/root_dev/config.json @@ -1,11 +1,18 @@ { + "bandColors": { + "amateur": "#FF0000FF", + "aviation": "#00FF00FF", + "broadcast": "#0000FFFF", + "marine": "#00FFFFFF", + "military": "#FFFF00FF" + }, "bandPlan": "General", "bandPlanEnabled": true, "centerTuning": false, "fftHeight": 300, - "frequency": 7350000, - "max": -25.0, - "maximized": true, + "frequency": 105000000, + "max": 0.0, + "maximized": false, "menuOrder": [ "Source", "Radio", @@ -17,7 +24,7 @@ "Display" ], "menuWidth": 300, - "min": -63.235294342041016, + "min": -70.0, "moduleInstances": { "AirspyHF+ Source": "airspyhf_source", "Audio Sink": "audio_sink", @@ -25,7 +32,8 @@ "RTL-TCP Source": "rtl_tcp_source", "Radio": "radio", "Recorder": "recorder", - "SoapySDR Source": "soapy_source" + "SoapySDR Source": "soapy_source", + "SpyServer Source": "spyserver_source" }, "modules": [ "./radio/Release/radio.dll", @@ -34,26 +42,17 @@ "./airspyhf_source/Release/airspyhf_source.dll", "./rtl_tcp_source/Release/rtl_tcp_source.dll", "./audio_sink/Release/audio_sink.dll", - "./plutosdr_source/Release/plutosdr_source.dll" + "./plutosdr_source/Release/plutosdr_source.dll", + "./spyserver_source/Release/spyserver_source.dll" ], "offset": 0.0, "showWaterfall": true, - "source": "Airspy HF+", + "source": "SoapySDR", "streams": { "Radio": { "muted": false, "sink": "Audio", - "volume": 0.13265305757522583 - }, - "Radio 1": { - "muted": false, - "sink": "Audio", - "volume": 0.6377550959587097 - }, - "Radio 2": { - "muted": false, - "sink": "Audio", - "volume": 0.4292035400867462 + "volume": 0.5867347121238708 } }, "windowSize": { diff --git a/root_dev/plutosdr_source_config.json b/root_dev/plutosdr_source_config.json index cf337e35..697ea531 100644 --- a/root_dev/plutosdr_source_config.json +++ b/root_dev/plutosdr_source_config.json @@ -1,6 +1,6 @@ { "IP": "192.168.2.1", "gain": 0.0, - "gainMode": 2, + "gainMode": 0, "sampleRate": 4000000.0 } \ No newline at end of file diff --git a/root_dev/radio_config.json b/root_dev/radio_config.json index a91fcc6b..4f09e0fd 100644 --- a/root_dev/radio_config.json +++ b/root_dev/radio_config.json @@ -1,44 +1,5 @@ { "Radio": { - "AM": { - "bandwidth": 12500.0, - "snapInterval": 1000.0, - "squelchLevel": -100.0 - }, - "CW": { - "bandwidth": 200.0, - "snapInterval": 10.0, - "squelchLevel": -100.0 - }, - "DSB": { - "bandwidth": 6000.0, - "snapInterval": 100.0 - }, - "FM": { - "bandwidth": 12500.0, - "snapInterval": 10000.0, - "squelchLevel": -47.474998474121094 - }, - "LSB": { - "bandwidth": 3000.0, - "snapInterval": 100.0 - }, - "RAW": { - "snapInterval": 10000.0 - }, - "USB": { - "bandwidth": 3000.0, - "snapInterval": 100.0 - }, - "WFM": { - "bandwidth": 200000.0, - "deempMode": 0, - "snapInterval": 100000.0, - "squelchLevel": -100.0 - }, - "selectedDemodId": 2 - }, - "Radio 1": { "AM": { "bandwidth": 12500.0, "snapInterval": 1000.0 @@ -70,43 +31,7 @@ "bandwidth": 200000.0, "deempMode": 0, "snapInterval": 100000.0, - "squelchLevel": -100.0 - }, - "selectedDemodId": 1 - }, - "Radio 2": { - "AM": { - "bandwidth": 12500.0, - "snapInterval": 1000.0 - }, - "CW": { - "bandwidth": 200.0, - "snapInterval": 10.0 - }, - "DSB": { - "bandwidth": 6000.0, - "snapInterval": 100.0 - }, - "FM": { - "bandwidth": 12500.0, - "snapInterval": 10000.0 - }, - "LSB": { - "bandwidth": 3000.0, - "snapInterval": 100.0 - }, - "RAW": { - "snapInterval": 10000.0 - }, - "USB": { - "bandwidth": 3000.0, - "snapInterval": 100.0 - }, - "WFM": { - "bandwidth": 200000.0, - "deempMode": 1, - "snapInterval": 100000.0, - "squelchLevel": -70.2020034790039 + "squelchLevel": -60.60599899291992 }, "selectedDemodId": 1 } diff --git a/root_dev/soapy_source_config.json b/root_dev/soapy_source_config.json index 8e7495e4..01baa098 100644 --- a/root_dev/soapy_source_config.json +++ b/root_dev/soapy_source_config.json @@ -1,49 +1,12 @@ { - "device": "AirSpy HF+ [c852435de0224af7]", + "device": "Generic RTL2832U OEM :: 00000001", "devices": { - "": { + "Generic RTL2832U OEM :: 00000001": { "agc": true, "gains": { - "PGA": 0.0 + "TUNER": 0.0 }, - "sampleRate": 4000000.0 - }, - "AirSpy HF+ [c852435de0224af7]": { - "agc": false, - "gains": { - "LNA": 6.0, - "RF": -48.0 - }, - "sampleRate": 768000.0 - }, - "CABLE Output (VB-Audio Virtual Cable)": { - "sampleRate": 96000.0 - }, - "Default Device": { - "agc": false, - "sampleRate": 192000.0 - }, - "Generic RTL2832U OEM :: 00000001": { - "agc": false, - "gains": { - "TUNER": 37.3390007019043 - }, - "sampleRate": 250000.0 - }, - "HackRF One #0 901868dc282c8f8b": { - "gains": { - "AMP": 0.0, - "LNA": 24.503000259399414, - "VGA": 16.332000732421875 - }, - "sampleRate": 8000000.0 - }, - "Microphone (Realtek High Definition Audio)": { - "agc": false, - "sampleRate": 44100.0 - }, - "PulseAudio": { - "sampleRate": 96000.0 + "sampleRate": 2560000.0 } } } \ No newline at end of file diff --git a/sdrpp_0.2.5_beta_airspyhf/band_colors.json b/sdrpp_0.2.5_beta_airspyhf/band_colors.json new file mode 100644 index 00000000..2e9d8a95 --- /dev/null +++ b/sdrpp_0.2.5_beta_airspyhf/band_colors.json @@ -0,0 +1,7 @@ +{ + "broadcast": "#0000FFFF", + "amateur": "#FF0000FF", + "aviation": "#00FF00FF", + "marine": "#00FFFFFF", + "military": "#FFFF00FF" +} \ No newline at end of file diff --git a/sdrpp_0.2.5_beta_airspyhf/bandplans/general.json b/sdrpp_0.2.5_beta_airspyhf/bandplans/general.json new file mode 100644 index 00000000..600c8eef --- /dev/null +++ b/sdrpp_0.2.5_beta_airspyhf/bandplans/general.json @@ -0,0 +1,267 @@ +{ + "name": "General", + "country_name": "Worldwide", + "country_code": "--", + "author_name": "Ryzerth", + "author_url": "https://github.com/AlexandreRouma", + "bands": [ + { + "name": "Long Wave", + "type": "broadcast", + "start": 148500, + "end": 283500 + }, + { + "name": "Medium Wave", + "type": "broadcast", + "start": 526500, + "end": 1606500 + }, + { + "name": "Shortwave Broadcast", + "type": "broadcast", + "start": 2300000, + "end": 2468000 + }, + { + "name": "Shortwave Broadcast", + "type": "broadcast", + "start": 3200000, + "end": 3400000 + }, + { + "name": "Shortwave Broadcast", + "type": "broadcast", + "start": 3950000, + "end": 4000000 + }, + { + "name": "Shortwave Broadcast", + "type": "broadcast", + "start": 4750000, + "end": 4995000 + }, + { + "name": "Shortwave Broadcast", + "type": "broadcast", + "start": 5005000, + "end": 5060000 + }, + { + "name": "Shortwave Broadcast", + "type": "broadcast", + "start": 5900000, + "end": 6200000 + }, + { + "name": "Shortwave Broadcast", + "type": "broadcast", + "start": 7200000, + "end": 7450000 + }, + { + "name": "Shortwave Broadcast", + "type": "broadcast", + "start": 9400000, + "end": 9900000 + }, + { + "name": "Shortwave Broadcast", + "type": "broadcast", + "start": 11600000, + "end": 12100000 + }, + { + "name": "Shortwave Broadcast", + "type": "broadcast", + "start": 13570000, + "end": 13870000 + }, + { + "name": "Shortwave Broadcast", + "type": "broadcast", + "start": 15100000, + "end": 15800000 + }, + { + "name": "Shortwave Broadcast", + "type": "broadcast", + "start": 17480000, + "end": 17900000 + }, + { + "name": "Shortwave Broadcast", + "type": "broadcast", + "start": 18900000, + "end": 19020000 + }, + { + "name": "Shortwave Broadcast", + "type": "broadcast", + "start": 21450000, + "end": 21850000 + }, + { + "name": "Shortwave Broadcast", + "type": "broadcast", + "start": 25670000, + "end": 26100000 + }, + { + "name": "FM Broadcast", + "type": "broadcast", + "start": 87500000, + "end": 108000000 + }, + { + "name": "Air Band VOR/ILS", + "type": "aviation", + "start": 108000000, + "end": 118000000 + }, + { + "name": "Air Band Voice", + "type": "aviation", + "start": 118000000, + "end": 137000000 + }, + { + "name": "160m Ham Band", + "type": "amateur", + "start": 1800000, + "end": 2000000 + }, + { + "name": "80m Ham Band", + "type": "amateur", + "start": 3500000, + "end": 3950000 + }, + { + "name": "60m Ham Band", + "type": "amateur", + "start": 5351500, + "end": 5366500 + }, + { + "name": "40m Ham Band", + "type": "amateur", + "start": 7000000, + "end": 7200000 + }, + { + "name": "30m Ham Band", + "type": "amateur", + "start": 10100000, + "end": 10150000 + }, + { + "name": "20m Ham Band", + "type": "amateur", + "start": 14000000, + "end": 14350000 + }, + { + "name": "17m Ham Band", + "type": "amateur", + "start": 18068000, + "end": 18168000 + }, + { + "name": "15m Ham Band", + "type": "amateur", + "start": 21000000, + "end": 21450000 + }, + { + "name": "12m Ham Band", + "type": "amateur", + "start": 24890000, + "end": 24990000 + }, + { + "name": "CB", + "type": "amateur", + "start": 26960000, + "end": 27410000 + }, + { + "name": "10m Ham Band", + "type": "amateur", + "start": 28000000, + "end": 29750000 + }, + { + "name": "6m Ham Band", + "type": "amateur", + "start": 50000000, + "end": 54000000 + }, + { + "name": "2m Ham Band", + "type": "amateur", + "start": 144000000, + "end": 148000000 + }, + { + "name": "Marine", + "type": "marine", + "start": 156000000, + "end": 162025000 + }, + { + "name": "1.25m Ham Band", + "type": "amateur", + "start": 222000000, + "end": 225000000 + }, + { + "name": "Military Air", + "type": "military", + "start": 225000000, + "end": 380000000 + }, + { + "name": "Military Sat", + "type": "military", + "start": 240000000, + "end": 270000000 + }, + { + "name": "70cm Ham Band", + "type": "amateur", + "start": 420000000, + "end": 450000000 + }, + { + "name": "PMR446", + "type": "amateur", + "start": 446000000, + "end": 446200000 + }, + { + "name": "33cm Ham Band", + "type": "amateur", + "start": 902000000, + "end": 928000000 + }, + { + "name": "23cm Ham Band", + "type": "amateur", + "start": 1240000000, + "end": 1300000000 + }, + { + "name": "13cm Ham Band", + "type": "amateur", + "start": 2300000000, + "end": 2310000000 + }, + { + "name": "13cm Ham Band", + "type": "amateur", + "start": 2390000000, + "end": 2450000000 + } + ] +} \ No newline at end of file diff --git a/sdrpp_0.2.5_beta_airspyhf/bandplans/germany.json b/sdrpp_0.2.5_beta_airspyhf/bandplans/germany.json new file mode 100644 index 00000000..c9568bf8 --- /dev/null +++ b/sdrpp_0.2.5_beta_airspyhf/bandplans/germany.json @@ -0,0 +1,141 @@ +{ + "name": "Germany", + "country_name": "Germany", + "country_code": "DE", + "author_name": "Tobias Mädel", + "author_url": "https://tbspace.de", + "bands": [ + { + "name": "LW", + "type": "amateur", + "start": 135700, + "end": 137800 + }, + { + "name": "630m", + "type": "amateur", + "start": 472000, + "end": 479000 + }, + { + "name": "160m", + "type": "amateur", + "start": 1810000, + "end": 2000000 + }, + { + "name": "80m", + "type": "amateur", + "start": 3500000, + "end": 3800000 + }, + { + "name": "60m", + "type": "amateur", + "start": 5351500, + "end": 5366500 + }, + { + "name": "40m", + "type": "amateur", + "start": 7000000, + "end": 7200000 + }, + { + "name": "30m", + "type": "amateur", + "start": 10100000, + "end": 10150000 + }, + { + "name": "20m", + "type": "amateur", + "start": 14000000, + "end": 14350000 + }, + { + "name": "17m", + "type": "amateur", + "start": 18068000, + "end": 18168000 + }, + { + "name": "15m", + "type": "amateur", + "start": 21000000, + "end": 21450000 + }, + { + "name": "12m", + "type": "amateur", + "start": 24890000, + "end": 24990000 + }, + { + "name": "CB", + "type": "other", + "start": 26565000, + "end": 27405000 + }, + { + "name": "10m", + "type": "amateur", + "start": 28000000, + "end": 29700000 + }, + { + "name": "6m", + "type": "amateur", + "start": 50030000, + "end": 51000000 + }, + { + "name": "4m", + "type": "amateur", + "start": 70150000, + "end": 70200000 + }, + { + "name": "FM", + "type": "broadcast", + "start": 87500000, + "end": 108000000 + }, + { + "name": "2m", + "type": "amateur", + "start": 144000000, + "end": 146000000 + }, + { + "name": "Freenet", + "type": "other", + "start": 149025000, + "end": 149115625 + }, + { + "name": "70cm", + "type": "amateur", + "start": 430000000, + "end": 440000000 + }, + { + "name": "PMR446", + "type": "other", + "start": 446006250, + "end": 446196875 + }, + { + "name": "23cm", + "type": "amateur", + "start": 1240000000, + "end": 1300000000 + }, + { + "name": "13cm", + "type": "amateur", + "start": 2320000000, + "end": 2450000000 + } + ] +} \ No newline at end of file diff --git a/sdrpp_0.2.5_beta_airspyhf/bandplans/russia.json b/sdrpp_0.2.5_beta_airspyhf/bandplans/russia.json new file mode 100644 index 00000000..3ebcb11c --- /dev/null +++ b/sdrpp_0.2.5_beta_airspyhf/bandplans/russia.json @@ -0,0 +1,135 @@ +{ + "name": "Russia", + "country_name": "Russia", + "country_code": "RU", + "author_name": "Raov", + "author_url": "https://twitter.com/raov_birbtog", + "bands": [ + { + "name": "160m CW", + "type": "amateur", + "start": 1810000, + "end": 1838000 + }, + { + "name": "160m NB", + "type": "amateur", + "start": 1838000, + "end": 1840000 + }, + { + "name": "160m + Digi", + "type": "amateur", + "start": 1840000, + "end": 1843000 + }, + { + "name": "160m", + "type": "amateur", + "start": 1843000, + "end": 2000000 + }, + { + "name": "80m CW", + "type": "amateur", + "start": 3500000, + "end": 3510000 + }, + { + "name": "80m CW Contest", + "type": "amateur", + "start": 3510000, + "end": 3560000 + }, + { + "name": "80m CW", + "type": "amateur", + "start": 3560000, + "end": 3570000 + }, + { + "name": "80m NB, Digi 200Hz", + "type": "amateur", + "start": 3570000, + "end": 3580000 + }, + { + "name": "80m NB, Digi 500Hz", + "type": "amateur", + "start": 3580000, + "end": 3600000 + }, + { + "name": "80m SSB Contest", + "type": "amateur", + "start": 3600000, + "end": 3650000 + }, + { + "name": "80m", + "type": "amateur", + "start": 3650000, + "end": 3700000 + }, + { + "name": "80m SSB Contest", + "type": "amateur", + "start": 3700000, + "end": 3800000 + }, + { + "name": "60m CW 200Hz", + "type": "amateur", + "start": 5351500, + "end": 5354000 + }, + { + "name": "60m USB", + "type": "amateur", + "start": 5354000, + "end": 5366000 + }, + { + "name": "60m CW 20Hz", + "type": "amateur", + "start": 5356000, + "end": 5366500 + }, + { + "name": "40m CW", + "type": "amateur", + "start": 7000000, + "end": 7040000 + }, + { + "name": "40m NB, Digi 500Hz", + "type": "amateur", + "start": 7040000, + "end": 7050000 + }, + { + "name": "40m", + "type": "amateur", + "start": 7050000, + "end": 7060000 + }, + { + "name": "40m SSB Contest", + "type": "amateur", + "start": 7060000, + "end": 7100000 + }, + { + "name": "40m", + "type": "amateur", + "start": 7100000, + "end": 7130000 + }, + { + "name": "40m SSB Contest", + "type": "amateur", + "start": 7130000, + "end": 7200000 + } + ] +} \ No newline at end of file diff --git a/sdrpp_0.2.5_beta_airspyhf/config.json b/sdrpp_0.2.5_beta_airspyhf/config.json new file mode 100644 index 00000000..30c71725 --- /dev/null +++ b/sdrpp_0.2.5_beta_airspyhf/config.json @@ -0,0 +1,46 @@ +{ + "bandColors": { + "amateur": "#FF0000FF", + "aviation": "#00FF00FF", + "broadcast": "#0000FFFF", + "marine": "#00FFFFFF", + "military": "#FFFF00FF" + }, + "bandPlan": "General", + "bandPlanEnabled": true, + "centerTuning": false, + "fftHeight": 300, + "frequency": 100004000, + "max": 0.0, + "maximized": false, + "menuOrder": [ + "Source", + "Radio", + "Recorder", + "Sinks", + "Audio", + "Scripting", + "Band Plan", + "Display" + ], + "menuWidth": 300, + "min": -70.0, + "moduleInstances": { + "AirspyHF+ Source": "airspyhf_source", + "Audio Sink": "audio_sink", + "PlutoSDR Source": "plutosdr_source", + "RTL-TCP Source": "rtl_tcp_source", + "Radio": "radio", + "Recorder": "recorder", + "SoapySDR Source": "soapy_source" + }, + "modules": [], + "offset": 0.0, + "showWaterfall": true, + "source": "", + "streams": {}, + "windowSize": { + "h": 720, + "w": 1280 + } +} \ No newline at end of file diff --git a/sdrpp_0.2.5_beta_airspyhf/modules/.gitkeep b/sdrpp_0.2.5_beta_airspyhf/modules/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/sdrpp_0.2.5_beta_airspyhf/recordings/.gitkeep b/sdrpp_0.2.5_beta_airspyhf/recordings/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/sdrpp_0.2.5_beta_airspyhf/res/fonts/Roboto-Medium.ttf b/sdrpp_0.2.5_beta_airspyhf/res/fonts/Roboto-Medium.ttf new file mode 100644 index 00000000..39c63d74 Binary files /dev/null and b/sdrpp_0.2.5_beta_airspyhf/res/fonts/Roboto-Medium.ttf differ diff --git a/sdrpp_0.2.5_beta_airspyhf/res/icons/center_tuning.png b/sdrpp_0.2.5_beta_airspyhf/res/icons/center_tuning.png new file mode 100644 index 00000000..00bf8482 Binary files /dev/null and b/sdrpp_0.2.5_beta_airspyhf/res/icons/center_tuning.png differ diff --git a/sdrpp_0.2.5_beta_airspyhf/res/icons/menu.png b/sdrpp_0.2.5_beta_airspyhf/res/icons/menu.png new file mode 100644 index 00000000..b830b5b5 Binary files /dev/null and b/sdrpp_0.2.5_beta_airspyhf/res/icons/menu.png differ diff --git a/sdrpp_0.2.5_beta_airspyhf/res/icons/muted.png b/sdrpp_0.2.5_beta_airspyhf/res/icons/muted.png new file mode 100644 index 00000000..e7a1ad55 Binary files /dev/null and b/sdrpp_0.2.5_beta_airspyhf/res/icons/muted.png differ diff --git a/sdrpp_0.2.5_beta_airspyhf/res/icons/normal_tuning.png b/sdrpp_0.2.5_beta_airspyhf/res/icons/normal_tuning.png new file mode 100644 index 00000000..76d171e2 Binary files /dev/null and b/sdrpp_0.2.5_beta_airspyhf/res/icons/normal_tuning.png differ diff --git a/sdrpp_0.2.5_beta_airspyhf/res/icons/play.png b/sdrpp_0.2.5_beta_airspyhf/res/icons/play.png new file mode 100644 index 00000000..a5bc44df Binary files /dev/null and b/sdrpp_0.2.5_beta_airspyhf/res/icons/play.png differ diff --git a/sdrpp_0.2.5_beta_airspyhf/res/icons/sdrpp.ico b/sdrpp_0.2.5_beta_airspyhf/res/icons/sdrpp.ico new file mode 100644 index 00000000..9ef26c2c Binary files /dev/null and b/sdrpp_0.2.5_beta_airspyhf/res/icons/sdrpp.ico differ diff --git a/sdrpp_0.2.5_beta_airspyhf/res/icons/sdrpp.png b/sdrpp_0.2.5_beta_airspyhf/res/icons/sdrpp.png new file mode 100644 index 00000000..03cf6183 Binary files /dev/null and b/sdrpp_0.2.5_beta_airspyhf/res/icons/sdrpp.png differ diff --git a/sdrpp_0.2.5_beta_airspyhf/res/icons/stop.png b/sdrpp_0.2.5_beta_airspyhf/res/icons/stop.png new file mode 100644 index 00000000..6fac0cbc Binary files /dev/null and b/sdrpp_0.2.5_beta_airspyhf/res/icons/stop.png differ diff --git a/sdrpp_0.2.5_beta_airspyhf/res/icons/unmuted.png b/sdrpp_0.2.5_beta_airspyhf/res/icons/unmuted.png new file mode 100644 index 00000000..64fcb987 Binary files /dev/null and b/sdrpp_0.2.5_beta_airspyhf/res/icons/unmuted.png differ diff --git a/soapy_source/src/main.cpp b/soapy_source/src/main.cpp index 4d117360..84331c2a 100644 --- a/soapy_source/src/main.cpp +++ b/soapy_source/src/main.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -386,7 +387,7 @@ private: }; MOD_EXPORT void _INIT_() { - config.setPath(ROOT_DIR "/soapy_source_config.json"); + config.setPath(options::opts.root + "/soapy_source_config.json"); json defConf; defConf["device"] = ""; defConf["devices"] = json({}); diff --git a/spyserver_source/CMakeLists.txt b/spyserver_source/CMakeLists.txt new file mode 100644 index 00000000..48a43217 --- /dev/null +++ b/spyserver_source/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.13) +project(spyserver_source) + +if (MSVC) + set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc") +else() + set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive") +endif (MSVC) + +include_directories("src/") + +file(GLOB SRC "src/*.cpp") + +add_library(spyserver_source SHARED ${SRC}) +target_link_libraries(spyserver_source PRIVATE sdrpp_core) +set_target_properties(spyserver_source PROPERTIES PREFIX "") + +if(WIN32) + target_link_libraries(spyserver_source PRIVATE wsock32 ws2_32) +endif() \ No newline at end of file diff --git a/spyserver_source/src/main.cpp b/spyserver_source/src/main.cpp new file mode 100644 index 00000000..64c82a9a --- /dev/null +++ b/spyserver_source/src/main.cpp @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define CONCAT(a, b) ((std::string(a) + b).c_str()) + +SDRPP_MOD_INFO { + /* Name: */ "spyserver_source", + /* Description: */ "SpyServer source module for SDR++", + /* Author: */ "Ryzerth", + /* Version: */ 0, 1, 0, + /* Max instances */ 1 +}; + +class SpyServerSourceModule : public ModuleManager::Instance { +public: + SpyServerSourceModule(std::string name) { + this->name = name; + + sampleRate = 2560000.0; + + handler.ctx = this; + handler.selectHandler = menuSelected; + handler.deselectHandler = menuDeselected; + handler.menuHandler = menuHandler; + handler.startHandler = start; + handler.stopHandler = stop; + handler.tuneHandler = tune; + handler.stream = &client.iqStream; + sigpath::sourceManager.registerSource("SpyServer", &handler); + } + + ~SpyServerSourceModule() { + + } + + void enable() { + enabled = true; + } + + void disable() { + enabled = false; + } + + bool isEnabled() { + return enabled; + } + +private: + static void menuSelected(void* ctx) { + SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; + core::setInputSampleRate(_this->sampleRate); + spdlog::info("SpyServerSourceModule '{0}': Menu Select!", _this->name); + } + + static void menuDeselected(void* ctx) { + SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; + spdlog::info("SpyServerSourceModule '{0}': Menu Deselect!", _this->name); + } + + static void start(void* ctx) { + SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; + if (_this->running) { + return; + } + if (!_this->client.connectToSpyserver(_this->ip, _this->port)) { + spdlog::error("Could not connect to {0}:{1}", _this->ip, _this->port); + return; + } + _this->client.tune(_this->freq); + _this->client.setSampleRate(_this->sampleRate); + //_this->client.setGainIndex(_this->gain); + //_this->client.setGainMode(!_this->tunerAGC); + //_this->client.setDirectSampling(_this->directSamplingMode); + //_this->client.setAGCMode(_this->rtlAGC); + _this->running = true; + spdlog::info("SpyServerSourceModule '{0}': Start!", _this->name); + } + + static void stop(void* ctx) { + SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; + if (!_this->running) { + return; + } + _this->running = false; + _this->client.disconnect(); + spdlog::info("SpyServerSourceModule '{0}': Stop!", _this->name); + } + + static void tune(double freq, void* ctx) { + SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; + if (_this->running) { + _this->client.tune(freq); + } + _this->freq = freq; + spdlog::info("SpyServerSourceModule '{0}': Tune: {1}!", _this->name, freq); + } + + static void menuHandler(void* ctx) { + SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; + float menuWidth = ImGui::GetContentRegionAvailWidth(); + float portWidth = ImGui::CalcTextSize("00000").x + 20; + + ImGui::SetNextItemWidth(menuWidth - portWidth); + ImGui::InputText(CONCAT("##_ip_select_", _this->name), _this->ip, 1024); + ImGui::SameLine(); + ImGui::SetNextItemWidth(portWidth); + ImGui::InputInt(CONCAT("##_port_select_", _this->name), &_this->port, 0); + + ImGui::SetNextItemWidth(ImGui::CalcTextSize("OOOOOOOOOO").x); + if (ImGui::Combo("Direct sampling", &_this->directSamplingMode, "Disabled\0I branch\0Q branch\0")) { + if (_this->running) { + //_this->client.setDirectSampling(_this->directSamplingMode); + } + } + + if (ImGui::Checkbox("RTL AGC", &_this->rtlAGC)) { + if (_this->running) { + //_this->client.setAGCMode(_this->rtlAGC); + } + } + + if (ImGui::Checkbox("Tuner AGC", &_this->tunerAGC)) { + if (_this->running) { + //_this->client.setGainMode(!_this->tunerAGC); + if (!_this->tunerAGC) { + //_this->client.setGainIndex(_this->gain); + } + } + } + + if (_this->tunerAGC) { style::beginDisabled(); } + ImGui::SetNextItemWidth(menuWidth); + if (ImGui::SliderInt(CONCAT("##_gain_select_", _this->name), &_this->gain, 0, 29, "")) { + if (_this->running) { + //_this->client.setGainIndex(_this->gain); + } + } + if (_this->tunerAGC) { style::endDisabled(); } + } + + std::string name; + bool enabled = true; + dsp::stream stream; + double sampleRate; + SourceManager::SourceHandler handler; + std::thread workerThread; + SpyServerClient client; + bool running = false; + double freq; + char ip[1024] = "localhost"; + int port = 5555; + int gain = 0; + bool rtlAGC = false; + bool tunerAGC = false; + int directSamplingMode = 0; +}; + +MOD_EXPORT void _INIT_() { + // Do your one time init here +} + +MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { + return new SpyServerSourceModule(name); +} + +MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { + delete (SpyServerSourceModule*)instance; +} + +MOD_EXPORT void _END_() { + // Do your one shutdown here +} \ No newline at end of file diff --git a/spyserver_source/src/spyserver_client.cpp b/spyserver_source/src/spyserver_client.cpp new file mode 100644 index 00000000..504af7da --- /dev/null +++ b/spyserver_source/src/spyserver_client.cpp @@ -0,0 +1,254 @@ +#include +#include + +SpyServerClient::SpyServerClient() { + +} + +bool SpyServerClient::connectToSpyserver(char* host, int port) { + if (connected) { + return true; + } + +#ifdef _WIN32 + struct addrinfo *result = NULL; + struct addrinfo *ptr = NULL; + struct addrinfo hints; + + ZeroMemory( &hints, sizeof(hints) ); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + char buf[128]; + sprintf(buf, "%hu", port); + + int iResult = getaddrinfo(host, buf, &hints, &result); + if (iResult != 0) { + // TODO: log error + printf("A"); + WSACleanup(); + return false; + } + ptr = result; + + sock = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); + + if (sock == INVALID_SOCKET) { + // TODO: log error + printf("B"); + freeaddrinfo(result); + WSACleanup(); + return false; + } + + iResult = connect(sock, ptr->ai_addr, (int)ptr->ai_addrlen); + if (iResult == SOCKET_ERROR) { + printf("C"); + closesocket(sock); + freeaddrinfo(result); + WSACleanup(); + return false; + } + freeaddrinfo(result); +#else + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + // TODO: Log error + return false; + } + struct hostent *server = gethostbyname(host); + struct sockaddr_in serv_addr; + bzero(&serv_addr, sizeof(struct sockaddr_in)); + serv_addr.sin_family = AF_INET; + bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length); + serv_addr.sin_port = htons(port); + if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0) { + // TODO: log error + return false; + } +#endif + + // Switch to non-blocking mode +#ifdef _WIN32 + unsigned long mode = 1; + ioctlsocket(sock, FIONBIO, &mode); +#else + fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK) +#endif + + connected = true; + waiting = true; + + workerThread = std::thread(&SpyServerClient::worker, this); + + printf("Connected"); + + return true; +} + +bool SpyServerClient::disconnect() { + if (!connected) { + return false; + } + waiting = false; + if (workerThread.joinable()) { + workerThread.join(); + } +#ifdef _WIN32 + closesocket(sock); + WSACleanup(); +#else + close(sockfd); +#endif + connected = false; + return true; +} + +void SpyServerClient::setSampleRate(uint32_t setSampleRate) { + +} + +void SpyServerClient::tune(uint32_t freq) { + +} + +int SpyServerClient::receive(char* buf, int count) { +#ifdef _WIN32 + return checkError(recv(sock, (char*)buf, count, 0), count); +#else + return checkError(read(sockfd, buf, count)); +#endif +} + +int SpyServerClient::checkError(int len, int expected) { +#ifdef _WIN32 + if (len != SOCKET_ERROR) { return len; } + int code = WSAGetLastError(); + if (code == WSAEWOULDBLOCK) { return 0; } + spdlog::error("{0}", code); + return -1; +#else + if (len <= expected) { + return len; + } + if (len == EAGAIN || len == EWOULDBLOCK) { return 0; } + return -1; +#endif +} + +int SpyServerClient::receiveSync(char* buf, int count) { + int len = receive(buf, count); + while (len == 0 && waiting) { + len = receive(buf, count); + } + if (!waiting) { + return 0; + } + return len; +} + +void SpyServerClient::worker() { + // Allocate dummy buffer + char* dummyBuf = (char*)malloc(1000000); + + // Send hello + hello(); + + // SETTING_STREAMING_MODE = 0, + // SETTING_STREAMING_ENABLED = 1, + // SETTING_GAIN = 2, + + // SETTING_IQ_FORMAT = 100, // 0x64 + // SETTING_IQ_FREQUENCY = 101, // 0x65 + // SETTING_IQ_DECIMATION = 102, // 0x66 + // SETTING_IQ_DIGITAL_GAIN = 103, // 0x67 + + // Set settings + setSetting(SETTING_STREAMING_MODE, STREAM_MODE_IQ_ONLY); + setSetting(SETTING_GAIN, 5); + setSetting(SETTING_IQ_FORMAT, STREAM_FORMAT_FLOAT); + setSetting(SETTING_IQ_FREQUENCY, 2000000); + setSetting(SETTING_IQ_DECIMATION, 1); + setSetting(SETTING_IQ_DIGITAL_GAIN, 1); + + // Enable stream + setSetting(SETTING_STREAMING_ENABLED, 1); + + // Main message receive loop + while (true) { + MessageHeader msgHeader; + int len = receiveSync((char*)&msgHeader, sizeof(MessageHeader)); + if (len < 0) { + spdlog::error("Socket error"); + return; + } + if (len == 0) { return; } + + int type = (msgHeader.MessageType & 0xFFFF); + + if (type == MSG_TYPE_DEVICE_INFO) { + DeviceInfo devInfo; + len = receiveSync((char*)&devInfo, sizeof(DeviceInfo)); + if (len < 0) { + spdlog::error("A Socket error"); + return; + } + if (len == 0) { return; } + + spdlog::warn("Dev type: {0}", devInfo.DeviceType); + } + // else if (type == MSG_TYPE_FLOAT_IQ) { + // //if (iqStream.aquire() < 0) { return; } + // len = receiveSync(dummyBuf, msgHeader.BodySize); + // //iqStream.write(msgHeader.BodySize); + // if (len < 0) { + // spdlog::error("T Socket error"); + // return; + // } + // if (len == 0) { return; } + // } + else if (msgHeader.BodySize > 0) { + len = receiveSync(dummyBuf, msgHeader.BodySize); + if (len < 0) { + spdlog::error("B Socket error {0}", msgHeader.ProtocolID); + return; + } + if (len == 0) { return; } + } + } + + free(dummyBuf); +} + +void SpyServerClient::sendCommand(uint32_t cmd, void* body, size_t bodySize) { + int size = sizeof(CommandHeader) + bodySize; + char* buf = new char[size]; + CommandHeader* cmdHdr = (CommandHeader*)buf; + memcpy(&buf[sizeof(CommandHeader)], body, bodySize); + cmdHdr->CommandType = cmd; + cmdHdr->BodySize = bodySize; +#ifdef _WIN32 + send(sock, buf, size, 0); +#else + write(sockfd, buf, size); +#endif + delete[] buf; +} + +void SpyServerClient::setSetting(uint32_t setting, uint32_t value) { + char buf[sizeof(SettingTarget) + sizeof(uint32_t)]; + SettingTarget* tgt = (SettingTarget*)buf; + uint32_t* val = (uint32_t*)&buf[sizeof(SettingTarget)]; + tgt->SettingType = setting; + *val = value; + sendCommand(CMD_SET_SETTING, buf, sizeof(SettingTarget) + sizeof(uint32_t)); +} + +void SpyServerClient::hello() { + char buf[1024]; + ClientHandshake* handshake = (ClientHandshake*)buf; + handshake->ProtocolVersion = SPYSERVER_PROTOCOL_VERSION; + strcpy(&buf[sizeof(ClientHandshake)], "sdr++"); + sendCommand(CMD_HELLO, buf, sizeof(ClientHandshake) + 5); +} \ No newline at end of file diff --git a/spyserver_source/src/spyserver_client.h b/spyserver_source/src/spyserver_client.h new file mode 100644 index 00000000..23f7fde6 --- /dev/null +++ b/spyserver_source/src/spyserver_client.h @@ -0,0 +1,69 @@ +#pragma once +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif + +#ifdef _WIN32 +#define __attribute__(x) +#pragma pack(push, 1) +#endif +struct command_t{ + unsigned char cmd; + unsigned int param; +}__attribute__((packed)); +#ifdef _WIN32 +#pragma pack(pop) +#endif + +class SpyServerClient { +public: + SpyServerClient(); + + bool connectToSpyserver(char* host, int port); + bool disconnect(); + + void start(); + void stop(); + + void setSampleRate(uint32_t setSampleRate); + + void tune(uint32_t freq); + + dsp::stream iqStream; + +private: + int receive(char* buf, int count); + int receiveSync(char* buf, int count); + int checkError(int err, int expected); + void worker(); + + void sendCommand(uint32_t cmd, void* body, size_t bodySize); + void setSetting(uint32_t setting, uint32_t value); + + void hello(); + +#ifdef _WIN32 + SOCKET sock; +#else + int sockfd; +#endif + bool connected = false; + bool waiting = false; + + std::thread workerThread; + +}; \ No newline at end of file diff --git a/spyserver_source/src/spyserver_protocol.h b/spyserver_source/src/spyserver_protocol.h new file mode 100644 index 00000000..de9ca6cb --- /dev/null +++ b/spyserver_source/src/spyserver_protocol.h @@ -0,0 +1,188 @@ +/* +SPY Server protocol structures and constants +Copyright (C) 2017 Youssef Touil youssef@live.com +*/ + + +#pragma once + +#include +#include + +#define SPYSERVER_PROTOCOL_VERSION (((2) << 24) | ((0) << 16) | (1700)) + +#define SPYSERVER_MAX_COMMAND_BODY_SIZE (256) +#define SPYSERVER_MAX_MESSAGE_BODY_SIZE (1 << 20) +#define SPYSERVER_MAX_DISPLAY_PIXELS (1 << 15) +#define SPYSERVER_MIN_DISPLAY_PIXELS (100) +#define SPYSERVER_MAX_FFT_DB_RANGE (150) +#define SPYSERVER_MIN_FFT_DB_RANGE (10) +#define SPYSERVER_MAX_FFT_DB_OFFSET (100) + +enum DeviceType +{ + DEVICE_INVALID = 0, + DEVICE_AIRSPY_ONE = 1, + DEVICE_AIRSPY_HF = 2, + DEVICE_RTLSDR = 3, +}; + +enum CommandType +{ + CMD_HELLO = 0, + CMD_GET_SETTING = 1, + CMD_SET_SETTING = 2, + CMD_PING = 3, +}; + +enum SettingType +{ + SETTING_STREAMING_MODE = 0, + SETTING_STREAMING_ENABLED = 1, + SETTING_GAIN = 2, + + SETTING_IQ_FORMAT = 100, // 0x64 + SETTING_IQ_FREQUENCY = 101, // 0x65 + SETTING_IQ_DECIMATION = 102, // 0x66 + SETTING_IQ_DIGITAL_GAIN = 103, // 0x67 + + SETTING_FFT_FORMAT = 200, // 0xc8 + SETTING_FFT_FREQUENCY = 201, // 0xc9 + SETTING_FFT_DECIMATION = 202, // 0xca + SETTING_FFT_DB_OFFSET = 203, // 0xcb + SETTING_FFT_DB_RANGE = 204, // 0xcc + SETTING_FFT_DISPLAY_PIXELS = 205, // 0xcd +}; + +enum StreamType +{ + STREAM_TYPE_STATUS = 0, + STREAM_TYPE_IQ = 1, + STREAM_TYPE_AF = 2, + STREAM_TYPE_FFT = 4, +}; + + +enum StreamingMode +{ + STREAM_MODE_IQ_ONLY = STREAM_TYPE_IQ, // 0x01 + STREAM_MODE_AF_ONLY = STREAM_TYPE_AF, // 0x02 + STREAM_MODE_FFT_ONLY = STREAM_TYPE_FFT, // 0x04 + STREAM_MODE_FFT_IQ = STREAM_TYPE_FFT | STREAM_TYPE_IQ, // 0x05 + STREAM_MODE_FFT_AF = STREAM_TYPE_FFT | STREAM_TYPE_AF, // 0x06 +}; + +enum StreamFormat +{ + STREAM_FORMAT_INVALID = 0, + STREAM_FORMAT_UINT8 = 1, + STREAM_FORMAT_INT16 = 2, + STREAM_FORMAT_INT24 = 3, + STREAM_FORMAT_FLOAT = 4, + STREAM_FORMAT_DINT4 = 5, +}; + +enum MessageType +{ + MSG_TYPE_DEVICE_INFO = 0, + MSG_TYPE_CLIENT_SYNC = 1, + MSG_TYPE_PONG = 2, + MSG_TYPE_READ_SETTING = 3, + + MSG_TYPE_UINT8_IQ = 100, // 0x64 + MSG_TYPE_INT16_IQ = 101, // 0x65 + MSG_TYPE_INT24_IQ = 102, // 0x66 + MSG_TYPE_FLOAT_IQ = 103, // 0x67 + + MSG_TYPE_UINT8_AF = 200, // 0xc8 + MSG_TYPE_INT16_AF = 201, // 0xc9 + MSG_TYPE_INT24_AF = 202, // 0xca + MSG_TYPE_FLOAT_AF = 203, // 0xcb + + MSG_TYPE_DINT4_FFT = 300, //0x12C + MSG_TYPE_UINT8_FFT = 301, //0x12D +}; + +#ifdef _WIN32 +#define __attribute__(x) +#pragma pack(push, 1) +#endif + +struct ClientHandshake +{ + uint32_t ProtocolVersion; + // SDR# doesn't seem to send this + //uint32_t ClientNameLength; +}__attribute__((packed)); + +struct CommandHeader +{ + uint32_t CommandType; + uint32_t BodySize; +}__attribute__((packed)); + +struct SettingTarget +{ + // Again, not used + //uint32_t StreamType; + uint32_t SettingType; +}__attribute__((packed)); + +struct MessageHeader +{ + uint32_t ProtocolID; + uint32_t MessageType; + uint32_t StreamType; + uint32_t SequenceNumber; + uint32_t BodySize; +}__attribute__((packed)); + +struct DeviceInfo +{ + uint32_t DeviceType; + uint32_t DeviceSerial; + uint32_t MaximumSampleRate; + uint32_t MaximumBandwidth; + uint32_t DecimationStageCount; + uint32_t GainStageCount; + uint32_t MaximumGainIndex; + uint32_t MinimumFrequency; + uint32_t MaximumFrequency; + uint32_t Resolution; + uint32_t MinimumIQDecimation; + uint32_t ForcedIQFormat; +}__attribute__((packed)); + +struct ClientSync +{ + uint32_t CanControl; + uint32_t Gain; + uint32_t DeviceCenterFrequency; + uint32_t IQCenterFrequency; + uint32_t FFTCenterFrequency; + uint32_t MinimumIQCenterFrequency; + uint32_t MaximumIQCenterFrequency; + uint32_t MinimumFFTCenterFrequency; + uint32_t MaximumFFTCenterFrequency; +}__attribute__((packed)); + +struct ComplexInt16 +{ + int16_t real; + int16_t imag; +}__attribute__((packed)); + +struct ComplexUInt8 +{ + uint8_t real; + uint8_t imag; +}__attribute__((packed)); + +#ifdef _WIN32 +#pragma pack(pop) +#endif + +enum ParserPhase { + AcquiringHeader, + ReadingData +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 023f73bc..d349eb46 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,6 @@ #include #include -int main() { - return sdrpp_main(); +int main(int argc, char *argv[]) { + return sdrpp_main(argc, argv); } \ No newline at end of file