diff --git a/CMakeLists.txt b/CMakeLists.txt index a01476e8..ad8d0035 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ option(OPT_BUILD_NEW_PORTAUDIO_SINK "Build the new PortAudio Sink Module (Depend # Decoders option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder (Dependencies: ffplay)" OFF) +option(OPT_BUILD_KG_SSTV_DECODER "Build the M17 decoder module (no dependencies required)" OFF) option(OPT_BUILD_M17_DECODER "Build the M17 decoder module (no dependencies required)" OFF) option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON) option(OPT_BUILD_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON) @@ -64,7 +65,7 @@ option(OPT_BUILD_SCANNER "Frequency scanner" OFF) option(OPT_BUILD_SCHEDULER "Build the scheduler" OFF) # Other options -option(USE_INTERNAL_LIBCORRECT "Use an external version of libcorrect" ON) +option(USE_INTERNAL_LIBCORRECT "Use an internal version of libcorrect" ON) option(USE_BUNDLE_DEFAULTS "Set the default resource and module directories to the right ones for a MacOS .app" OFF) # Core of SDR++ @@ -159,6 +160,10 @@ if (OPT_BUILD_FALCON9_DECODER) add_subdirectory("decoder_modules/falcon9_decoder") endif (OPT_BUILD_FALCON9_DECODER) +if (OPT_BUILD_KG_SSTV_DECODER) +add_subdirectory("decoder_modules/kg_sstv_decoder") +endif (OPT_BUILD_KG_SSTV_DECODER) + if (OPT_BUILD_M17_DECODER) add_subdirectory("decoder_modules/m17_decoder") endif (OPT_BUILD_M17_DECODER) diff --git a/android/app/src/main/java/MainActivity.kt b/android/app/src/main/java/MainActivity.kt index 9f81dbde..9fbf5ec4 100644 --- a/android/app/src/main/java/MainActivity.kt +++ b/android/app/src/main/java/MainActivity.kt @@ -124,9 +124,7 @@ class MainActivity : NativeActivity() { // We assume dispatchKeyEvent() of the NativeActivity is actually called for every // KeyEvent and not consumed by any View before it reaches here override fun dispatchKeyEvent(event: KeyEvent): Boolean { - Log.w(TAG, "Key Action"); if (event.action == KeyEvent.ACTION_DOWN) { - Log.w(TAG, "Key Pressed"); unicodeCharacterQueue.offer(event.getUnicodeChar(event.metaState)) } return super.dispatchKeyEvent(event) diff --git a/core/backends/android/backend.cpp b/core/backends/android/backend.cpp index 16ca2479..85527743 100644 --- a/core/backends/android/backend.cpp +++ b/core/backends/android/backend.cpp @@ -16,7 +16,6 @@ #include #include #include -#include // Credit to the ImGui android OpenGL3 example for a lot of this code! @@ -36,9 +35,10 @@ namespace backend { int PollUnicodeChars(); void doPartialInit() { + std::string root = core::args["root"]; backend::init(); - style::loadFonts(options::opts.root + "/res"); // TODO: Don't hardcode, use config - icons::load(options::opts.root + "/res"); + style::loadFonts(root + "/res"); // TODO: Don't hardcode, use config + icons::load(root + "/res"); thememenu::applyTheme(); ImGui::GetStyle().ScaleAllSizes(style::uiScale); gui::mainWindow.setFirstMenuRender(); diff --git a/core/src/command_args.h b/core/src/command_args.h index 17998021..cc93423b 100644 --- a/core/src/command_args.h +++ b/core/src/command_args.h @@ -86,6 +86,31 @@ public: return sval; } + bool b() { + if (type != CLI_ARG_TYPE_BOOL && type != CLI_ARG_TYPE_VOID) { throw std::runtime_error("Not a bool"); } + return bval; + } + + int i() { + if (type != CLI_ARG_TYPE_INT) { throw std::runtime_error("Not an int"); } + return ival; + } + + float f() { + if (type != CLI_ARG_TYPE_FLOAT) { throw std::runtime_error("Not a float"); } + return (float)fval; + } + + double d() { + if (type != CLI_ARG_TYPE_FLOAT) { throw std::runtime_error("Not a float"); } + return fval; + } + + const std::string& s() { + if (type != CLI_ARG_TYPE_STRING) { throw std::runtime_error("Not a string"); } + return sval; + } + friend CommandArgsParser; CLIArgType type; diff --git a/core/src/core.cpp b/core/src/core.cpp index 2b46cc26..fc082933 100644 --- a/core/src/core.cpp +++ b/core/src/core.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -41,7 +40,7 @@ namespace core { void setInputSampleRate(double samplerate) { // Forward this to the server - if (options::opts.serverMode) { server::setInputSampleRate(samplerate); return; } + if (args["server"].b()) { server::setInputSampleRate(samplerate); return; } sigpath::signalPath.sourceSampleRate = samplerate; double effectiveSr = samplerate / ((double)(1 << sigpath::signalPath.decimation)); @@ -75,25 +74,25 @@ int sdrpp_main(int argc, char* argv[]) { return 0; } - // Load default options and parse command line - options::loadDefaults(); - if (!options::parse(argc, argv)) { return -1; } + bool serverMode = core::args["server"]; #ifdef _WIN32 - if (!options::opts.showConsole && !options::opts.serverMode) { FreeConsole(); } + if (!core::args["con"].b() && !serverMode) { FreeConsole(); } #endif // Check root directory - if (!std::filesystem::exists(options::opts.root)) { - spdlog::warn("Root directory {0} does not exist, creating it", options::opts.root); - if (!std::filesystem::create_directories(options::opts.root)) { - spdlog::error("Could not create root directory {0}", options::opts.root); + std::string root = core::args["root"]; + if (!std::filesystem::exists(root)) { + spdlog::warn("Root directory {0} does not exist, creating it", root); + if (!std::filesystem::create_directories(root)) { + spdlog::error("Could not create root directory {0}", root); return -1; } } - if (!std::filesystem::is_directory(options::opts.root)) { - spdlog::error("{0} is not a directory", options::opts.root); + // Check that the path actually is a directory + if (!std::filesystem::is_directory(root)) { + spdlog::error("{0} is not a directory", root); return -1; } @@ -225,8 +224,8 @@ int sdrpp_main(int argc, char* argv[]) { defConfig["modulesDirectory"] = "../Plugins"; defConfig["resourcesDirectory"] = "../Resources"; #elif defined(__ANDROID__) - defConfig["modulesDirectory"] = options::opts.root + "/modules"; - defConfig["resourcesDirectory"] = options::opts.root + "/res"; + defConfig["modulesDirectory"] = root + "/modules"; + defConfig["resourcesDirectory"] = root + "/res"; #else defConfig["modulesDirectory"] = INSTALL_PREFIX "/lib/sdrpp/plugins"; defConfig["resourcesDirectory"] = INSTALL_PREFIX "/share/sdrpp"; @@ -234,7 +233,7 @@ int sdrpp_main(int argc, char* argv[]) { // Load config spdlog::info("Loading config"); - core::configManager.setPath(options::opts.root + "/config.json"); + core::configManager.setPath(root + "/config.json"); core::configManager.load(defConfig); core::configManager.enableAutoSave(); core::configManager.acquire(); @@ -296,7 +295,7 @@ int sdrpp_main(int argc, char* argv[]) { core::configManager.release(true); - if (options::opts.serverMode) { return server::main(); } + if (serverMode) { return server::main(); } core::configManager.acquire(); std::string resDir = core::configManager.conf["resourcesDirectory"]; @@ -314,6 +313,9 @@ int sdrpp_main(int argc, char* argv[]) { int biRes = backend::init(resDir); if (biRes < 0) { return biRes; } + // Intialize SmGui in normal mode + SmGui::init(false); + if (!style::loadFonts(resDir)) { return -1; } thememenu::init(resDir); LoadingScreen::init(); diff --git a/core/src/gui/icons.cpp b/core/src/gui/icons.cpp index cc2c68ec..22fc18f4 100644 --- a/core/src/gui/icons.cpp +++ b/core/src/gui/icons.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #define STB_IMAGE_IMPLEMENTATION #include diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index 7625e739..369e043d 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/core/src/gui/menus/theme.cpp b/core/src/gui/menus/theme.cpp index fa161b30..accd9d35 100644 --- a/core/src/gui/menus/theme.cpp +++ b/core/src/gui/menus/theme.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include diff --git a/core/src/gui/smgui.cpp b/core/src/gui/smgui.cpp index aa6d596f..7935dde3 100644 --- a/core/src/gui/smgui.cpp +++ b/core/src/gui/smgui.cpp @@ -1,6 +1,5 @@ #include "smgui.h" #include "style.h" -#include #include #include @@ -25,6 +24,7 @@ namespace SmGui { std::string diffId = ""; DrawListElem diffValue; bool nextItemFillWidth = false; + bool serverMode = false; std::string ImStrToString(const char* imstr) { int len = 0; @@ -33,6 +33,10 @@ namespace SmGui { return std::string(imstr, end); } + void init(bool server) { + serverMode = server; + } + // Rec/Play functions void setDiff(std::string id, SmGui::DrawListElem value) { diffId = id; @@ -457,7 +461,7 @@ namespace SmGui { // Format functions void FillWidth() { - if (!options::opts.serverMode) { + if (!serverMode) { nextItemFillWidth = true; ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); return; @@ -466,17 +470,17 @@ namespace SmGui { } void SameLine() { - if (!options::opts.serverMode) { ImGui::SameLine(); return; } + if (!serverMode) { ImGui::SameLine(); return; } if (rdl) { rdl->pushStep(DRAW_STEP_SAME_LINE, false); } } void BeginDisabled() { - if (!options::opts.serverMode) { style::beginDisabled(); return; } + if (!serverMode) { style::beginDisabled(); return; } if (rdl) { rdl->pushStep(DRAW_STEP_BEGIN_DISABLED, false); } } void EndDisabled() { - if (!options::opts.serverMode) { style::endDisabled(); return; } + if (!serverMode) { style::endDisabled(); return; } if (rdl) { rdl->pushStep(DRAW_STEP_END_DISABLED, false); } } @@ -484,7 +488,7 @@ namespace SmGui { // Widget functions bool Combo(const char *label, int *current_item, const char *items_separated_by_zeros, int popup_max_height_in_items) { nextItemFillWidth = false; - if (!options::opts.serverMode) { return ImGui::Combo(label, current_item, items_separated_by_zeros, popup_max_height_in_items); } + if (!serverMode) { return ImGui::Combo(label, current_item, items_separated_by_zeros, popup_max_height_in_items); } if (rdl) { rdl->pushStep(DRAW_STEP_COMBO, forceSyncForNext); rdl->pushString(label); @@ -501,7 +505,7 @@ namespace SmGui { } bool Button(const char *label, ImVec2 size) { - if (!options::opts.serverMode) { + if (!serverMode) { if (nextItemFillWidth) { nextItemFillWidth = false; size.x = ImGui::GetContentRegionAvail().x; @@ -519,7 +523,7 @@ namespace SmGui { } void Columns(int count, const char *id, bool border) { - if (!options::opts.serverMode) { ImGui::Columns(count, id, border); return; } + if (!serverMode) { ImGui::Columns(count, id, border); return; } if (rdl) { rdl->pushStep(DRAW_STEP_COLUMNS, forceSyncForNext); rdl->pushInt(count); @@ -530,12 +534,12 @@ namespace SmGui { } void NextColumn() { - if (!options::opts.serverMode) { ImGui::NextColumn(); return; } + if (!serverMode) { ImGui::NextColumn(); return; } if (rdl) { rdl->pushStep(DRAW_STEP_NEXT_COLUMN, false); } } bool RadioButton(const char *label, bool active) { - if (!options::opts.serverMode) { return ImGui::RadioButton(label, active); } + if (!serverMode) { return ImGui::RadioButton(label, active); } if (rdl) { rdl->pushStep(DRAW_STEP_RADIO_BUTTON, forceSyncForNext); rdl->pushString(label); @@ -546,17 +550,17 @@ namespace SmGui { } void BeginGroup() { - if (!options::opts.serverMode) { ImGui::BeginGroup(); return; } + if (!serverMode) { ImGui::BeginGroup(); return; } if (rdl) { rdl->pushStep(DRAW_STEP_BEGIN_GROUP, false); } } void EndGroup() { - if (!options::opts.serverMode) { ImGui::EndGroup(); return; } + if (!serverMode) { ImGui::EndGroup(); return; } if (rdl) { rdl->pushStep(DRAW_STEP_END_GROUP, false); } } void LeftLabel(const char *text) { - if (!options::opts.serverMode) { ImGui::LeftLabel(text); return; } + if (!serverMode) { ImGui::LeftLabel(text); return; } if (rdl) { rdl->pushStep(DRAW_STEP_LEFT_LABEL, forceSyncForNext); rdl->pushString(text); @@ -566,7 +570,7 @@ namespace SmGui { bool SliderInt(const char *label, int *v, int v_min, int v_max, FormatString format, ImGuiSliderFlags flags) { nextItemFillWidth = false; - if (!options::opts.serverMode) { return ImGui::SliderInt(label, v, v_min, v_max, fmtStr[format], flags); } + if (!serverMode) { return ImGui::SliderInt(label, v, v_min, v_max, fmtStr[format], flags); } if (rdl) { rdl->pushStep(DRAW_STEP_SLIDER_INT, forceSyncForNext); rdl->pushString(label); @@ -586,7 +590,7 @@ namespace SmGui { bool SliderFloatWithSteps(const char *label, float *v, float v_min, float v_max, float v_step, FormatString display_format) { nextItemFillWidth = false; - if (!options::opts.serverMode) { return ImGui::SliderFloatWithSteps(label, v, v_min, v_max, v_step, fmtStr[display_format]); } + if (!serverMode) { return ImGui::SliderFloatWithSteps(label, v, v_min, v_max, v_step, fmtStr[display_format]); } if (rdl) { rdl->pushStep(DRAW_STEP_SLIDER_FLOAT_WITH_STEPS, forceSyncForNext); rdl->pushString(label); @@ -606,7 +610,7 @@ namespace SmGui { bool InputInt(const char *label, int *v, int step, int step_fast, ImGuiInputTextFlags flags) { nextItemFillWidth = false; - if (!options::opts.serverMode) { return ImGui::InputInt(label, v, step, step_fast, flags); } + if (!serverMode) { return ImGui::InputInt(label, v, step, step_fast, flags); } if (rdl) { rdl->pushStep(DRAW_STEP_INPUT_INT, forceSyncForNext); rdl->pushString(label); @@ -624,7 +628,7 @@ namespace SmGui { } bool Checkbox(const char *label, bool *v) { - if (!options::opts.serverMode) { return ImGui::Checkbox(label, v); } + if (!serverMode) { return ImGui::Checkbox(label, v); } if (rdl) { rdl->pushStep(DRAW_STEP_CHECKBOX, forceSyncForNext); rdl->pushString(label); @@ -640,7 +644,7 @@ namespace SmGui { bool SliderFloat(const char *label, float *v, float v_min, float v_max, FormatString format, ImGuiSliderFlags flags) { nextItemFillWidth = false; - if (!options::opts.serverMode) { return ImGui::SliderFloat(label, v, v_min, v_max, fmtStr[format], flags); } + if (!serverMode) { return ImGui::SliderFloat(label, v, v_min, v_max, fmtStr[format], flags); } if (rdl) { rdl->pushStep(DRAW_STEP_SLIDER_FLOAT, forceSyncForNext); rdl->pushString(label); @@ -660,7 +664,7 @@ namespace SmGui { bool InputText(const char *label, char *buf, size_t buf_size, ImGuiInputTextFlags flags) { nextItemFillWidth = false; - if (!options::opts.serverMode) { return ImGui::InputText(label, buf, buf_size, flags); } + if (!serverMode) { return ImGui::InputText(label, buf, buf_size, flags); } if (rdl) { rdl->pushStep(DRAW_STEP_INPUT_TEXT, forceSyncForNext); rdl->pushString(label); @@ -677,7 +681,7 @@ namespace SmGui { } void Text(const char* str) { - if (!options::opts.serverMode) { ImGui::TextUnformatted(str); return; } + if (!serverMode) { ImGui::TextUnformatted(str); return; } if (rdl) { rdl->pushStep(DRAW_STEP_TEXT, false); rdl->pushString(str); @@ -685,7 +689,7 @@ namespace SmGui { } void TextColored(const ImVec4 &col, const char *str) { - if (!options::opts.serverMode) { ImGui::TextColored(col, "%s", str); return; } + if (!serverMode) { ImGui::TextColored(col, "%s", str); return; } if (rdl) { rdl->pushStep(DRAW_STEP_TEXT_COLORED, false); rdl->pushFloat(col.x); @@ -697,7 +701,7 @@ namespace SmGui { } void OpenPopup(const char *str_id, ImGuiPopupFlags popup_flags) { - if (!options::opts.serverMode) { ImGui::OpenPopup(str_id, popup_flags); return; } + if (!serverMode) { ImGui::OpenPopup(str_id, popup_flags); return; } if (rdl) { rdl->pushStep(DRAW_STEP_OPEN_POPUP, false); rdl->pushString(str_id); @@ -706,7 +710,7 @@ namespace SmGui { } bool BeginPopup(const char *str_id, ImGuiWindowFlags flags) { - if (!options::opts.serverMode) { return ImGui::BeginPopup(str_id, flags); } + if (!serverMode) { return ImGui::BeginPopup(str_id, flags); } if (rdl) { rdl->pushStep(DRAW_STEP_BEGIN_POPUP, false); rdl->pushString(str_id); @@ -716,14 +720,14 @@ namespace SmGui { } void EndPopup() { - if (!options::opts.serverMode) { ImGui::EndPopup(); return; } + if (!serverMode) { ImGui::EndPopup(); return; } if (rdl) { rdl->pushStep(DRAW_STEP_END_POPUP, false); } } bool BeginTable(const char *str_id, int column, ImGuiTableFlags flags, const ImVec2 &outer_size, float inner_width) { - if (!options::opts.serverMode) { return ImGui::BeginTable(str_id, column, flags, outer_size, inner_width); } + if (!serverMode) { return ImGui::BeginTable(str_id, column, flags, outer_size, inner_width); } if (rdl) { rdl->pushStep(DRAW_STEP_BEGIN_TABLE, false); rdl->pushString(str_id); @@ -737,14 +741,14 @@ namespace SmGui { } void EndTable() { - if (!options::opts.serverMode) { ImGui::EndTable(); return; } + if (!serverMode) { ImGui::EndTable(); return; } if (rdl) { rdl->pushStep(DRAW_STEP_END_TABLE, false); } } void TableNextRow(ImGuiTableRowFlags row_flags, float min_row_height) { - if (!options::opts.serverMode) { ImGui::TableNextRow(row_flags, min_row_height); return; } + if (!serverMode) { ImGui::TableNextRow(row_flags, min_row_height); return; } if (rdl) { rdl->pushStep(DRAW_STEP_TABLE_NEXT_ROW, false); rdl->pushInt(row_flags); @@ -753,7 +757,7 @@ namespace SmGui { } void TableSetColumnIndex(int column_n) { - if (!options::opts.serverMode) { ImGui::TableSetColumnIndex(column_n); return; } + if (!serverMode) { ImGui::TableSetColumnIndex(column_n); return; } if (rdl) { rdl->pushStep(DRAW_STEP_TABLE_SET_COLUMN_INDEX, false); rdl->pushInt(column_n); @@ -761,7 +765,7 @@ namespace SmGui { } void SetNextItemWidth(float item_width) { - if (!options::opts.serverMode) { ImGui::SetNextItemWidth(item_width); return; } + if (!serverMode) { ImGui::SetNextItemWidth(item_width); return; } if (rdl) { rdl->pushStep(DRAW_STEP_SET_NEXT_ITEM_WIDTH, false); rdl->pushFloat(item_width); diff --git a/core/src/gui/smgui.h b/core/src/gui/smgui.h index d3308859..206f173f 100644 --- a/core/src/gui/smgui.h +++ b/core/src/gui/smgui.h @@ -100,6 +100,7 @@ namespace SmGui { // Rec/Play functions // TODO: Maybe move verification to the load function instead of checking in drawFrame + void init(bool server); void setDiff(std::string id, SmGui::DrawListElem value); void startRecord(DrawList* dl); void stopRecord(); diff --git a/core/src/gui/style.cpp b/core/src/gui/style.cpp index efa0beb8..58918230 100644 --- a/core/src/gui/style.cpp +++ b/core/src/gui/style.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include diff --git a/core/src/gui/widgets/file_select.cpp b/core/src/gui/widgets/file_select.cpp index c1c64d86..dea857dc 100644 --- a/core/src/gui/widgets/file_select.cpp +++ b/core/src/gui/widgets/file_select.cpp @@ -1,11 +1,12 @@ #include #include -#include #include #include +#include FileSelect::FileSelect(std::string defaultPath, std::vector filter) { _filter = filter; + root = core::args["root"]; setPath(defaultPath); } @@ -54,7 +55,7 @@ void FileSelect::setPath(std::string path, bool markChanged) { } std::string FileSelect::expandString(std::string input) { - input = std::regex_replace(input, std::regex("%ROOT%"), options::opts.root); + input = std::regex_replace(input, std::regex("%ROOT%"), root); return std::regex_replace(input, std::regex("//"), "/"); } diff --git a/core/src/gui/widgets/file_select.h b/core/src/gui/widgets/file_select.h index 25755a3d..d5691edd 100644 --- a/core/src/gui/widgets/file_select.h +++ b/core/src/gui/widgets/file_select.h @@ -22,6 +22,7 @@ private: void worker(); std::thread workerThread; std::vector _filter; + std::string root = ""; bool pathValid = false; bool dialogOpen = false; diff --git a/core/src/gui/widgets/folder_select.cpp b/core/src/gui/widgets/folder_select.cpp index 1a1cd722..b2f7029c 100644 --- a/core/src/gui/widgets/folder_select.cpp +++ b/core/src/gui/widgets/folder_select.cpp @@ -1,10 +1,11 @@ #include #include -#include #include #include +#include FolderSelect::FolderSelect(std::string defaultPath) { + root = core::args["root"]; setPath(defaultPath); } @@ -53,7 +54,7 @@ void FolderSelect::setPath(std::string path, bool markChanged) { } std::string FolderSelect::expandString(std::string input) { - input = std::regex_replace(input, std::regex("%ROOT%"), options::opts.root); + input = std::regex_replace(input, std::regex("%ROOT%"), root); return std::regex_replace(input, std::regex("//"), "/"); } diff --git a/core/src/gui/widgets/folder_select.h b/core/src/gui/widgets/folder_select.h index 194e94a6..27a89872 100644 --- a/core/src/gui/widgets/folder_select.h +++ b/core/src/gui/widgets/folder_select.h @@ -20,6 +20,7 @@ public: private: void worker(); std::thread workerThread; + std::string root = ""; bool pathValid = false; bool dialogOpen = false; diff --git a/core/src/options.cpp b/core/src/options.cpp deleted file mode 100644 index 296bd363..00000000 --- a/core/src/options.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include -#include -#include - -namespace options { - CMDLineOptions opts; - - void loadDefaults() { -#if defined(_WIN32) - opts.root = "."; - opts.showConsole = false; -#elif defined(IS_MACOS_BUNDLE) - std::string homedir = getenv("HOME"); - opts.root = homedir + "/Library/Application Support/sdrpp"; -#elif defined(__ANDROID__) - opts.root = "/storage/self/primary/sdrpp"; -#else - std::string homedir = getenv("HOME"); - opts.root = homedir + "/.config/sdrpp"; -#endif - opts.root = std::filesystem::absolute(opts.root).string(); - opts.serverHost = "0.0.0.0"; - opts.serverPort = 5259; - } - - 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 = std::filesystem::absolute(argv[++i]).string(); - } - else if (!strcmp(arg, "-s") || !strcmp(arg, "--show-console")) { - opts.showConsole = true; - } - else if (!strcmp(arg, "--server")) { - opts.serverMode = true; - } - else if (!strcmp(arg, "-a") || !strcmp(arg, "--addr")) { - if (i == argc - 1) { return false; } - opts.serverHost = argv[++i]; - opts.showConsole = true; - } - else if (!strcmp(arg, "-p") || !strcmp(arg, "--port")) { - if (i == argc - 1) { return false; } - sscanf(argv[++i], "%d", &opts.serverPort); - opts.showConsole = true; - } - 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 deleted file mode 100644 index 50520f75..00000000 --- a/core/src/options.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include -#include - -namespace options { - struct CMDLineOptions { - std::string root; - bool showConsole; - bool serverMode; - std::string serverHost; - int serverPort; - }; - - SDRPP_EXPORT CMDLineOptions opts; - - void loadDefaults(); - bool parse(int argc, char* argv[]); -} \ No newline at end of file diff --git a/core/src/server.cpp b/core/src/server.cpp index d29b00f9..367ea278 100644 --- a/core/src/server.cpp +++ b/core/src/server.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -75,6 +74,7 @@ namespace server { // Initialize compressor cctx = ZSTD_createCCtx(); + // Load config core::configManager.acquire(); std::string modulesDir = core::configManager.conf["modulesDirectory"]; std::vector modules = core::configManager.conf["modules"]; @@ -83,8 +83,10 @@ namespace server { core::configManager.release(); modulesDir = std::filesystem::absolute(modulesDir).string(); - spdlog::info("Loading modules"); + // Intialize SmGui in server mode + SmGui::init(true); + spdlog::info("Loading modules"); // Load modules and check type to only load sources ( TODO: Have a proper type parameter int the info ) // TODO LATER: Add whitelist/blacklist stuff if (std::filesystem::is_directory(modulesDir)) { @@ -146,10 +148,12 @@ namespace server { sigpath::sourceManager.selectSource(sourceList[sourceId]); // TODO: Use command line option - listener = net::listen(options::opts.serverHost, options::opts.serverPort); + std::string host = core::args["addr"]; + int port = core::args["port"]; + listener = net::listen(host, port); listener->acceptAsync(_clientHandler, NULL); - spdlog::info("Ready, listening on {0}:{1}", options::opts.serverHost, options::opts.serverPort); + spdlog::info("Ready, listening on {0}:{1}", host, port); while(1) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } return 0; diff --git a/core/src/signal_path/source.cpp b/core/src/signal_path/source.cpp index 3b878e37..7dc495fc 100644 --- a/core/src/signal_path/source.cpp +++ b/core/src/signal_path/source.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include SourceManager::SourceManager() { } @@ -50,7 +50,7 @@ void SourceManager::selectSource(std::string name) { selectedHandler = sources[name]; selectedHandler->selectHandler(selectedHandler->ctx); selectedName = name; - if (options::opts.serverMode) { + if (core::args["server"].b()) { server::setInput(selectedHandler->stream); } else { diff --git a/decoder_modules/falcon9_decoder/src/main.cpp b/decoder_modules/falcon9_decoder/src/main.cpp index f7f4f1e3..7342b8cb 100644 --- a/decoder_modules/falcon9_decoder/src/main.cpp +++ b/decoder_modules/falcon9_decoder/src/main.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include diff --git a/decoder_modules/kg_sstv_decoder/CMakeLists.txt b/decoder_modules/kg_sstv_decoder/CMakeLists.txt new file mode 100644 index 00000000..4bb74df2 --- /dev/null +++ b/decoder_modules/kg_sstv_decoder/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.13) +project(kg_sstv_decoder) + +file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c") + +add_library(kg_sstv_decoder SHARED ${SRC}) +target_link_libraries(kg_sstv_decoder PRIVATE sdrpp_core) +set_target_properties(kg_sstv_decoder PROPERTIES PREFIX "") + +target_include_directories(kg_sstv_decoder PRIVATE "src/") + +if (MSVC) + target_compile_options(kg_sstv_decoder PRIVATE /O2 /Ob2 /std:c++17 /EHsc) +elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(kg_sstv_decoder PRIVATE -O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup) +else () + target_compile_options(kg_sstv_decoder PRIVATE -O3 -std=c++17) +endif () + +# Install directives +install(TARGETS kg_sstv_decoder DESTINATION lib/sdrpp/plugins) \ No newline at end of file diff --git a/decoder_modules/kg_sstv_decoder/protocol_translated.txt b/decoder_modules/kg_sstv_decoder/protocol_translated.txt new file mode 100644 index 00000000..0ff98960 --- /dev/null +++ b/decoder_modules/kg_sstv_decoder/protocol_translated.txt @@ -0,0 +1,133 @@ +○ Transmission by MSK and 4L-FSK +MSK is a type of FSK method that is a modulation method for digital signals, and is the mark frequency of FSK. +Minimize the frequency between the number and the space frequency (Minimum Shift Keying) +4L-FSK is a 4-value FSK, and 4 modulation frequencies that can be taken with a type of FSK are provided. +This makes it possible to obtain twice the transmission speed compared to MSK. +4L-FSK is more susceptible to noise because the frequency interval is narrower than MSK. +It also becomes more susceptible to deterioration of transmission line characteristics. +There are two types of choices, but when the line condition such as FM is good, the image will be sent quickly, so +Or I think you should select 4L-FSK to send high quality images. wireless +If the SSB transmission in HF where the line condition is unstable is set to MSK, the data will be as it is. +I think it will be. +The first pull-down menu from the top of the pull-down menu on the right side of Send Image +You can select MSK or 4L-FSK in the new. + +○ Transmission in convolutional code mode +The second pull-down menu from the top of the pull-down menu on the right side of Send Image +New is the choice of code processing. Here no code processing (NORM) or convolutional code processing +Set whether to perform (CONV). +When convolutional code processing is performed, it becomes stronger against noise, but the same data is sent. +It takes about twice as long to complete. +Please select the code processing on a case-by-case basis while checking the transmission status. +This setting can be changed even during transmission. + +○ Sending and receiving text messages +With KG-STV, a maximum of 510 half-width characters (255 full-width characters) text message can be sent once. +You can send sage. +To send a text message, text in the input box under Resp BSR +And click Send Text on the right. +When you receive a text message, the text will be displayed in the box below. + +○ KG-STV transmission standard +Modulation format: MSK or composite modulation of MSK and 4-value FSK +Modulation speed: 1200baud +Modulation frequency: MSK …… Space frequency 1200Hz Mark frequency 1800Hz + +4L-FSK ... +'00' 1200Hz +'01' 1400Hz +'10' 1600Hz +'11' 1800Hz + +Bandwidth: 500-2500Hz +Error correction: None or Viterbi code (NASA standard K = 7, R=1/2, P=[109, 79] code) +Whitening: Yes (add M-sequence code with a period of 127 bits to each bit) +Interleaver: None +Header: 01 repeat signal is 256 bits +Basic code configuration: Synchronous code + 54-bit length information chunk + arbitrary length data chunk +Error detection: Yes (CRC 16 CCITT) +Synchronous bit: 63-bit M-sequence code +Image compression: JPEG compatible (16x16 pixels, thinning 4: 1: 1) +Character code: Shift JIS compatible +Radio format: F1D (operation in SSB mode) +F2D (operation in FM mode) + +Information chunk configuration (system code version '0') + +┌─────┬─────┬───┬───┬───┬───┬────┬──────┬─────┐ +│ sys │ com │ c │ m │ x │ y │ sc │ size │ CRC │ +└─────┴─────┴───┴───┴───┴───┴────┴──────┴─────┘ +0 4 8 9 10 16 22 26 38 54bit + +Sys: system code +Com: command code +C: sign mode +M: Modulation mode +X: Image block position (X) +Y: Image block position (Y) +Sc: JPEG scale size +Size: data size +CRC: Error detection code + +* Information chunks always perform MSK modulation + convolution processing. + +Data chunk structure (system code version '0') + +┌──────┬─────┐ +│ data │ CRC │ +└──────┴─────┘ + +* Data chunks are transmitted following the CRC code of the information chunks. + +Command table (system code version '0') +┌─────┬────────┐ +│ Command value │ Operation │ │ +├─────┼────────┤ +│ 0 │ text transmission │ +├─────┼────────┤ +│ 1 │ Image transmission │ │ +├─────┼────────┤ +│ 2 │ BSR response │ │ +├─────┼────────┤ +│ │ 3 │ end │ │ +├─────┼────────┤ +│ │ 4 │ BSR request │ │ +├─────┼────────┤ +│ 5 │ Canceled (suspended) │ +├─────┼────────┤ +│ 6 │ call sign │ +└─────┴────────┘ + +* When starting transmission, the header code of 01 shall be transmitted first. +* One data format (code configuration) is +Synchronous code + information chunk + data chunk +It will be. However, the end signal, stop signal, and BSR signal do not include data chunks. + stomach. +* In the case of image data, this data format is continuous for the number of image blocks. +It shall be sent. +* In the case of text transmission, it shall be completed in one data format. +* When the transmission is completed normally, the end signal shall be transmitted three times at the end. +* When the transmission is canceled, the cancellation signal shall be transmitted three times at the end. + +Synchronous code +000011100001001000110110010110101110111100110001010100111111010 + +Whitening code +1110110011000100100111001111100100000100011010101001101101001010000101100001100101111111010110111011110001110100010101110000001 + +○ KG-STV needs more experiments +KG-STV was born from the creator's own interest in digital image communication. +However, there is a lack of practical experience due to the ability of the person himself / herself. +Therefore, the actual luck is that the transmission time is slow, the image quality is poor, and it is difficult to receive. +I think there are many situations where it is not practical in terms of use. +In such a case, I would like to research as a new issue, so by all means +We hope that many people will use it and give us their impressions. +Inconvenience such as bugs and operational problems until the degree of completion is high after receiving your opinions +We apologize for the inconvenience, but thank you for your cooperation. + +Creator: K.G (JJ0OBZ Myoko City, Niigata Prefecture) + +-------------------------------------------------- -------------------------------------------------- -------------------- + Created and written by K.G k.g8956@ymail.plala.or.jp +Today also from the day of the plane http://www2.plala.or.jp/hikokibiyori/ +-------------------------------------------------- -------------------------------------------------- -------------------- \ No newline at end of file diff --git a/decoder_modules/kg_sstv_decoder/src/kg_sstv_dsp.h b/decoder_modules/kg_sstv_decoder/src/kg_sstv_dsp.h new file mode 100644 index 00000000..cd55c0f9 --- /dev/null +++ b/decoder_modules/kg_sstv_decoder/src/kg_sstv_dsp.h @@ -0,0 +1,280 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include +} + +#define KGSSTV_DEVIATION 300 +#define KGSSTV_BAUDRATE 1200 +#define KGSSTV_RRC_ALPHA 0.7f +#define KGSSTV_4FSK_HIGH_CUT 0.5f + +// const uint8_t KGSSTV_SYNC_WORD[] = { +// 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, +// 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, +// 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, +// 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, +// 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, +// 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, +// 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, +// 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0 +// }; + +const uint8_t KGSSTV_SYNC_WORD[] = { + 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, + 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, + 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0 +}; + +const uint8_t KGSSTV_SCRAMBLING[] = { + 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, + 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, + 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, + 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, + 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, + 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1 +}; + +const uint8_t KGSSTV_SCRAMBLING_BYTES[] = { + 0b11101100, 0b11000100, 0b10011100, 0b11111001, 0b00000100, + 0b01101010, 0b10011011, 0b01001010, 0b00010110, 0b00011001, + 0b01111111, 0b01011011, 0b10111100, 0b01110100, 0b01010111, + 0b00000010 +}; + +static const correct_convolutional_polynomial_t kgsstv_polynomial[] = {0155, 0117}; + +#define KGSSTV_SYNC_WORD_SIZE sizeof(KGSSTV_SYNC_WORD) +#define KGSSTV_SYNC_SCRAMBLING_SIZE sizeof(KGSSTV_SCRAMBLING) + +namespace kgsstv { + // class Slice4FSK : public dsp::generic_block { + // public: + // Slice4FSK() {} + + // Slice4FSK(dsp::stream* in) { init(in); } + + // void init(dsp::stream* in) { + // _in = in; + // dsp::generic_block::registerInput(_in); + // dsp::generic_block::registerOutput(&out); + // dsp::generic_block::_block_init = true; + // } + + // void setInput(dsp::stream* in) { + // assert(dsp::generic_block::_block_init); + // std::lock_guard lck(dsp::generic_block::ctrlMtx); + // dsp::generic_block::tempStop(); + // dsp::generic_block::unregisterInput(_in); + // _in = in; + // dsp::generic_block::registerInput(_in); + // dsp::generic_block::tempStart(); + // } + + // int run() { + // int count = _in->read(); + // if (count < 0) { return -1; } + + // float val; + // for (int i = 0; i < count; i++) { + // val = _in->readBuf[i]; + + // out.writeBuf[i * 2] = (val > 0.0f); + // if (val > 0.0f) { + // out.writeBuf[(i * 2) + 1] = (val > KGSSTV_4FSK_HIGH_CUT); + // } + // else { + // out.writeBuf[(i * 2) + 1] = (val > -KGSSTV_4FSK_HIGH_CUT); + // } + // } + + // _in->flush(); + + // if (!out.swap(count * 2)) { return -1; } + // return count; + // } + + // dsp::stream out; + + // private: + // dsp::stream* _in; + // }; + + class Deframer : public dsp::generic_block { + public: + Deframer() {} + + Deframer(dsp::stream* in) { init(in); } + + void init(dsp::stream* in) { + _in = in; + + // TODO: Destroy + conv = correct_convolutional_create(2, 7, kgsstv_polynomial); + memset(convTmp, 0x00, 1024); + + dsp::generic_block::registerInput(_in); + dsp::generic_block::registerOutput(&out); + dsp::generic_block::_block_init = true; + } + + void setInput(dsp::stream* in) { + assert(dsp::generic_block::_block_init); + std::lock_guard lck(dsp::generic_block::ctrlMtx); + dsp::generic_block::tempStop(); + dsp::generic_block::unregisterInput(_in); + _in = in; + dsp::generic_block::registerInput(_in); + dsp::generic_block::tempStart(); + } + + int run() { + int count = _in->read(); + if (count < 0) { return -1; } + + for (int i = 0; i < count; i++) { + if (syncing) { + // If sync broken, reset sync + if ((_in->readBuf[i] > 0.0f) && !KGSSTV_SYNC_WORD[match]) { + if (++err > 4) { + i -= match - 1; + match = 0; + err = 0; + continue; + } + } + + // If full syncword was detected, switch to read mode + if (++match == KGSSTV_SYNC_WORD_SIZE) { + spdlog::warn("Frame detected"); + syncing = false; + readCount = 0; + writeCount = 0; + } + } + else { + // // Process symbol + // if (!(readCount % 2)) { + // int bitOffset = writeCount & 0b111; + // int byteOffset = writeCount >> 3; + // if (!bitOffset) { convTmp[byteOffset] = 0; } + // convTmp[byteOffset] |= _in->readBuf[i] << (7 - bitOffset); + // writeCount++; + // } + + // Process symbol + convTmp[readCount] = std::clamp((_in->readBuf[i] + 1.0f) * 128.0f, 0, 255); + + // When info was read, write data and get back to + if (++readCount == 108) { + match = 0; + err = 0; + syncing = true; + + // Descramble + for (int j = 0; j < 108; j++) { + if (KGSSTV_SCRAMBLING[j]) { + convTmp[j] = 255 - convTmp[j]; + } + + //convTmp[j >> 3] ^= KGSSTV_SCRAMBLING[j] << (7 - (j & 0b111)); + } + + // Decode convolutional code + int convOutCount = correct_convolutional_decode_soft(conv, convTmp, 124, out.writeBuf); + + spdlog::warn("Frames written: {0}, frameBytes: {1}", ++framesWritten, convOutCount); + if (!out.swap(7)) { + _in->flush(); + return -1; + } + } + } + } + + _in->flush(); + + return count; + } + + dsp::stream out; + + private: + dsp::stream* _in; + correct_convolutional* conv = NULL; + uint8_t convTmp[1024]; + + int match = 0; + int err = 0; + int readCount = 0; + int writeCount = 0; + bool syncing = true; + + int framesWritten = 0; + }; + + class Decoder : public dsp::generic_hier_block { + public: + Decoder() {} + + Decoder(dsp::stream* input, float sampleRate) { + init(input, sampleRate); + } + + void init(dsp::stream* input, float sampleRate) { + _sampleRate = sampleRate; + + demod.init(input, _sampleRate, KGSSTV_DEVIATION); + rrc.init(31, _sampleRate, KGSSTV_BAUDRATE, KGSSTV_RRC_ALPHA); + fir.init(&demod.out, &rrc); + recov.init(&fir.out, _sampleRate / KGSSTV_BAUDRATE, 1e-6f, 0.01f, 0.01f); + doubler.init(&recov.out); + + //slicer.init(&doubler.outA); + deframer.init(&doubler.outA); + ns2.init(&deframer.out, "kgsstv_out.bin"); + diagOut = &doubler.outB; + + dsp::generic_hier_block::registerBlock(&demod); + dsp::generic_hier_block::registerBlock(&fir); + dsp::generic_hier_block::registerBlock(&recov); + dsp::generic_hier_block::registerBlock(&doubler); + //dsp::generic_hier_block::registerBlock(&slicer); + dsp::generic_hier_block::registerBlock(&deframer); + dsp::generic_hier_block::registerBlock(&ns2); + + dsp::generic_hier_block::_block_init = true; + } + + void setInput(dsp::stream* input) { + assert(dsp::generic_hier_block::_block_init); + demod.setInput(input); + } + + dsp::stream* diagOut = NULL; + + private: + dsp::FloatFMDemod demod; + dsp::RRCTaps rrc; + dsp::FIR fir; + dsp::MMClockRecovery recov; + dsp::StreamDoubler doubler; + + // Slice4FSK slicer; + Deframer deframer; + dsp::FileSink ns2; + + + float _sampleRate; + }; +} \ No newline at end of file diff --git a/decoder_modules/kg_sstv_decoder/src/main.cpp b/decoder_modules/kg_sstv_decoder/src/main.cpp new file mode 100644 index 00000000..87feab29 --- /dev/null +++ b/decoder_modules/kg_sstv_decoder/src/main.cpp @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kg_sstv_dsp.h" + +#define CONCAT(a, b) ((std::string(a) + b).c_str()) + +SDRPP_MOD_INFO{ + /* Name: */ "kg_sstv_decoder", + /* Description: */ "KG-SSTV Digital SSTV Decoder for SDR++", + /* Author: */ "Ryzerth", + /* Version: */ 0, 1, 0, + /* Max instances */ -1 +}; + +ConfigManager config; + +#define INPUT_SAMPLE_RATE 6000 + +class M17DecoderModule : public ModuleManager::Instance { +public: + M17DecoderModule(std::string name) : diag(0.8, 480) { + this->name = name; + + // Load config + config.acquire(); + if (!config.conf.contains(name)) { + config.conf[name]["showLines"] = false; + } + showLines = config.conf[name]["showLines"]; + if (showLines) { + diag.lines.push_back(-0.75f); + diag.lines.push_back(-0.25f); + diag.lines.push_back(0.25f); + diag.lines.push_back(0.75f); + } + config.release(true); + + + // Initialize VFO + vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 3000, INPUT_SAMPLE_RATE, 3000, 3000, true); + vfo->setSnapInterval(250); + + // Initialize DSP here + decoder.init(vfo->output, INPUT_SAMPLE_RATE); + + reshape.init(decoder.diagOut, 480, 0); + diagHandler.init(&reshape.out, _diagHandler, this); + + // Start DSO Here + decoder.start(); + reshape.start(); + diagHandler.start(); + + //stream.start(); + + gui::menu.registerEntry(name, menuHandler, this, this); + } + + ~M17DecoderModule() { + gui::menu.removeEntry(name); + // Stop DSP Here + if (enabled) { + decoder.stop(); + reshape.stop(); + diagHandler.stop(); + sigpath::vfoManager.deleteVFO(vfo); + } + + sigpath::sinkManager.unregisterStream(name); + } + + void postInit() {} + + void enable() { + double bw = gui::waterfall.getBandwidth(); + vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp(0, -bw / 2.0, bw / 2.0), 9600, INPUT_SAMPLE_RATE, 9600, 9600, true); + vfo->setSnapInterval(250); + + // Set Input of demod here + decoder.setInput(vfo->output); + + // Start DSP here + decoder.start(); + reshape.start(); + diagHandler.start(); + + enabled = true; + } + + void disable() { + // Stop DSP here + decoder.stop(); + reshape.stop(); + diagHandler.stop(); + + sigpath::vfoManager.deleteVFO(vfo); + enabled = false; + } + + bool isEnabled() { + return enabled; + } + +private: + static void menuHandler(void* ctx) { + M17DecoderModule* _this = (M17DecoderModule*)ctx; + + float menuWidth = ImGui::GetContentRegionAvail().x; + + if (!_this->enabled) { style::beginDisabled(); } + + ImGui::SetNextItemWidth(menuWidth); + _this->diag.draw(); + + if (ImGui::Checkbox(CONCAT("Show Reference Lines##m17_showlines_", _this->name), &_this->showLines)) { + if (_this->showLines) { + _this->diag.lines.push_back(-0.75f); + _this->diag.lines.push_back(-0.25f); + _this->diag.lines.push_back(0.25f); + _this->diag.lines.push_back(0.75f); + } + else { + _this->diag.lines.clear(); + } + config.acquire(); + config.conf[_this->name]["showLines"] = _this->showLines; + config.release(true); + } + + if (!_this->enabled) { style::endDisabled(); } + } + + static void _diagHandler(float* data, int count, void* ctx) { + M17DecoderModule* _this = (M17DecoderModule*)ctx; + float* buf = _this->diag.acquireBuffer(); + memcpy(buf, data, count * sizeof(float)); + _this->diag.releaseBuffer(); + } + + std::string name; + bool enabled = true; + + // DSP Chain + VFOManager::VFO* vfo; + kgsstv::Decoder decoder; + + dsp::Reshaper reshape; + dsp::HandlerSink diagHandler; + dsp::stream dummy; + + ImGui::SymbolDiagram diag; + + bool showLines = false; +}; + +MOD_EXPORT void _INIT_() { + // Create default recording directory + json def = json({}); + config.setPath(core::args["root"].s() + "/kg_sstv_decoder_config.json"); + config.load(def); + config.enableAutoSave(); +} + +MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { + return new M17DecoderModule(name); +} + +MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { + delete (M17DecoderModule*)instance; +} + +MOD_EXPORT void _END_() { + config.disableAutoSave(); + config.save(); +} diff --git a/decoder_modules/m17_decoder/src/main.cpp b/decoder_modules/m17_decoder/src/main.cpp index 068a2184..b02a5699 100644 --- a/decoder_modules/m17_decoder/src/main.cpp +++ b/decoder_modules/m17_decoder/src/main.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -296,7 +295,7 @@ private: MOD_EXPORT void _INIT_() { // Create default recording directory json def = json({}); - config.setPath(options::opts.root + "/m17_decoder_config.json"); + config.setPath(core::args["root"].s() + "/m17_decoder_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/decoder_modules/meteor_demodulator/src/main.cpp b/decoder_modules/meteor_demodulator/src/main.cpp index 1f675880..016a2ef8 100644 --- a/decoder_modules/meteor_demodulator/src/main.cpp +++ b/decoder_modules/meteor_demodulator/src/main.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -244,14 +243,15 @@ private: MOD_EXPORT void _INIT_() { // Create default recording directory - if (!std::filesystem::exists(options::opts.root + "/recordings")) { + std::string root = core::args["root"]; + if (!std::filesystem::exists(root + "/recordings")) { spdlog::warn("Recordings directory does not exist, creating it"); - if (!std::filesystem::create_directory(options::opts.root + "/recordings")) { + if (!std::filesystem::create_directory(root + "/recordings")) { spdlog::error("Could not create recordings directory"); } } json def = json({}); - config.setPath(options::opts.root + "/meteor_demodulator_config.json"); + config.setPath(root + "/meteor_demodulator_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/decoder_modules/radio/src/main.cpp b/decoder_modules/radio/src/main.cpp index d65ff421..680dec3b 100644 --- a/decoder_modules/radio/src/main.cpp +++ b/decoder_modules/radio/src/main.cpp @@ -1,5 +1,4 @@ #include "radio_module.h" -#include SDRPP_MOD_INFO{ /* Name: */ "radio", @@ -11,7 +10,7 @@ SDRPP_MOD_INFO{ MOD_EXPORT void _INIT_() { json def = json({}); - config.setPath(options::opts.root + "/radio_config.json"); + config.setPath(core::args["root"].s() + "/radio_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/decoder_modules/weather_sat_decoder/src/main.cpp b/decoder_modules/weather_sat_decoder/src/main.cpp index e83f868d..c93d07de 100644 --- a/decoder_modules/weather_sat_decoder/src/main.cpp +++ b/decoder_modules/weather_sat_decoder/src/main.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include diff --git a/misc_modules/frequency_manager/src/main.cpp b/misc_modules/frequency_manager/src/main.cpp index 98dcd5e8..9c244613 100644 --- a/misc_modules/frequency_manager/src/main.cpp +++ b/misc_modules/frequency_manager/src/main.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -831,7 +830,7 @@ MOD_EXPORT void _INIT_() { def["lists"]["General"]["showOnWaterfall"] = true; def["lists"]["General"]["bookmarks"] = json::object(); - config.setPath(options::opts.root + "/frequency_manager_config.json"); + config.setPath(core::args["root"].s() + "/frequency_manager_config.json"); config.load(def); config.enableAutoSave(); diff --git a/misc_modules/recorder/src/main.cpp b/misc_modules/recorder/src/main.cpp index 4684e6cc..a68f28bb 100644 --- a/misc_modules/recorder/src/main.cpp +++ b/misc_modules/recorder/src/main.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -29,11 +28,6 @@ SDRPP_MOD_INFO{ ConfigManager config; -std::string expandString(std::string input) { - input = std::regex_replace(input, std::regex("%ROOT%"), options::opts.root); - return std::regex_replace(input, std::regex("//"), "/"); -} - std::string genFileName(std::string prefix, bool isVfo, std::string name = "") { time_t now = time(0); tm* ltm = localtime(&now); @@ -52,6 +46,8 @@ public: RecorderModule(std::string name) : folderSelect("%ROOT%/recordings") { this->name = name; + root = core::args["root"]; + // Load config config.acquire(); bool created = false; @@ -462,6 +458,11 @@ private: } } + std::string expandString(std::string input) { + input = std::regex_replace(input, std::regex("%ROOT%"), root); + return std::regex_replace(input, std::regex("//"), "/"); + } + std::string name; bool enabled = true; @@ -496,6 +497,7 @@ private: std::string streamNamesTxt; int streamId = 0; std::string selectedStreamName = ""; + std::string root; // Baseband path dsp::stream basebandStream; @@ -516,14 +518,15 @@ struct RecorderContext_t { MOD_EXPORT void _INIT_() { // Create default recording directory - if (!std::filesystem::exists(options::opts.root + "/recordings")) { + std::string root = core::args["root"]; + if (!std::filesystem::exists(root + "/recordings")) { spdlog::warn("Recordings directory does not exist, creating it"); - if (!std::filesystem::create_directory(options::opts.root + "/recordings")) { + if (!std::filesystem::create_directory(root + "/recordings")) { spdlog::error("Could not create recordings directory"); } } json def = json({}); - config.setPath(options::opts.root + "/recorder_config.json"); + config.setPath(root + "/recorder_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/misc_modules/rigctl_server/src/main.cpp b/misc_modules/rigctl_server/src/main.cpp index 504e86d0..4740f2df 100644 --- a/misc_modules/rigctl_server/src/main.cpp +++ b/misc_modules/rigctl_server/src/main.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -734,7 +733,7 @@ private: }; MOD_EXPORT void _INIT_() { - config.setPath(options::opts.root + "/rigctl_server_config.json"); + config.setPath(core::args["root"].s() + "/rigctl_server_config.json"); config.load(json::object()); config.enableAutoSave(); } diff --git a/sink_modules/android_audio_sink/src/main.cpp b/sink_modules/android_audio_sink/src/main.cpp index a4837dd2..45627ef5 100644 --- a/sink_modules/android_audio_sink/src/main.cpp +++ b/sink_modules/android_audio_sink/src/main.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -175,7 +174,7 @@ private: MOD_EXPORT void _INIT_() { json def = json({}); - config.setPath(options::opts.root + "/audio_sink_config.json"); + config.setPath(core::args["root"].s() + "/audio_sink_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/sink_modules/audio_sink/src/main.cpp b/sink_modules/audio_sink/src/main.cpp index 257cf4c2..2be37011 100644 --- a/sink_modules/audio_sink/src/main.cpp +++ b/sink_modules/audio_sink/src/main.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -276,7 +275,7 @@ private: MOD_EXPORT void _INIT_() { json def = json({}); - config.setPath(options::opts.root + "/audio_sink_config.json"); + config.setPath(core::args["root"].s() + "/audio_sink_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/sink_modules/network_sink/src/main.cpp b/sink_modules/network_sink/src/main.cpp index f642afc8..c59a7314 100644 --- a/sink_modules/network_sink/src/main.cpp +++ b/sink_modules/network_sink/src/main.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -339,7 +338,7 @@ private: MOD_EXPORT void _INIT_() { json def = json({}); - config.setPath(options::opts.root + "/network_sink_config.json"); + config.setPath(core::args["root"].s() + "/network_sink_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/sink_modules/new_portaudio_sink/src/main.cpp b/sink_modules/new_portaudio_sink/src/main.cpp index 864df852..5829ea65 100644 --- a/sink_modules/new_portaudio_sink/src/main.cpp +++ b/sink_modules/new_portaudio_sink/src/main.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -424,7 +423,7 @@ private: }; MOD_EXPORT void _INIT_() { - config.setPath(options::opts.root + "/new_audio_sink_config.json"); + config.setPath(core::args["root"].s() + "/new_audio_sink_config.json"); config.load(json::object()); config.enableAutoSave(); } diff --git a/source_modules/airspy_source/src/main.cpp b/source_modules/airspy_source/src/main.cpp index 6a9d0493..e338f247 100644 --- a/source_modules/airspy_source/src/main.cpp +++ b/source_modules/airspy_source/src/main.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -610,7 +609,7 @@ MOD_EXPORT void _INIT_() { json def = json({}); def["devices"] = json({}); def["device"] = ""; - config.setPath(options::opts.root + "/airspy_config.json"); + config.setPath(core::args["root"].s() + "/airspy_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/source_modules/airspyhf_source/src/main.cpp b/source_modules/airspyhf_source/src/main.cpp index 65b8a7e0..e5da7810 100644 --- a/source_modules/airspyhf_source/src/main.cpp +++ b/source_modules/airspyhf_source/src/main.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -407,7 +406,7 @@ MOD_EXPORT void _INIT_() { json def = json({}); def["devices"] = json({}); def["device"] = ""; - config.setPath(options::opts.root + "/airspyhf_config.json"); + config.setPath(core::args["root"].s() + "/airspyhf_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/source_modules/bladerf_source/src/main.cpp b/source_modules/bladerf_source/src/main.cpp index b23fd4f3..f368fb38 100644 --- a/source_modules/bladerf_source/src/main.cpp +++ b/source_modules/bladerf_source/src/main.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -610,7 +609,7 @@ MOD_EXPORT void _INIT_() { json def = json({}); def["devices"] = json({}); def["device"] = ""; - config.setPath(options::opts.root + "/bladerf_config.json"); + config.setPath(core::args["root"].s() + "/bladerf_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/source_modules/file_source/src/main.cpp b/source_modules/file_source/src/main.cpp index 5c23b2e6..0d104ae6 100644 --- a/source_modules/file_source/src/main.cpp +++ b/source_modules/file_source/src/main.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -28,7 +27,7 @@ public: FileSourceModule(std::string name) : fileSelect("", { "Wav IQ Files (*.wav)", "*.wav", "All Files", "*" }) { this->name = name; - if (options::opts.serverMode) { return; } + if (core::args["server"].b()) { return; } config.acquire(); fileSelect.setPath(config.conf["path"], true); @@ -200,7 +199,7 @@ private: MOD_EXPORT void _INIT_() { json def = json({}); def["path"] = ""; - config.setPath(options::opts.root + "/file_source_config.json"); + config.setPath(core::args["root"].s() + "/file_source_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/source_modules/hackrf_source/src/main.cpp b/source_modules/hackrf_source/src/main.cpp index 2c549103..28f379ab 100644 --- a/source_modules/hackrf_source/src/main.cpp +++ b/source_modules/hackrf_source/src/main.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #ifndef __ANDROID__ @@ -416,7 +415,7 @@ MOD_EXPORT void _INIT_() { json def = json({}); def["devices"] = json({}); def["device"] = ""; - config.setPath(options::opts.root + "/hackrf_config.json"); + config.setPath(core::args["root"].s() + "/hackrf_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/source_modules/limesdr_source/src/main.cpp b/source_modules/limesdr_source/src/main.cpp index 762ac8c5..0d083eae 100644 --- a/source_modules/limesdr_source/src/main.cpp +++ b/source_modules/limesdr_source/src/main.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -522,7 +521,7 @@ MOD_EXPORT void _INIT_() { json def = json({}); def["devices"] = json({}); def["device"] = ""; - config.setPath(options::opts.root + "/limesdr_config.json"); + config.setPath(core::args["root"].s() + "/limesdr_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/source_modules/plutosdr_source/src/main.cpp b/source_modules/plutosdr_source/src/main.cpp index d0720c56..46a16f65 100644 --- a/source_modules/plutosdr_source/src/main.cpp +++ b/source_modules/plutosdr_source/src/main.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -298,7 +297,7 @@ MOD_EXPORT void _INIT_() { defConf["sampleRate"] = 4000000.0f; defConf["gainMode"] = 0; defConf["gain"] = 0.0f; - config.setPath(options::opts.root + "/plutosdr_source_config.json"); + config.setPath(core::args["root"].s() + "/plutosdr_source_config.json"); config.load(defConf); config.enableAutoSave(); } diff --git a/source_modules/rfspace_source/src/main.cpp b/source_modules/rfspace_source/src/main.cpp index b5c1d584..b3e42d4e 100644 --- a/source_modules/rfspace_source/src/main.cpp +++ b/source_modules/rfspace_source/src/main.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -326,7 +325,7 @@ MOD_EXPORT void _INIT_() { def["hostname"] = "192.168.0.111"; def["port"] = 50000; def["devices"] = json::object(); - config.setPath(options::opts.root + "/rfspace_source_config.json"); + config.setPath(core::args["root"].s() + "/rfspace_source_config.json"); config.load(def); config.enableAutoSave(); diff --git a/source_modules/rtl_sdr_source/src/main.cpp b/source_modules/rtl_sdr_source/src/main.cpp index eb2180e8..baa6a840 100644 --- a/source_modules/rtl_sdr_source/src/main.cpp +++ b/source_modules/rtl_sdr_source/src/main.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -60,6 +59,8 @@ public: RTLSDRSourceModule(std::string name) { this->name = name; + serverMode = core::args["server"]; + sampleRate = sampleRates[0]; handler.ctx = this; @@ -425,7 +426,7 @@ private: SmGui::ForceSync(); // TODO: FIND ANOTHER WAY - if (options::opts.serverMode) { + if (serverMode) { if (SmGui::SliderInt(CONCAT("##_rtlsdr_gain_", _this->name), &_this->gainId, 0, _this->gainList.size() - 1, SmGui::FMT_STR_NONE)) { _this->updateGainTxt(); if (_this->running) { @@ -539,6 +540,7 @@ private: int srId = 0; int devCount = 0; std::thread workerThread; + bool serverMode = false; #ifdef __ANDROID__ int devFd = -1; @@ -571,7 +573,7 @@ MOD_EXPORT void _INIT_() { json def = json({}); def["devices"] = json({}); def["device"] = 0; - config.setPath(options::opts.root + "/rtl_sdr_config.json"); + config.setPath(core::args["root"].s() + "/rtl_sdr_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/source_modules/rtl_tcp_source/src/main.cpp b/source_modules/rtl_tcp_source/src/main.cpp index a3649a0c..5e898667 100644 --- a/source_modules/rtl_tcp_source/src/main.cpp +++ b/source_modules/rtl_tcp_source/src/main.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -337,7 +336,7 @@ private: }; MOD_EXPORT void _INIT_() { - config.setPath(options::opts.root + "/rtl_tcp_config.json"); + config.setPath(core::args["root"].s() + "/rtl_tcp_config.json"); json defConf; defConf["host"] = "localhost"; defConf["port"] = 1234; diff --git a/source_modules/sddc_source/src/main.cpp b/source_modules/sddc_source/src/main.cpp index 142b3a3d..55f6ae47 100644 --- a/source_modules/sddc_source/src/main.cpp +++ b/source_modules/sddc_source/src/main.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -29,7 +28,7 @@ public: AirspyHFSourceModule(std::string name) { this->name = name; - if (options::opts.serverMode) { return; } + if (core::args["server"].b()) { return; } sampleRate = 768000.0; @@ -229,7 +228,7 @@ MOD_EXPORT void _INIT_() { json def = json({}); def["devices"] = json({}); def["device"] = ""; - config.setPath(options::opts.root + "/sddc_config.json"); + config.setPath(core::args["root"].s() + "/sddc_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/source_modules/sdrplay_source/src/main.cpp b/source_modules/sdrplay_source/src/main.cpp index c0f7116d..de7cc72b 100644 --- a/source_modules/sdrplay_source/src/main.cpp +++ b/source_modules/sdrplay_source/src/main.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -1207,7 +1206,7 @@ MOD_EXPORT void _INIT_() { json def = json({}); def["devices"] = json({}); def["device"] = ""; - config.setPath(options::opts.root + "/sdrplay_config.json"); + config.setPath(core::args["root"].s() + "/sdrplay_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/source_modules/sdrpp_server_source/src/main.cpp b/source_modules/sdrpp_server_source/src/main.cpp index d7a737f2..3493f1c6 100644 --- a/source_modules/sdrpp_server_source/src/main.cpp +++ b/source_modules/sdrpp_server_source/src/main.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -30,7 +29,7 @@ public: this->name = name; // Yeah no server-ception, sorry... - if (options::opts.serverMode) { return; } + if (core::args["server"].b()) { return; } // Initialize lists sampleTypeList.define("Int8", dsp::PCM_TYPE_I8); @@ -282,7 +281,7 @@ MOD_EXPORT void _INIT_() { def["hostname"] = "localhost"; def["port"] = 5259; def["servers"] = json::object(); - config.setPath(options::opts.root + "/sdrpp_server_source_config.json"); + config.setPath(core::args["root"].s() + "/sdrpp_server_source_config.json"); config.load(def); config.enableAutoSave(); } diff --git a/source_modules/soapy_source/src/main.cpp b/source_modules/soapy_source/src/main.cpp index 64941337..c8ad57ef 100644 --- a/source_modules/soapy_source/src/main.cpp +++ b/source_modules/soapy_source/src/main.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -525,7 +524,7 @@ private: }; MOD_EXPORT void _INIT_() { - config.setPath(options::opts.root + "/soapy_source_config.json"); + config.setPath(core::args["root"].s() + "/soapy_source_config.json"); json defConf; defConf["device"] = ""; defConf["devices"] = json({}); diff --git a/source_modules/spyserver_source/src/main.cpp b/source_modules/spyserver_source/src/main.cpp index dcfe558f..83cc03db 100644 --- a/source_modules/spyserver_source/src/main.cpp +++ b/source_modules/spyserver_source/src/main.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -308,7 +307,7 @@ MOD_EXPORT void _INIT_() { def["hostname"] = "localhost"; def["port"] = 5555; def["devices"] = json::object(); - config.setPath(options::opts.root + "/spyserver_config.json"); + config.setPath(core::args["root"].s() + "/spyserver_config.json"); config.load(def); config.enableAutoSave();