From 74b9d13360e41c0aa2a210ab02256fef4dc209fe Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Fri, 21 Jan 2022 20:22:13 +0100 Subject: [PATCH] SDR++ server beta :) --- .gitignore | 1 + CMakeLists.txt | 5 + core/src/core.cpp | 7 +- core/src/dsp/compression.h | 86 +- core/src/dsp/link.h | 54 ++ core/src/gui/main_window.cpp | 2 +- core/src/gui/smgui.cpp | 745 ++++++++++++++++++ core/src/gui/smgui.h | 146 ++++ core/src/gui/style.cpp | 4 + core/src/gui/style.h | 1 + core/src/options.cpp | 12 + core/src/options.h | 2 + core/src/server.cpp | 357 ++++++++- core/src/server.h | 26 +- core/src/server_protocol.h | 49 ++ core/src/signal_path/source.cpp | 10 +- core/src/utils/networking.cpp | 64 +- core/src/utils/new_networking.cpp | 2 + core/src/utils/new_networking.h | 95 +++ source_modules/airspy_source/src/main.cpp | 96 +-- source_modules/airspyhf_source/src/main.cpp | 35 +- source_modules/bladerf_source/src/main.cpp | 51 +- source_modules/file_source/src/main.cpp | 2 + source_modules/hackrf_source/src/main.cpp | 46 +- source_modules/limesdr_source/src/main.cpp | 45 +- source_modules/plutosdr_source/src/main.cpp | 37 +- source_modules/rfspace_source/src/main.cpp | 95 +-- source_modules/rtl_sdr_source/src/main.cpp | 87 +- source_modules/rtl_tcp_source/src/main.cpp | 49 +- source_modules/sddc_source/src/main.cpp | 2 + source_modules/sdrplay_source/src/main.cpp | 228 +++--- .../sdrpp_server_source/CMakeLists.txt | 25 + .../sdrpp_server_source/src/main.cpp | 279 +++++++ .../src/sdrpp_server_client.cpp | 233 ++++++ .../src/sdrpp_server_client.h | 134 ++++ source_modules/soapy_source/src/main.cpp | 67 +- source_modules/spyserver_source/src/main.cpp | 50 +- 37 files changed, 2731 insertions(+), 498 deletions(-) create mode 100644 core/src/dsp/link.h create mode 100644 core/src/gui/smgui.cpp create mode 100644 core/src/gui/smgui.h create mode 100644 core/src/server_protocol.h create mode 100644 core/src/utils/new_networking.cpp create mode 100644 core/src/utils/new_networking.h create mode 100644 source_modules/sdrpp_server_source/CMakeLists.txt create mode 100644 source_modules/sdrpp_server_source/src/main.cpp create mode 100644 source_modules/sdrpp_server_source/src/sdrpp_server_client.cpp create mode 100644 source_modules/sdrpp_server_source/src/sdrpp_server_client.h diff --git a/.gitignore b/.gitignore index 53cf5307..10ad3722 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ build/ *.wav .DS_Store root_dev/ +root_dev_srv/ Folder.DotSettings.user CMakeSettings.json poggers_decoder diff --git a/CMakeLists.txt b/CMakeLists.txt index ad1754c3..788ce053 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ option(OPT_BUILD_FILE_SOURCE "Wav file source" ON) option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON) option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF) option(OPT_BUILD_SDDC_SOURCE "Build SDDC Source Module (Dependencies: libusb-1.0)" OFF) +option(OPT_BUILD_SDRPP_SERVER_SOURCE "Build SDR++ Server Source Module (no dependencies required)" ON) option(OPT_BUILD_RFSPACE_SOURCE "Build RFspace Source Module (no dependencies required)" ON) option(OPT_BUILD_RTL_SDR_SOURCE "Build RTL-SDR Source Module (Dependencies: librtlsdr)" ON) option(OPT_BUILD_RTL_TCP_SOURCE "Build RTL-TCP Source Module (no dependencies required)" ON) @@ -83,6 +84,10 @@ if (OPT_BUILD_SDDC_SOURCE) add_subdirectory("source_modules/sddc_source") endif (OPT_BUILD_SDDC_SOURCE) +if (OPT_BUILD_SDRPP_SERVER_SOURCE) +add_subdirectory("source_modules/sdrpp_server_source") +endif (OPT_BUILD_SDRPP_SERVER_SOURCE) + if (OPT_BUILD_RFSPACE_SOURCE) add_subdirectory("source_modules/rfspace_source") endif (OPT_BUILD_RFSPACE_SOURCE) diff --git a/core/src/core.cpp b/core/src/core.cpp index c68d978e..634eb725 100644 --- a/core/src/core.cpp +++ b/core/src/core.cpp @@ -1,3 +1,4 @@ +#include #include "imgui.h" #include "imgui_impl_glfw.h" #include "imgui_impl_opengl3.h" @@ -18,7 +19,6 @@ #include #include #include -#include #define STB_IMAGE_RESIZE_IMPLEMENTATION #include @@ -70,6 +70,9 @@ namespace core { GLFWwindow* window; void setInputSampleRate(double samplerate) { + // Forward this to the server + if (options::opts.serverMode) { server::setInputSampleRate(samplerate); return; } + sigpath::signalPath.sourceSampleRate = samplerate; double effectiveSr = samplerate / ((double)(1 << sigpath::signalPath.decimation)); // NOTE: Zoom controls won't work @@ -297,7 +300,7 @@ int sdrpp_main(int argc, char* argv[]) { core::configManager.release(true); - if (options::opts.serverMode) { return server_main(); } + if (options::opts.serverMode) { return server::main(); } core::configManager.acquire(); int winWidth = core::configManager.conf["windowSize"]["w"]; diff --git a/core/src/dsp/compression.h b/core/src/dsp/compression.h index ee0db3d7..5e882119 100644 --- a/core/src/dsp/compression.h +++ b/core/src/dsp/compression.h @@ -2,16 +2,16 @@ #include namespace dsp { + enum PCMType { + PCM_TYPE_I8, + PCM_TYPE_I16, + PCM_TYPE_F32 + }; + class DynamicRangeCompressor : public generic_block { public: DynamicRangeCompressor() {} - enum PCMType { - PCM_TYPE_I8, - PCM_TYPE_I16, - PCM_TYPE_F32 - }; - DynamicRangeCompressor(stream* in, PCMType pcmType) { init(in, pcmType); } void init(stream* in, PCMType pcmType) { @@ -41,44 +41,42 @@ namespace dsp { int run() { int count = _in->read(); if (count < 0) { return -1; } + PCMType type = _pcmType; - float* scaler = (float*)out.writeBuf; - void* dataBuf = &out.writeBuf[4]; + uint16_t* compressionType = (uint16_t*)out.writeBuf; + uint16_t* sampleType = (uint16_t*)&out.writeBuf[2]; + float* scaler = (float*)&out.writeBuf[4]; + void* dataBuf = &out.writeBuf[8]; - // If no dynamic range compression is to be done, just pass the data to the output with a null scaler - if (_pcmType == PCM_TYPE_F32) { + // Write options and leave blank space for compression + *compressionType = 0; + *sampleType = type; + + // If type is float32, no compression is needed + if (type == PCM_TYPE_F32) { *scaler = 0; memcpy(dataBuf, _in->readBuf, count * sizeof(complex_t)); _in->flush(); - if (!out.swap(4 + (count * sizeof(complex_t)))) { return -1; } + if (!out.swap(8 + (count * sizeof(complex_t)))) { return -1; } return count; } // Find maximum value - complex_t val; - float absre; - float absim; - float maxVal = 0; - for (int i = 0; i < count; i++) { - val = _in->readBuf[i]; - absre = fabsf(val.re); - absim = fabsf(val.im); - if (absre > maxVal) { maxVal = absre; } - if (absim > maxVal) { maxVal = absim; } - } + uint32_t maxIdx; + volk_32f_index_max_32u(&maxIdx, (float*)_in->readBuf, count * 2); + float maxVal = ((float*)_in->readBuf)[maxIdx]; + *scaler = maxVal; // Convert to the right type and send it out (sign bit determins pcm type) - if (_pcmType == PCM_TYPE_I8) { - *scaler = maxVal; + if (type == PCM_TYPE_I8) { volk_32f_s32f_convert_8i((int8_t*)dataBuf, (float*)_in->readBuf, 128.0f / maxVal, count * 2); _in->flush(); - if (!out.swap(4 + (count * sizeof(int8_t) * 2))) { return -1; } + if (!out.swap(8 + (count * sizeof(int8_t) * 2))) { return -1; } } - else if (_pcmType == PCM_TYPE_I16) { - *scaler = -maxVal; + else if (type == PCM_TYPE_I16) { volk_32f_s32f_convert_16i((int16_t*)dataBuf, (float*)_in->readBuf, 32768.0f / maxVal, count * 2); _in->flush(); - if (!out.swap(4 + (count * sizeof(int16_t) * 2))) { return -1; } + if (!out.swap(8 + (count * sizeof(int16_t) * 2))) { return -1; } } else { _in->flush(); @@ -121,31 +119,29 @@ namespace dsp { int count = _in->read(); if (count < 0) { return -1; } - float* scaler = (float*)_in->readBuf; - void* dataBuf = &_in->readBuf[4]; + uint16_t sampleType = *(uint16_t*)&_in->readBuf[2]; + float scaler = *(float*)&_in->readBuf[4]; + void* dataBuf = &_in->readBuf[8]; - // If the scaler is null, data is F32 - if (*scaler == 0) { - memcpy(out.writeBuf, dataBuf, count - 4); + if (sampleType == PCM_TYPE_F32) { + memcpy(out.writeBuf, dataBuf, count - 8); _in->flush(); - if (!out.swap((count - 4) / sizeof(complex_t))) { return -1; } - return count; + if (!out.swap((count - 8) / sizeof(complex_t))) { return -1; } } - - // Convert back to f32 from the pcm type - float absScale = fabsf(*scaler); - if (*scaler > 0) { - spdlog::warn("{0}", absScale); - int outCount = (count - 4) / (sizeof(int8_t) * 2); - volk_8i_s32f_convert_32f((float*)out.writeBuf, (int8_t*)dataBuf, 128.0f / absScale, outCount * 2); + else if (sampleType == PCM_TYPE_I16) { + int outCount = (count - 8) / (sizeof(int16_t) * 2); + volk_16i_s32f_convert_32f((float*)out.writeBuf, (int16_t*)dataBuf, 32768.0f / scaler, outCount * 2); + _in->flush(); + if (!out.swap(outCount)) { return -1; } + } + else if (sampleType == PCM_TYPE_I8) { + int outCount = (count - 8) / (sizeof(int8_t) * 2); + volk_8i_s32f_convert_32f((float*)out.writeBuf, (int8_t*)dataBuf, 128.0f / scaler, outCount * 2); _in->flush(); if (!out.swap(outCount)) { return -1; } } else { - int outCount = (count - 4) / (sizeof(int16_t) * 2); - volk_16i_s32f_convert_32f((float*)out.writeBuf, (int16_t*)dataBuf, 32768.0f / absScale, outCount * 2); _in->flush(); - if (!out.swap(outCount)) { return -1; } } return count; diff --git a/core/src/dsp/link.h b/core/src/dsp/link.h new file mode 100644 index 00000000..2695408c --- /dev/null +++ b/core/src/dsp/link.h @@ -0,0 +1,54 @@ +#pragma once +#include + +namespace dsp { + template + class Link : public generic_block> { + public: + Link() {} + + Link(stream* a, stream* b) { init(a, b); } + + void init(stream* in, stream* out) { + _in = in; + _out = out; + generic_block>::registerInput(_in); + generic_block>::registerOutput(_out); + generic_block>::_block_init = true; + } + + void setInput(stream* in) { + assert(generic_block>::_block_init); + std::lock_guard lck(generic_block>::ctrlMtx); + generic_block>::tempStop(); + generic_block>::unregisterInput(_in); + _in = in; + generic_block>::registerInput(_in); + generic_block>::tempStart(); + } + + void setOutput(stream* out) { + assert(generic_block>::_block_init); + std::lock_guard lck(generic_block>::ctrlMtx); + generic_block>::tempStop(); + generic_block>::unregisterOutput(_out); + _out = out; + generic_block>::registerOutput(_out); + generic_block>::tempStart(); + } + + int run() { + int count = _in->read(); + if (count < 0) { return -1; } + + memcpy(_out->writeBuf, _in->readBuf, count * sizeof(T)); + + _in->flush(); + return _out->swap(count) ? count : -1; + } + + private: + stream* _in; + stream* _out; + }; +} \ No newline at end of file diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index 68f8c4ea..de90d994 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -516,7 +516,7 @@ void MainWindow::draw() { } if (ImGui::CollapsingHeader("Debug")) { - ImGui::Text("Frame time: %.3f ms/frame", 1000.0 / ImGui::GetIO().Framerate); + ImGui::Text("Frame time: %.3f ms/frame", ImGui::GetIO().DeltaTime * 1000.0f); ImGui::Text("Framerate: %.1f FPS", ImGui::GetIO().Framerate); ImGui::Text("Center Frequency: %.0f Hz", gui::waterfall.getCenterFrequency()); ImGui::Text("Source name: %s", sourceName.c_str()); diff --git a/core/src/gui/smgui.cpp b/core/src/gui/smgui.cpp new file mode 100644 index 00000000..534758af --- /dev/null +++ b/core/src/gui/smgui.cpp @@ -0,0 +1,745 @@ +#include "smgui.h" +#include "style.h" +#include +#include +#include + +namespace SmGui { + std::map fmtStr = { + { FMT_STR_NONE, "" }, + { FMT_STR_INT_DEFAULT, "%d" }, + { FMT_STR_INT_DB, "%d dB" }, + { FMT_STR_FLOAT_DEFAULT, "%f" }, + { FMT_STR_FLOAT_NO_DECIMAL, "%.0f" }, + { FMT_STR_FLOAT_ONE_DECIMAL, "%.1f" }, + { FMT_STR_FLOAT_TWO_DECIMAL, "%.2f" }, + { FMT_STR_FLOAT_THREE_DECIMAL, "%.3f" }, + { FMT_STR_FLOAT_DB_NO_DECIMAL, "%.0f dB" }, + { FMT_STR_FLOAT_DB_ONE_DECIMAL, "%.1f dB" }, + { FMT_STR_FLOAT_DB_TWO_DECIMAL, "%.2f dB" }, + { FMT_STR_FLOAT_DB_THREE_DECIMAL, "%.3f dB" } + }; + + DrawList* rdl = NULL; + bool forceSyncForNext = false; + std::string diffId = ""; + DrawListElem diffValue; + bool nextItemFillWidth = false; + + std::string ImStrToString(const char* imstr) { + int len = 0; + const char* end = imstr; + while (*end) { end += strlen(end) + 1; } + return std::string(imstr, end); + } + + // Rec/Play functions + void setDiff(std::string id, SmGui::DrawListElem value) { + diffId = id; + diffValue = value; + } + + // TODO: Add getDiff function for client + + void startRecord(DrawList* dl) { + rdl = dl; + } + + void stopRecord() { + rdl = NULL; + } + + #define SET_DIFF_VOID(id) diffId = id; syncRequired = elem.forceSync; + #define SET_DIFF_BOOL(id, bo) diffId = id; diffValue.type = DRAW_LIST_ELEM_TYPE_BOOL; diffValue.b = bo; syncRequired = elem.forceSync; + #define SET_DIFF_INT(id, in) diffId = id; diffValue.type = DRAW_LIST_ELEM_TYPE_INT; diffValue.i = in; syncRequired = elem.forceSync; + #define SET_DIFF_FLOAT(id, fl) diffId = id; diffValue.type = DRAW_LIST_ELEM_TYPE_FLOAT; diffValue.f = fl; syncRequired = elem.forceSync; + #define SET_DIFF_STRING(id, st) diffId = id; diffValue.type = DRAW_LIST_ELEM_TYPE_STRING; diffValue.str = st; syncRequired = elem.forceSync; + + void DrawList::draw(std::string& diffId, DrawListElem& diffValue, bool& syncRequired) { + int elemCount = elements.size(); + nextItemFillWidth = false; + + for (int i = 0; i < elemCount;) { + // Get element + DrawListElem& elem = elements[i++]; + if (elem.type != DRAW_LIST_ELEM_TYPE_DRAW_STEP) { continue; } + + // Format calls + if (elem.step == DRAW_STEP_FILL_WIDTH) { FillWidth(); } + else if (elem.step == DRAW_STEP_SAME_LINE) { SameLine(); } + else if (elem.step == DRAW_STEP_BEGIN_DISABLED) { BeginDisabled(); } + else if (elem.step == DRAW_STEP_END_DISABLED) { EndDisabled(); } + + // Widget Calls + else if (elem.step == DRAW_STEP_COMBO) { + if (Combo(elements[i].str.c_str(), &elements[i+1].i, elements[i+2].str.c_str(), elements[i+3].i)) { + SET_DIFF_INT(elements[i].str, elements[i+1].i); + } + i += 4; + } + else if (elem.step == DRAW_STEP_BUTTON) { + if (Button(elements[i].str.c_str(), ImVec2(elements[i+1].f, elements[i+2].f))) { + SET_DIFF_VOID(elements[i].str); + } + i += 3; + } + else if (elem.step == DRAW_STEP_COLUMNS) { + Columns(elements[i].i, elements[i+1].str.c_str(), elements[i+2].b); + i += 3; + } + else if (elem.step == DRAW_STEP_NEXT_COLUMN) { NextColumn(); } + else if (elem.step == DRAW_STEP_RADIO_BUTTON) { + if (RadioButton(elements[i].str.c_str(), elements[i+1].b)) { + SET_DIFF_VOID(elements[i].str); + } + i += 2; + } + else if (elem.step == DRAW_STEP_BEGIN_GROUP) { BeginGroup(); } + else if (elem.step == DRAW_STEP_END_GROUP) { EndGroup(); } + else if (elem.step == DRAW_STEP_LEFT_LABEL) { + LeftLabel(elements[i].str.c_str()); + i++; + } + else if (elem.step == DRAW_STEP_SLIDER_INT) { + if (SliderInt(elements[i].str.c_str(), &elements[i+1].i, elements[i+2].i, elements[i+3].i, (FormatString)elements[i+4].i, elements[i+5].i)) { + SET_DIFF_INT(elements[i].str, elements[i+1].i); + } + i += 6; + } + else if (elem.step == DRAW_STEP_SLIDER_FLOAT_WITH_STEPS) { + if (SliderFloatWithSteps(elements[i].str.c_str(), &elements[i+1].f, elements[i+2].f, elements[i+3].f, elements[i+4].f, (FormatString)elements[i+5].i)) { + SET_DIFF_FLOAT(elements[i].str, elements[i+1].f); + } + i += 6; + } + else if (elem.step == DRAW_STEP_INPUT_INT) { + if (InputInt(elements[i].str.c_str(), &elements[i+1].i, elements[i+2].i, elements[i+3].i, elements[i+4].i)) { + SET_DIFF_INT(elements[i].str, elements[i+1].i); + } + i += 5; + } + else if (elem.step == DRAW_STEP_CHECKBOX) { + if (Checkbox(elements[i].str.c_str(), &elements[i+1].b)) { + SET_DIFF_BOOL(elements[i].str, elements[i+1].b); + } + i += 2; + } + else if (elem.step == DRAW_STEP_SLIDER_FLOAT) { + if (SliderFloat(elements[i].str.c_str(), &elements[i+1].f, elements[i+2].f, elements[i+3].f, (FormatString)elements[i+4].i, elements[i+5].i)) { + SET_DIFF_FLOAT(elements[i].str, elements[i+1].f); + } + i += 6; + } + else if (elem.step == DRAW_STEP_INPUT_TEXT) { + char tmpBuf[4096]; + strcpy(tmpBuf, elements[i+1].str.c_str()); + if (InputText(elements[i].str.c_str(), tmpBuf, elements[i+2].i, elements[i+3].i)) { + elements[i+1].str = tmpBuf; + SET_DIFF_STRING(elements[i].str, tmpBuf); + } + i += 4; + } + else if (elem.step == DRAW_STEP_TEXT) { + Text(elements[i].str.c_str()); + i++; + } + else if (elem.step == DRAW_STEP_TEXT_COLORED) { + TextColored(ImVec4(elements[i].f, elements[i+1].f, elements[i+2].f, elements[i+3].f), elements[i+4].str.c_str()); + i += 5; + } + else if (elem.step == DRAW_STEP_OPEN_POPUP) { + OpenPopup(elements[i].str.c_str(), elements[i+1].i); + i += 2; + } + else if (elem.step == DRAW_STEP_BEGIN_POPUP) { + gui::mainWindow.lockWaterfallControls = true; + if (!BeginPopup(elements[i].str.c_str(), elements[i+1].i)) { + i += 2; + while (i < elemCount && !(elements[i].type == DRAW_LIST_ELEM_TYPE_DRAW_STEP && elements[i].step == DRAW_STEP_END_POPUP)) { i++; } + i++; + } + else { + i += 2; + } + } + else if (elem.step == DRAW_STEP_END_POPUP) { + EndPopup(); + } + else if (elem.step == DRAW_STEP_BEGIN_TABLE) { + if (!BeginTable(elements[i].str.c_str(), elements[i+1].i, elements[i+2].i, ImVec2(elements[i+3].f, elements[i+4].f), elements[i+5].f)) { + i += 6; + while (i < elemCount && !(elements[i].type == DRAW_LIST_ELEM_TYPE_DRAW_STEP && elements[i].step == DRAW_STEP_END_TABLE)) { i++; } + i++; + } + else { + i += 6; + } + } + else if (elem.step == DRAW_STEP_END_TABLE) { + EndTable(); + } + else if (elem.step == DRAW_STEP_TABLE_NEXT_ROW) { + TableNextRow(elements[i].i, elements[i+1].f); + i += 2; + } + else if (elem.step == DRAW_STEP_TABLE_SET_COLUMN_INDEX) { + TableSetColumnIndex(elements[i].i); + i++; + } + else if (elem.step == DRAW_STEP_SET_NEXT_ITEM_WIDTH) { + SetNextItemWidth(elements[i].f); + i++; + } + else { + spdlog::error("Invalid widget in Drawlist"); + } + + if (elem.step != DRAW_STEP_FILL_WIDTH) { nextItemFillWidth = false; } + } + } + + // Drawlist stuff + void DrawList::pushStep(DrawStep step, bool forceSync) { + DrawListElem elem; + elem.type = DRAW_LIST_ELEM_TYPE_DRAW_STEP; + elem.step = step; + elem.forceSync = forceSync; + elements.push_back(elem); + } + + void DrawList::pushBool(bool b) { + DrawListElem elem; + elem.type = DRAW_LIST_ELEM_TYPE_BOOL; + elem.b = b; + elements.push_back(elem); + } + + void DrawList::pushInt(int i) { + DrawListElem elem; + elem.type = DRAW_LIST_ELEM_TYPE_INT; + elem.i = i; + elements.push_back(elem); + } + + void DrawList::pushFloat(float f) { + DrawListElem elem; + elem.type = DRAW_LIST_ELEM_TYPE_FLOAT; + elem.f = f; + elements.push_back(elem); + } + + void DrawList::pushString(std::string str) { + DrawListElem elem; + elem.type = DRAW_LIST_ELEM_TYPE_STRING; + elem.str = str; + elements.push_back(elem); + } + + int DrawList::loadItem(DrawListElem& elem, uint8_t* data, int len) { + // Get type + int i = 0; + elem.type = (DrawListElemType)data[i++]; + + // Read data depending on type + if (elem.type == DRAW_LIST_ELEM_TYPE_DRAW_STEP && len >= 2) { + elem.step = (DrawStep)data[i++]; + elem.forceSync = data[i++]; + } + else if (elem.type == DRAW_LIST_ELEM_TYPE_BOOL && len-- >= 1) { + elem.b = data[i++]; + } + else if (elem.type == DRAW_LIST_ELEM_TYPE_INT && len >= 4) { + elem.i = *(int*)&data[i]; + i += 4; + } + else if (elem.type == DRAW_LIST_ELEM_TYPE_FLOAT && len >= 4) { + elem.f = *(float*)&data[i]; + i += 4; + } + else if (elem.type == DRAW_LIST_ELEM_TYPE_STRING && len >= 2) { + uint16_t slen = *(uint16_t*)&data[i]; + if (len < slen + 2) { return -1; } + elem.str = std::string(&data[i + 2], &data[i + 2 + slen]); + i += slen + 2; + } + else { + return -1; + } + + return i; + } + + int DrawList::load(void* data, int len) { + uint8_t* buf = (uint8_t*)data; + elements.clear(); + int i = 0; + + // Load all entries + while (len > 0) { + // Load entry type + DrawListElem elem; + int consumed = loadItem(elem, &buf[i], len); + if (consumed < 0) { return -1; } + i += consumed; + len -= consumed; + + // Add element to list + elements.push_back(elem); + } + + // Validate and clear if invalid + if (!validate()) { + spdlog::error("Drawlist validation failed"); + return -1; + } + + return i; + } + + int DrawList::storeItem(DrawListElem& elem, void* data, int len) { + // Check size requirement + uint8_t* buf = (uint8_t*)data; + if (len < 1) { return -1; } + int i = 0; + len--; + + // Save entry type + buf[i++] = elem.type; + + // Check type and save data accordingly + if (elem.type == DRAW_LIST_ELEM_TYPE_DRAW_STEP && len >= 2) { + buf[i++] = elem.step; + buf[i++] = elem.forceSync; + } + else if (elem.type == DRAW_LIST_ELEM_TYPE_BOOL && len >= 1) { + buf[i++] = elem.b; + } + else if (elem.type == DRAW_LIST_ELEM_TYPE_INT && len >= 4) { + *(int*)&buf[i] = elem.i; + i += 4; + } + else if (elem.type == DRAW_LIST_ELEM_TYPE_FLOAT && len >= 4) { + *(float*)&buf[i] = elem.f; + i += 4; + } + else if (elem.type == DRAW_LIST_ELEM_TYPE_STRING) { + int slen = elem.str.size(); + if (len < slen + 2) { return -1; } + *(uint16_t*)&buf[i] = slen; + memcpy(&buf[i + 2], elem.str.c_str(), slen); + i += slen + 2; + } + else { + return -1; + } + + return i; + } + + int DrawList::store(void* data, int len) { + uint8_t* buf = (uint8_t*)data; + int i = 0; + + // Iterate through all element and write the data in the buffer + for (auto& elem : elements) { + int count = storeItem(elem, &buf[i], len); + if (count < 0) { return -1; } + i += count; + len -= count; + } + + return i; + } + + int DrawList::getItemSize(DrawListElem& elem) { + if (elem.type == DRAW_LIST_ELEM_TYPE_DRAW_STEP) { return 3; } + else if (elem.type == DRAW_LIST_ELEM_TYPE_BOOL) { return 2; } + else if (elem.type == DRAW_LIST_ELEM_TYPE_INT) { return 5; } + else if (elem.type == DRAW_LIST_ELEM_TYPE_FLOAT) { return 5; } + else if (elem.type == DRAW_LIST_ELEM_TYPE_STRING) { return 3 + elem.str.size(); } + return -1; + } + + int DrawList::getSize() { + int size = 0; + + // Iterate through all element to add up the total size + for (auto& elem : elements) { + size += getItemSize(elem); + } + + return size; + } + + bool DrawList::checkTypes(int firstId, int n, ...) { + va_list args; + va_start(args, n); + + // Check if enough elements are left + if (elements.size() - firstId < n) { return false; } + + // Check the type of each element + for (int i = 0; i < n; i++) { + if (va_arg(args, DrawListElemType) != elements[firstId + i].type) { + va_end(args); + return false; + } + } + + va_end(args); + return true; + } + + #define VALIDATE_WIDGET(n, ws, ...) if (step == ws) { if (!checkTypes(i, n, __VA_ARGS__)) { return false; }; i += n; } + #define E_VALIDATE_WIDGET(n, ws, ...) else VALIDATE_WIDGET(n, ws, __VA_ARGS__) + + bool DrawList::validate() { + int count = elements.size(); + for (int i = 0; i < count;) { + if (elements[i].type != DRAW_LIST_ELEM_TYPE_DRAW_STEP) { return false; } + DrawStep step = elements[i++].step; + + VALIDATE_WIDGET(4, DRAW_STEP_COMBO, DRAW_LIST_ELEM_TYPE_STRING, DRAW_LIST_ELEM_TYPE_INT, DRAW_LIST_ELEM_TYPE_STRING, DRAW_LIST_ELEM_TYPE_INT) + E_VALIDATE_WIDGET(3, DRAW_STEP_BUTTON, DRAW_LIST_ELEM_TYPE_STRING, DRAW_LIST_ELEM_TYPE_FLOAT, DRAW_LIST_ELEM_TYPE_FLOAT) + E_VALIDATE_WIDGET(3, DRAW_STEP_COLUMNS, DRAW_LIST_ELEM_TYPE_INT, DRAW_LIST_ELEM_TYPE_STRING, DRAW_LIST_ELEM_TYPE_BOOL) + E_VALIDATE_WIDGET(2, DRAW_STEP_RADIO_BUTTON, DRAW_LIST_ELEM_TYPE_STRING, DRAW_LIST_ELEM_TYPE_BOOL) + E_VALIDATE_WIDGET(1, DRAW_STEP_LEFT_LABEL, DRAW_LIST_ELEM_TYPE_STRING) + E_VALIDATE_WIDGET(6, DRAW_STEP_SLIDER_INT, DRAW_LIST_ELEM_TYPE_STRING, DRAW_LIST_ELEM_TYPE_INT, DRAW_LIST_ELEM_TYPE_INT, + DRAW_LIST_ELEM_TYPE_INT, DRAW_LIST_ELEM_TYPE_INT, DRAW_LIST_ELEM_TYPE_INT) + E_VALIDATE_WIDGET(6, DRAW_STEP_SLIDER_FLOAT_WITH_STEPS, DRAW_LIST_ELEM_TYPE_STRING, DRAW_LIST_ELEM_TYPE_FLOAT, DRAW_LIST_ELEM_TYPE_FLOAT, + DRAW_LIST_ELEM_TYPE_FLOAT, DRAW_LIST_ELEM_TYPE_FLOAT, DRAW_LIST_ELEM_TYPE_INT) + E_VALIDATE_WIDGET(5, DRAW_STEP_INPUT_INT, DRAW_LIST_ELEM_TYPE_STRING, DRAW_LIST_ELEM_TYPE_INT, DRAW_LIST_ELEM_TYPE_INT, + DRAW_LIST_ELEM_TYPE_INT, DRAW_LIST_ELEM_TYPE_INT) + E_VALIDATE_WIDGET(2, DRAW_STEP_CHECKBOX, DRAW_LIST_ELEM_TYPE_STRING, DRAW_LIST_ELEM_TYPE_BOOL) + E_VALIDATE_WIDGET(6, DRAW_STEP_SLIDER_FLOAT, DRAW_LIST_ELEM_TYPE_STRING, DRAW_LIST_ELEM_TYPE_FLOAT, DRAW_LIST_ELEM_TYPE_FLOAT, + DRAW_LIST_ELEM_TYPE_FLOAT, DRAW_LIST_ELEM_TYPE_INT, DRAW_LIST_ELEM_TYPE_INT) + E_VALIDATE_WIDGET(4, DRAW_STEP_INPUT_TEXT, DRAW_LIST_ELEM_TYPE_STRING, DRAW_LIST_ELEM_TYPE_STRING, DRAW_LIST_ELEM_TYPE_INT, DRAW_LIST_ELEM_TYPE_INT) + E_VALIDATE_WIDGET(1, DRAW_STEP_TEXT, DRAW_LIST_ELEM_TYPE_STRING) + E_VALIDATE_WIDGET(5, DRAW_STEP_TEXT_COLORED, DRAW_LIST_ELEM_TYPE_FLOAT, DRAW_LIST_ELEM_TYPE_FLOAT, DRAW_LIST_ELEM_TYPE_FLOAT, + DRAW_LIST_ELEM_TYPE_FLOAT, DRAW_LIST_ELEM_TYPE_STRING) + E_VALIDATE_WIDGET(2, DRAW_STEP_OPEN_POPUP, DRAW_LIST_ELEM_TYPE_STRING, DRAW_LIST_ELEM_TYPE_INT) + E_VALIDATE_WIDGET(2, DRAW_STEP_BEGIN_POPUP, DRAW_LIST_ELEM_TYPE_STRING, DRAW_LIST_ELEM_TYPE_INT) + E_VALIDATE_WIDGET(6, DRAW_STEP_BEGIN_TABLE, DRAW_LIST_ELEM_TYPE_STRING, DRAW_LIST_ELEM_TYPE_INT, DRAW_LIST_ELEM_TYPE_INT, + DRAW_LIST_ELEM_TYPE_FLOAT, DRAW_LIST_ELEM_TYPE_FLOAT, DRAW_LIST_ELEM_TYPE_FLOAT) + E_VALIDATE_WIDGET(2, DRAW_STEP_TABLE_NEXT_ROW, DRAW_LIST_ELEM_TYPE_INT, DRAW_LIST_ELEM_TYPE_FLOAT) + E_VALIDATE_WIDGET(1, DRAW_STEP_TABLE_SET_COLUMN_INDEX, DRAW_LIST_ELEM_TYPE_INT) + E_VALIDATE_WIDGET(1, DRAW_STEP_SET_NEXT_ITEM_WIDTH, DRAW_LIST_ELEM_TYPE_FLOAT) + } + + return true; + } + + // Signaling functions + void ForceSync() { + forceSyncForNext = true; + } + + // Format functions + void FillWidth() { + if (!options::opts.serverMode) { + nextItemFillWidth = true; + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth()); + return; + } + if (rdl) { rdl->pushStep(DRAW_STEP_FILL_WIDTH, false); } + } + + void SameLine() { + if (!options::opts.serverMode) { ImGui::SameLine(); return; } + if (rdl) { rdl->pushStep(DRAW_STEP_SAME_LINE, false); } + } + + void BeginDisabled() { + if (!options::opts.serverMode) { style::beginDisabled(); return; } + if (rdl) { rdl->pushStep(DRAW_STEP_BEGIN_DISABLED, false); } + } + + void EndDisabled() { + if (!options::opts.serverMode) { style::endDisabled(); return; } + if (rdl) { rdl->pushStep(DRAW_STEP_END_DISABLED, false); } + } + + + // Widget functions + bool Combo(const char *label, int *current_item, const char *items_separated_by_zeros, int popup_max_height_in_items) { + if (!options::opts.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); + rdl->pushInt(*current_item); + rdl->pushString(ImStrToString(items_separated_by_zeros)); + rdl->pushInt(popup_max_height_in_items); + forceSyncForNext = false; + } + if (diffId == label && diffValue.type == DRAW_LIST_ELEM_TYPE_INT) { + *current_item = diffValue.i; + return true; + } + return false; + } + + bool Button(const char *label, ImVec2 size) { + if (!options::opts.serverMode) { + if (nextItemFillWidth) { size.x = ImGui::GetContentRegionAvailWidth(); } + return ImGui::Button(label, size); + } + if (rdl) { + rdl->pushStep(DRAW_STEP_BUTTON, forceSyncForNext); + rdl->pushString(label); + rdl->pushFloat(size.x); + rdl->pushFloat(size.y); + forceSyncForNext = false; + } + return (diffId == label); + } + + void Columns(int count, const char *id, bool border) { + if (!options::opts.serverMode) { ImGui::Columns(count, id, border); return; } + if (rdl) { + rdl->pushStep(DRAW_STEP_COLUMNS, forceSyncForNext); + rdl->pushInt(count); + rdl->pushString(id); + rdl->pushBool(border); + forceSyncForNext = false; + } + } + + void NextColumn() { + if (!options::opts.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 (rdl) { + rdl->pushStep(DRAW_STEP_RADIO_BUTTON, forceSyncForNext); + rdl->pushString(label); + rdl->pushBool(active); + forceSyncForNext = false; + } + return (diffId == label); + } + + void BeginGroup() { + if (!options::opts.serverMode) { ImGui::BeginGroup(); return; } + if (rdl) { rdl->pushStep(DRAW_STEP_BEGIN_GROUP, false); } + } + + void EndGroup() { + if (!options::opts.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 (rdl) { + rdl->pushStep(DRAW_STEP_LEFT_LABEL, forceSyncForNext); + rdl->pushString(text); + forceSyncForNext = false; + } + } + + bool SliderInt(const char *label, int *v, int v_min, int v_max, FormatString format, ImGuiSliderFlags flags) { + if (!options::opts.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); + rdl->pushInt(*v); + rdl->pushInt(v_min); + rdl->pushInt(v_max); + rdl->pushInt(format); + rdl->pushInt(flags); + forceSyncForNext = false; + } + if (diffId == label && diffValue.type == DRAW_LIST_ELEM_TYPE_INT) { + *v = diffValue.i; + return true; + } + return false; + } + + bool SliderFloatWithSteps(const char *label, float *v, float v_min, float v_max, float v_step, FormatString display_format) { + if (!options::opts.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); + rdl->pushFloat(*v); + rdl->pushFloat(v_min); + rdl->pushFloat(v_max); + rdl->pushFloat(v_step); + rdl->pushInt(display_format); + forceSyncForNext = false; + } + if (diffId == label && diffValue.type == DRAW_LIST_ELEM_TYPE_FLOAT) { + *v = diffValue.f; + return true; + } + return false; + } + + bool InputInt(const char *label, int *v, int step, int step_fast, ImGuiInputTextFlags flags) { + if (!options::opts.serverMode) { return ImGui::InputInt(label, v, step, step_fast, flags); } + if (rdl) { + rdl->pushStep(DRAW_STEP_INPUT_INT, forceSyncForNext); + rdl->pushString(label); + rdl->pushInt(*v); + rdl->pushInt(step); + rdl->pushInt(step_fast); + rdl->pushInt(flags); + forceSyncForNext = false; + } + if (diffId == label && diffValue.type == DRAW_LIST_ELEM_TYPE_INT) { + *v = diffValue.i; + return true; + } + return false; + } + + bool Checkbox(const char *label, bool *v) { + if (!options::opts.serverMode) { return ImGui::Checkbox(label, v); } + if (rdl) { + rdl->pushStep(DRAW_STEP_CHECKBOX, forceSyncForNext); + rdl->pushString(label); + rdl->pushBool(*v); + forceSyncForNext = false; + } + if (diffId == label && diffValue.type == DRAW_LIST_ELEM_TYPE_BOOL) { + *v = diffValue.b; + return true; + } + return false; + } + + bool SliderFloat(const char *label, float *v, float v_min, float v_max, FormatString format, ImGuiSliderFlags flags) { + if (!options::opts.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); + rdl->pushFloat(*v); + rdl->pushFloat(v_min); + rdl->pushFloat(v_max); + rdl->pushInt(format); + rdl->pushInt(flags); + forceSyncForNext = false; + } + if (diffId == label && diffValue.type == DRAW_LIST_ELEM_TYPE_FLOAT) { + *v = diffValue.f; + return true; + } + return false; + } + + bool InputText(const char *label, char *buf, size_t buf_size, ImGuiInputTextFlags flags) { + if (!options::opts.serverMode) { return ImGui::InputText(label, buf, buf_size, flags); } + if (rdl) { + rdl->pushStep(DRAW_STEP_INPUT_TEXT, forceSyncForNext); + rdl->pushString(label); + rdl->pushString(buf); + rdl->pushInt(buf_size); + rdl->pushInt(flags); + forceSyncForNext = false; + } + if (diffId == label && diffValue.type == DRAW_LIST_ELEM_TYPE_STRING && diffValue.str.size() <= buf_size) { + strcpy(buf, diffValue.str.c_str()); + return true; + } + return false; + } + + void Text(const char* str) { + if (!options::opts.serverMode) { ImGui::Text("%s", str); return; } + if (rdl) { + rdl->pushStep(DRAW_STEP_TEXT, false); + rdl->pushString(str); + } + } + + void TextColored(const ImVec4 &col, const char *str) { + if (!options::opts.serverMode) { ImGui::TextColored(col, "%s", str); return; } + if (rdl) { + rdl->pushStep(DRAW_STEP_TEXT_COLORED, false); + rdl->pushFloat(col.x); + rdl->pushFloat(col.y); + rdl->pushFloat(col.z); + rdl->pushFloat(col.w); + rdl->pushString(str); + } + } + + void OpenPopup(const char *str_id, ImGuiPopupFlags popup_flags) { + if (!options::opts.serverMode) { ImGui::OpenPopup(str_id, popup_flags); return; } + if (rdl) { + rdl->pushStep(DRAW_STEP_OPEN_POPUP, false); + rdl->pushString(str_id); + rdl->pushInt(popup_flags); + } + } + + bool BeginPopup(const char *str_id, ImGuiWindowFlags flags) { + if (!options::opts.serverMode) { return ImGui::BeginPopup(str_id, flags); } + if (rdl) { + rdl->pushStep(DRAW_STEP_BEGIN_POPUP, false); + rdl->pushString(str_id); + rdl->pushInt(flags); + } + return true; + } + + void EndPopup() { + if (!options::opts.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 (rdl) { + rdl->pushStep(DRAW_STEP_BEGIN_TABLE, false); + rdl->pushString(str_id); + rdl->pushInt(column); + rdl->pushInt(flags); + rdl->pushFloat(outer_size.x); + rdl->pushFloat(outer_size.y); + rdl->pushFloat(inner_width); + } + return true; + } + + void EndTable() { + if (!options::opts.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 (rdl) { + rdl->pushStep(DRAW_STEP_TABLE_NEXT_ROW, false); + rdl->pushInt(row_flags); + rdl->pushFloat(min_row_height); + } + } + + void TableSetColumnIndex(int column_n) { + if (!options::opts.serverMode) { ImGui::TableSetColumnIndex(column_n); return; } + if (rdl) { + rdl->pushStep(DRAW_STEP_TABLE_SET_COLUMN_INDEX, false); + rdl->pushInt(column_n); + } + } + + void SetNextItemWidth(float item_width) { + if (!options::opts.serverMode) { ImGui::SetNextItemWidth(item_width); return; } + if (rdl) { + rdl->pushStep(DRAW_STEP_SET_NEXT_ITEM_WIDTH, false); + rdl->pushFloat(item_width); + } + } + + // Config configs + void ForceSyncForNext() { + forceSyncForNext = true; + } +} \ No newline at end of file diff --git a/core/src/gui/smgui.h b/core/src/gui/smgui.h new file mode 100644 index 00000000..d3308859 --- /dev/null +++ b/core/src/gui/smgui.h @@ -0,0 +1,146 @@ +#pragma once +#include +#include +#include +#include + +namespace SmGui { + enum DrawStep { + // Format calls + DRAW_STEP_FILL_WIDTH = 0x00, + DRAW_STEP_SAME_LINE, + DRAW_STEP_BEGIN_DISABLED, + DRAW_STEP_END_DISABLED, + + // Widget calls + DRAW_STEP_COMBO = 0x80, + DRAW_STEP_BUTTON, + DRAW_STEP_COLUMNS, + DRAW_STEP_NEXT_COLUMN, + DRAW_STEP_RADIO_BUTTON, + DRAW_STEP_BEGIN_GROUP, + DRAW_STEP_END_GROUP, + DRAW_STEP_LEFT_LABEL, + DRAW_STEP_SLIDER_INT, + DRAW_STEP_SLIDER_FLOAT_WITH_STEPS, + DRAW_STEP_INPUT_INT, + DRAW_STEP_CHECKBOX, + DRAW_STEP_SLIDER_FLOAT, + DRAW_STEP_INPUT_TEXT, + DRAW_STEP_TEXT, + DRAW_STEP_TEXT_COLORED, + DRAW_STEP_OPEN_POPUP, + DRAW_STEP_BEGIN_POPUP, + DRAW_STEP_END_POPUP, + DRAW_STEP_BEGIN_TABLE, + DRAW_STEP_END_TABLE, + DRAW_STEP_TABLE_NEXT_ROW, + DRAW_STEP_TABLE_SET_COLUMN_INDEX, + DRAW_STEP_SET_NEXT_ITEM_WIDTH + }; + + enum DrawListElemType { + DRAW_LIST_ELEM_TYPE_DRAW_STEP, + DRAW_LIST_ELEM_TYPE_BOOL, + DRAW_LIST_ELEM_TYPE_INT, + DRAW_LIST_ELEM_TYPE_FLOAT, + DRAW_LIST_ELEM_TYPE_STRING, + }; + + struct DrawListElem { + DrawListElemType type; + DrawStep step; + bool forceSync; + bool b; + int i; + float f; + std::string str; + }; + + enum FormatString { + FMT_STR_NONE, + FMT_STR_INT_DEFAULT, + FMT_STR_INT_DB, + FMT_STR_FLOAT_DEFAULT, + FMT_STR_FLOAT_NO_DECIMAL, + FMT_STR_FLOAT_ONE_DECIMAL, + FMT_STR_FLOAT_TWO_DECIMAL, + FMT_STR_FLOAT_THREE_DECIMAL, + FMT_STR_FLOAT_DB_NO_DECIMAL, + FMT_STR_FLOAT_DB_ONE_DECIMAL, + FMT_STR_FLOAT_DB_TWO_DECIMAL, + FMT_STR_FLOAT_DB_THREE_DECIMAL + }; + + extern std::map fmtStr; + + std::string ImStrToString(const char* imstr); + + class DrawList { + public: + void pushStep(DrawStep step, bool forceSync); + void pushBool(bool b); + void pushInt(int i); + void pushFloat(float f); + void pushString(std::string str); + + void draw(std::string& diffId, DrawListElem& diffValue, bool& syncRequired); + + static int loadItem(DrawListElem& elem, uint8_t* data, int len); + int load(void* data, int len); + static int storeItem(DrawListElem& elem, void* data, int len); + int store(void* data, int len); + static int getItemSize(DrawListElem& elem); + int getSize(); + bool checkTypes(int firstId, int n, ...); + bool validate(); + + std::vector elements; + }; + + // Rec/Play functions + // TODO: Maybe move verification to the load function instead of checking in drawFrame + void setDiff(std::string id, SmGui::DrawListElem value); + void startRecord(DrawList* dl); + void stopRecord(); + + // Signaling Functions + void ForceSync(); + + // Format functions + void FillWidth(); + void SameLine(); + void BeginDisabled(); + void EndDisabled(); + + // Widget functions + bool Combo(const char *label, int *current_item, const char *items_separated_by_zeros, int popup_max_height_in_items = -1); + bool Button(const char *label, ImVec2 size = ImVec2(0, 0)); + void Columns(int count = 1, const char *id = (const char *)0, bool border = true); + void NextColumn(); + bool RadioButton(const char *label, bool active); + void BeginGroup(); + void EndGroup(); + void LeftLabel(const char *text); + bool SliderInt(const char *label, int *v, int v_min, int v_max, FormatString format = FMT_STR_INT_DEFAULT, ImGuiSliderFlags flags = 0); + bool SliderFloatWithSteps(const char *label, float *v, float v_min, float v_max, float v_step, FormatString display_format = FMT_STR_FLOAT_THREE_DECIMAL); + bool InputInt(const char *label, int *v, int step = 1, int step_fast = 100, ImGuiInputTextFlags flags = 0); + bool Checkbox(const char *label, bool *v); + bool SliderFloat(const char *label, float *v, float v_min, float v_max, FormatString format = FMT_STR_FLOAT_THREE_DECIMAL, ImGuiSliderFlags flags = 0); + bool InputText(const char *label, char *buf, size_t buf_size, ImGuiInputTextFlags flags = 0); + void Text(const char* str); + void TextColored(const ImVec4 &col, const char *str); + void OpenPopup(const char *str_id, ImGuiPopupFlags popup_flags = 0); + bool BeginPopup(const char *str_id, ImGuiWindowFlags flags = 0); + void EndPopup(); + + bool BeginTable(const char *str_id, int column, ImGuiTableFlags flags = 0, const ImVec2 &outer_size = ImVec2((0.0F), (0.0F)), float inner_width = (0.0F)); + void EndTable(); + void TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = (0.0F)); + void TableSetColumnIndex(int column_n); + void SetNextItemWidth(float item_width); + + // Config configs + void ForceSyncForNext(); + +} \ No newline at end of file diff --git a/core/src/gui/style.cpp b/core/src/gui/style.cpp index 7d3e486f..407cfa0c 100644 --- a/core/src/gui/style.cpp +++ b/core/src/gui/style.cpp @@ -53,4 +53,8 @@ namespace ImGui { ImGui::SameLine(); ImGui::SetCursorPosY(vpos); } + + void FillWidth() { + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth()); + } } \ No newline at end of file diff --git a/core/src/gui/style.h b/core/src/gui/style.h index 8c51c4c7..d1bda7c0 100644 --- a/core/src/gui/style.h +++ b/core/src/gui/style.h @@ -16,4 +16,5 @@ namespace style { namespace ImGui { void LeftLabel(const char* text); + void FillWidth(); } \ No newline at end of file diff --git a/core/src/options.cpp b/core/src/options.cpp index 22e8080d..6e8df2ee 100644 --- a/core/src/options.cpp +++ b/core/src/options.cpp @@ -18,6 +18,8 @@ namespace options { 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[]) { @@ -33,6 +35,16 @@ namespace options { 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; diff --git a/core/src/options.h b/core/src/options.h index 8ada592f..50520f75 100644 --- a/core/src/options.h +++ b/core/src/options.h @@ -7,6 +7,8 @@ namespace options { std::string root; bool showConsole; bool serverMode; + std::string serverHost; + int serverPort; }; SDRPP_EXPORT CMDLineOptions opts; diff --git a/core/src/server.cpp b/core/src/server.cpp index a47da5de..a5b45f55 100644 --- a/core/src/server.cpp +++ b/core/src/server.cpp @@ -1,7 +1,354 @@ -#include +#include "server.h" +#include "core.h" #include +#include +#include +#include +#include +#include +#include +#include +#include +#include -int server_main() { - spdlog::error("Server mode is not implemented yet."); - return 0; -} \ No newline at end of file +namespace server { + dsp::stream dummyInput; + dsp::DynamicRangeCompressor comp; + dsp::HandlerSink hnd; + net::Conn client; + uint8_t* rbuf = NULL; + uint8_t* sbuf = NULL; + uint8_t* bbuf = NULL; + + PacketHeader* r_pkt_hdr = NULL; + uint8_t* r_pkt_data = NULL; + CommandHeader* r_cmd_hdr = NULL; + uint8_t* r_cmd_data = NULL; + + PacketHeader* s_pkt_hdr = NULL; + uint8_t* s_pkt_data = NULL; + CommandHeader* s_cmd_hdr = NULL; + uint8_t* s_cmd_data = NULL; + + PacketHeader* bb_pkt_hdr = NULL; + uint8_t* bb_pkt_data = NULL; + + SmGui::DrawListElem dummyElem; + + net::Listener listener; + + OptionList sourceList; + int sourceId = 0; + bool running = false; + double sampleRate = 1000000.0; + + int main() { + spdlog::info("=====| SERVER MODE |====="); + + // Init DSP + comp.init(&dummyInput, dsp::PCM_TYPE_I8); + hnd.init(&comp.out, _testServerHandler, NULL); + rbuf = new uint8_t[SERVER_MAX_PACKET_SIZE]; + sbuf = new uint8_t[SERVER_MAX_PACKET_SIZE]; + bbuf = new uint8_t[SERVER_MAX_PACKET_SIZE]; + comp.start(); + hnd.start(); + + // Initialize headers + r_pkt_hdr = (PacketHeader*)rbuf; + r_pkt_data = &rbuf[sizeof(PacketHeader)]; + r_cmd_hdr = (CommandHeader*)r_pkt_data; + r_cmd_data = &rbuf[sizeof(PacketHeader) + sizeof(CommandHeader)]; + + s_pkt_hdr = (PacketHeader*)sbuf; + s_pkt_data = &sbuf[sizeof(PacketHeader)]; + s_cmd_hdr = (CommandHeader*)s_pkt_data; + s_cmd_data = &sbuf[sizeof(PacketHeader) + sizeof(CommandHeader)]; + + bb_pkt_hdr = (PacketHeader*)bbuf; + bb_pkt_data = &bbuf[sizeof(PacketHeader)]; + + // Terminate config manager + core::configManager.disableAutoSave(); + core::configManager.save(); + + core::configManager.acquire(); + std::string modulesDir = core::configManager.conf["modulesDirectory"]; + std::vector modules = core::configManager.conf["modules"]; + auto modList = core::configManager.conf["moduleInstances"].items(); + core::configManager.release(); + modulesDir = std::filesystem::absolute(modulesDir).string(); + + 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)) { + for (const auto& file : std::filesystem::directory_iterator(modulesDir)) { + std::string path = file.path().generic_string(); + std::string fn = file.path().filename().string(); + if (file.path().extension().generic_string() != SDRPP_MOD_EXTENTSION) { + continue; + } + if (!file.is_regular_file()) { continue; } + if (fn.find("source") == std::string::npos) { continue; } + + spdlog::info("Loading {0}", path); + core::moduleManager.loadModule(path); + } + } + else { + spdlog::warn("Module directory {0} does not exist, not loading modules from directory", modulesDir); + } + + // Load additional modules through the config ( TODO: Have a proper type parameter int the info ) + // TODO LATER: Add whitelist/blacklist stuff + for (auto const& apath : modules) { + std::filesystem::path file = std::filesystem::absolute(apath); + std::string path = file.generic_string(); + std::string fn = file.filename().string(); + if (file.extension().generic_string() != SDRPP_MOD_EXTENTSION) { + continue; + } + if (!std::filesystem::is_regular_file(file)) { continue; } + if (fn.find("source") == std::string::npos) { continue; } + + spdlog::info("Loading {0}", path); + core::moduleManager.loadModule(path); + } + + // Create module instances + for (auto const& [name, _module] : modList) { + std::string mod = _module["module"]; + bool enabled = _module["enabled"]; + if (core::moduleManager.modules.find(mod) == core::moduleManager.modules.end()) { continue; } + spdlog::info("Initializing {0} ({1})", name, mod); + core::moduleManager.createInstance(name, mod); + if (!enabled) { core::moduleManager.disableInstance(name); } + } + + // Do post-init + core::moduleManager.doPostInitAll(); + + // Generate source list + auto list = sigpath::sourceManager.getSourceNames(); + for (auto& name : list) { + sourceList.define(name, name); + } + + // TODO: Load sourceId from config + + sigpath::sourceManager.selectSource(sourceList[sourceId]); + + // TODO: Use command line option + listener = net::listen(options::opts.serverHost, options::opts.serverPort); + listener->acceptAsync(_clientHandler, NULL); + + spdlog::info("Ready, listening on {0}:{1}", options::opts.serverHost, options::opts.serverPort); + while(1) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } + + return 0; + } + + void _clientHandler(net::Conn conn, void* ctx) { + spdlog::info("Connection from {0}:{1}", "TODO", "TODO"); + client = std::move(conn); + client->readAsync(sizeof(PacketHeader), rbuf, _packetHandler, NULL); + + // Perform settings reset + sigpath::sourceManager.stop(); + comp.setPCMType(dsp::PCM_TYPE_I16); + + sendSampleRate(sampleRate); + + // TODO: Wait otherwise someone else could connect + + listener->acceptAsync(_clientHandler, NULL); + } + + void _packetHandler(int count, uint8_t* buf, void* ctx) { + PacketHeader* hdr = (PacketHeader*)buf; + + // Read the rest of the data (TODO: CHECK SIZE OR SHIT WILL BE FUCKED + ADD TIMEOUT) + int len = 0; + int read = 0; + int goal = hdr->size - sizeof(PacketHeader); + while (len < goal) { + read = client->read(goal - len, &buf[sizeof(PacketHeader) + len]); + if (read < 0) { return; }; + len += read; + } + + // Parse and process + if (hdr->type == PACKET_TYPE_COMMAND && hdr->size >= sizeof(PacketHeader) + sizeof(CommandHeader)) { + CommandHeader* chdr = (CommandHeader*)&buf[sizeof(PacketHeader)]; + commandHandler((Command)chdr->cmd, &buf[sizeof(PacketHeader) + sizeof(CommandHeader)], hdr->size - sizeof(PacketHeader) - sizeof(CommandHeader)); + } + else { + sendError(ERROR_INVALID_PACKET); + } + + // Start another async read + client->readAsync(sizeof(PacketHeader), rbuf, _packetHandler, NULL); + } + + // void _testServerHandler(dsp::complex_t* data, int count, void* ctx) { + // // Build data packet + // PacketHeader* hdr = (PacketHeader*)bbuf; + // hdr->type = PACKET_TYPE_BASEBAND; + // hdr->size = sizeof(PacketHeader) + (count * sizeof(dsp::complex_t)); + // memcpy(&bbuf[sizeof(PacketHeader)], data, count * sizeof(dsp::complex_t)); + + // // Write to network + // if (client && client->isOpen()) { client->write(hdr->size, bbuf); } + // } + + void _testServerHandler(uint8_t* data, int count, void* ctx) { + // Build data packet + PacketHeader* hdr = (PacketHeader*)bbuf; + hdr->type = PACKET_TYPE_BASEBAND; + hdr->size = sizeof(PacketHeader) + count; + memcpy(&bbuf[sizeof(PacketHeader)], data, count); + + // Write to network + if (client && client->isOpen()) { client->write(hdr->size, bbuf); } + } + + void setInput(dsp::stream* stream) { + comp.setInput(stream); + } + + void commandHandler(Command cmd, uint8_t* data, int len) { + if (cmd == COMMAND_GET_UI) { + sendUI(COMMAND_GET_UI, "", dummyElem); + } + else if (cmd == COMMAND_UI_ACTION && len >= 3) { + // Check if sending back data is needed + int i = 0; + bool sendback = data[i++]; + len--; + + // Load id + SmGui::DrawListElem diffId; + int count = SmGui::DrawList::loadItem(diffId, &data[i], len); + if (count < 0) { sendError(ERROR_INVALID_ARGUMENT); return; } + if (diffId.type != SmGui::DRAW_LIST_ELEM_TYPE_STRING) { sendError(ERROR_INVALID_ARGUMENT); return; } + i += count; + len -= count; + + // Load value + SmGui::DrawListElem diffValue; + count = SmGui::DrawList::loadItem(diffValue, &data[i], len); + if (count < 0) { sendError(ERROR_INVALID_ARGUMENT); return; } + i += count; + len -= count; + + // Render and send back + if (sendback) { + sendUI(COMMAND_UI_ACTION, diffId.str, diffValue); + } + else { + renderUI(NULL, diffId.str, diffValue); + } + } + else if (cmd == COMMAND_START) { + sigpath::sourceManager.start(); + running = true; + } + else if (cmd == COMMAND_STOP) { + sigpath::sourceManager.stop(); + running = false; + } + else if (cmd == COMMAND_SET_FREQUENCY && len == 8) { + sigpath::sourceManager.tune(*(double*)data); + sendCommandAck(COMMAND_SET_FREQUENCY, 0); + } + else if (cmd == COMMAND_SET_SAMPLE_TYPE && len == 1) { + dsp::PCMType type = (dsp::PCMType)*(uint8_t*)data; + comp.setPCMType(type); + } + else { + spdlog::error("Invalid Command: {0} (len = {1})", cmd, len); + sendError(ERROR_INVALID_COMMAND); + } + } + + void drawMenu() { + if (running) { SmGui::BeginDisabled(); } + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Combo("##sdrpp_server_src_sel", &sourceId, sourceList.txt)) { + sigpath::sourceManager.selectSource(sourceList[sourceId]); + // TODO: Save config + } + if (running) { SmGui::EndDisabled(); } + + sigpath::sourceManager.showSelectedMenu(); + } + + void renderUI(SmGui::DrawList* dl, std::string diffId, SmGui::DrawListElem diffValue) { + // If we're recording and there's an action, render once with the action and record without + + if (dl && !diffId.empty()) { + SmGui::setDiff(diffId, diffValue); + drawMenu(); + + SmGui::setDiff("", dummyElem); + SmGui::startRecord(dl); + drawMenu(); + SmGui::stopRecord(); + } + else { + SmGui::setDiff(diffId, diffValue); + SmGui::startRecord(dl); + drawMenu(); + SmGui::stopRecord(); + } + } + + void sendUI(Command originCmd, std::string diffId, SmGui::DrawListElem diffValue) { + // Render UI + SmGui::DrawList dl; + renderUI(&dl, diffId, diffValue); + + // Create response + int size = dl.getSize(); + dl.store(s_cmd_data, size); + + // Send to network + sendCommandAck(originCmd, size); + } + + void sendError(Error err) { + PacketHeader* hdr = (PacketHeader*)sbuf; + s_pkt_data[0] = err; + sendPacket(PACKET_TYPE_ERROR, 1); + } + + void sendSampleRate(double sampleRate) { + *(double*)s_cmd_data = sampleRate; + sendCommand(COMMAND_SET_SAMPLERATE, sizeof(double)); + } + + void setInputSampleRate(double samplerate) { + sampleRate = samplerate; + if (!client || !client->isOpen()) { return; } + sendSampleRate(sampleRate); + } + + void sendPacket(PacketType type, int len) { + s_pkt_hdr->type = type; + s_pkt_hdr->size = sizeof(PacketHeader) + len; + client->write(s_pkt_hdr->size, sbuf); + } + + void sendCommand(Command cmd, int len) { + s_cmd_hdr->cmd = cmd; + sendPacket(PACKET_TYPE_COMMAND, sizeof(CommandHeader) + len); + } + + void sendCommandAck(Command cmd, int len) { + s_cmd_hdr->cmd = cmd; + sendPacket(PACKET_TYPE_COMMAND_ACK, sizeof(CommandHeader) + len); + } +} diff --git a/core/src/server.h b/core/src/server.h index 45d46fd6..7d46ee8c 100644 --- a/core/src/server.h +++ b/core/src/server.h @@ -1,3 +1,27 @@ #pragma once +#include +#include +#include +#include -int server_main(); \ No newline at end of file +namespace server { + void setInput(dsp::stream* stream); + int main(); + + void _clientHandler(net::Conn conn, void* ctx); + void _packetHandler(int count, uint8_t* buf, void* ctx); + void _testServerHandler(uint8_t* data, int count, void* ctx); + + void drawMenu(); + + void commandHandler(Command cmd, uint8_t* data, int len); + void renderUI(SmGui::DrawList* dl, std::string diffId, SmGui::DrawListElem diffValue); + void sendUI(Command originCmd, std::string diffId, SmGui::DrawListElem diffValue); + void sendError(Error err); + void sendSampleRate(double sampleRate); + void setInputSampleRate(double samplerate); + + void sendPacket(PacketType type, int len); + void sendCommand(Command cmd, int len); + void sendCommandAck(Command cmd, int len); +} diff --git a/core/src/server_protocol.h b/core/src/server_protocol.h new file mode 100644 index 00000000..05649249 --- /dev/null +++ b/core/src/server_protocol.h @@ -0,0 +1,49 @@ +#pragma once +#include +#include +#include + +#define SERVER_MAX_PACKET_SIZE (STREAM_BUFFER_SIZE * sizeof(dsp::complex_t) * 2) + +namespace server { + enum PacketType { + // Client to Server + PACKET_TYPE_COMMAND, + PACKET_TYPE_COMMAND_ACK, + PACKET_TYPE_BASEBAND, + PACKET_TYPE_VFO, + PACKET_TYPE_FFT, + PACKET_TYPE_ERROR + }; + + enum Command { + // Client to Server + COMMAND_GET_UI = 0x00, + COMMAND_UI_ACTION, + COMMAND_START, + COMMAND_STOP, + COMMAND_SET_FREQUENCY, + COMMAND_GET_SAMPLERATE, + COMMAND_SET_SAMPLE_TYPE, + + // Server to client + COMMAND_SET_SAMPLERATE = 0x80 + }; + + enum Error { + ERROR_NONE = 0x00, + ERROR_INVALID_PACKET, + ERROR_INVALID_COMMAND, + ERROR_INVALID_ARGUMENT + }; + + // TODO: Pack + struct PacketHeader { + uint32_t type; + uint32_t size; + }; + + struct CommandHeader { + uint32_t cmd; + }; +} \ No newline at end of file diff --git a/core/src/signal_path/source.cpp b/core/src/signal_path/source.cpp index 13d72186..3b878e37 100644 --- a/core/src/signal_path/source.cpp +++ b/core/src/signal_path/source.cpp @@ -1,6 +1,8 @@ +#include #include #include #include +#include SourceManager::SourceManager() { } @@ -48,7 +50,13 @@ void SourceManager::selectSource(std::string name) { selectedHandler = sources[name]; selectedHandler->selectHandler(selectedHandler->ctx); selectedName = name; - sigpath::signalPath.setInput(selectedHandler->stream); + if (options::opts.serverMode) { + server::setInput(selectedHandler->stream); + } + else { + sigpath::signalPath.setInput(selectedHandler->stream); + } + // Set server input here } void SourceManager::showSelectedMenu() { diff --git a/core/src/utils/networking.cpp b/core/src/utils/networking.cpp index 08f9d264..fc7364cf 100644 --- a/core/src/utils/networking.cpp +++ b/core/src/utils/networking.cpp @@ -1,5 +1,6 @@ #include #include +#include namespace net { @@ -70,19 +71,34 @@ namespace net { if (_udp) { socklen_t fromLen = sizeof(remoteAddr); ret = recvfrom(_sock, (char*)buf, count, 0, (struct sockaddr*)&remoteAddr, &fromLen); - } - else { - ret = recv(_sock, (char*)buf, count, 0); + if (ret <= 0) { + { + std::lock_guard lck(connectionOpenMtx); + connectionOpen = false; + } + connectionOpenCnd.notify_all(); + return -1; + } + return count; } - if (ret <= 0) { - { - std::lock_guard lck(connectionOpenMtx); - connectionOpen = false; + int beenRead = 0; + while (beenRead < count) { + ret = recv(_sock, (char*)&buf[beenRead], count - beenRead, 0); + + if (ret <= 0) { + { + std::lock_guard lck(connectionOpenMtx); + connectionOpen = false; + } + connectionOpenCnd.notify_all(); + return -1; } - connectionOpenCnd.notify_all(); + + beenRead += ret; } - return ret; + + return beenRead; } bool ConnClass::write(int count, uint8_t* buf) { @@ -93,19 +109,31 @@ namespace net { if (_udp) { int fromLen = sizeof(remoteAddr); ret = sendto(_sock, (char*)buf, count, 0, (struct sockaddr*)&remoteAddr, sizeof(remoteAddr)); - } - else { - ret = send(_sock, (char*)buf, count, 0); + if (ret <= 0) { + { + std::lock_guard lck(connectionOpenMtx); + connectionOpen = false; + } + connectionOpenCnd.notify_all(); + } + return (ret > 0); } - if (ret <= 0) { - { - std::lock_guard lck(connectionOpenMtx); - connectionOpen = false; + int beenWritten = 0; + while (beenWritten < count) { + ret = send(_sock, (char*)buf, count, 0); + if (ret <= 0) { + { + std::lock_guard lck(connectionOpenMtx); + connectionOpen = false; + } + connectionOpenCnd.notify_all(); + return false; } - connectionOpenCnd.notify_all(); + beenWritten += ret; } - return (ret > 0); + + return true; } void ConnClass::readAsync(int count, uint8_t* buf, void (*handler)(int count, uint8_t* buf, void* ctx), void* ctx) { diff --git a/core/src/utils/new_networking.cpp b/core/src/utils/new_networking.cpp new file mode 100644 index 00000000..4d04844d --- /dev/null +++ b/core/src/utils/new_networking.cpp @@ -0,0 +1,2 @@ +#include "new_networking.h" + diff --git a/core/src/utils/new_networking.h b/core/src/utils/new_networking.h new file mode 100644 index 00000000..a6cc9721 --- /dev/null +++ b/core/src/utils/new_networking.h @@ -0,0 +1,95 @@ +#pragma once +#include +#include +#include +#include + +/* + Ryzerth's Epic Networking Functins +*/ + +namespace net { + enum SocketType { + SOCK_TYPE_TCP, + SOCK_TYPE_UDP + }; + + struct ReadHandler { + int count; + void* buf; + void (*handle)(int count, void* buf, void* ctx); + void* ctx; + }; + + class SocketClass { + public: + SocketClass(struct addrinfo* localAddr, struct addrinfo* remoteAddr, SocketType sockType); + ~SocketClass(); + + int read(int count, void* buf, int timeout); + int write(int count, void* buf); + + void readAsync(int count, void* buf, void (*handle)(int count, void* buf, void* ctx), void* ctx); + + bool isOpen(); + void close(); + + private: + void readWorker(); + + bool open = false; + + struct addrinfo* laddr; + struct addrinfo* raddr; + SocketType type; + + std::queue readQueue; + std::thread readThread; + std::mutex readMtx; + std::condition_variable readCnd; + + }; + + typedef std::shared_ptr Socket; + + namespace tcp { + struct AcceptHandler { + void (*handle)(Socket client, void* ctx); + void* ctx; + }; + + class ListenerClass { + public: + ListenerClass(struct addrinfo* addr); + ~ListenerClass(); + + Socket accept(int count, void* buf, int timeout); + void acceptAsync(void (*handle)(int count, void* buf, void* ctx), void* ctx); + + bool isOpen(); + void close(); + + private: + void acceptWorker(); + + bool open = false; + + struct addrinfo* addr; + + std::queue acceptQueue; + std::thread acceptThread; + std::mutex acceptMtx; + std::condition_variable acceptCnd; + + }; + + typedef std::shared_ptr Listener; + + Socket connect(std::string host, int port); + Listener listen(std::string host, int port); + } + + namespace udp { + Socket open(std::string remoteHost, int remotePort, std::string localHost = "", int localPort = -1); + } +} \ No newline at end of file diff --git a/source_modules/airspy_source/src/main.cpp b/source_modules/airspy_source/src/main.cpp index 9b53bac6..101acb9f 100644 --- a/source_modules/airspy_source/src/main.cpp +++ b/source_modules/airspy_source/src/main.cpp @@ -7,9 +7,9 @@ #include #include #include +#include #include - #define CONCAT(a, b) ((std::string(a) + b).c_str()) SDRPP_MOD_INFO{ @@ -313,12 +313,12 @@ private: static void menuHandler(void* ctx) { AirspySourceModule* _this = (AirspySourceModule*)ctx; - float menuWidth = ImGui::GetContentRegionAvailWidth(); - if (_this->running) { style::beginDisabled(); } + if (_this->running) { SmGui::BeginDisabled(); } - ImGui::SetNextItemWidth(menuWidth); - if (ImGui::Combo(CONCAT("##_airspy_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) { + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Combo(CONCAT("##_airspy_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) { _this->selectBySerial(_this->devList[_this->devId]); core::setInputSampleRate(_this->sampleRate); if (_this->selectedSerStr != "") { @@ -328,7 +328,7 @@ private: } } - if (ImGui::Combo(CONCAT("##_airspy_sr_sel_", _this->name), &_this->srId, _this->sampleRateListTxt.c_str())) { + if (SmGui::Combo(CONCAT("##_airspy_sr_sel_", _this->name), &_this->srId, _this->sampleRateListTxt.c_str())) { _this->sampleRate = _this->sampleRateList[_this->srId]; core::setInputSampleRate(_this->sampleRate); if (_this->selectedSerStr != "") { @@ -338,9 +338,10 @@ private: } } - ImGui::SameLine(); - float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX(); - if (ImGui::Button(CONCAT("Refresh##_airspy_refr_", _this->name), ImVec2(refreshBtnWdith, 0))) { + SmGui::SameLine(); + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Button(CONCAT("Refresh##_airspy_refr_", _this->name))) { _this->refresh(); config.acquire(); std::string devSerial = config.conf["device"]; @@ -349,11 +350,12 @@ private: core::setInputSampleRate(_this->sampleRate); } - if (_this->running) { style::endDisabled(); } + if (_this->running) { SmGui::EndDisabled(); } - ImGui::BeginGroup(); - ImGui::Columns(3, CONCAT("AirspyGainModeColumns##_", _this->name), false); - if (ImGui::RadioButton(CONCAT("Sensitive##_airspy_gm_", _this->name), _this->gainMode == 0)) { + SmGui::BeginGroup(); + SmGui::Columns(3, CONCAT("AirspyGainModeColumns##_", _this->name), false); + SmGui::ForceSync(); + if (SmGui::RadioButton(CONCAT("Sensitive##_airspy_gm_", _this->name), _this->gainMode == 0)) { _this->gainMode = 0; if (_this->running) { airspy_set_lna_agc(_this->openDev, 0); @@ -366,8 +368,9 @@ private: config.release(true); } } - ImGui::NextColumn(); - if (ImGui::RadioButton(CONCAT("Linear##_airspy_gm_", _this->name), _this->gainMode == 1)) { + SmGui::NextColumn(); + SmGui::ForceSync(); + if (SmGui::RadioButton(CONCAT("Linear##_airspy_gm_", _this->name), _this->gainMode == 1)) { _this->gainMode = 1; if (_this->running) { airspy_set_lna_agc(_this->openDev, 0); @@ -380,8 +383,9 @@ private: config.release(true); } } - ImGui::NextColumn(); - if (ImGui::RadioButton(CONCAT("Free##_airspy_gm_", _this->name), _this->gainMode == 2)) { + SmGui::NextColumn(); + SmGui::ForceSync(); + if (SmGui::RadioButton(CONCAT("Free##_airspy_gm_", _this->name), _this->gainMode == 2)) { _this->gainMode = 2; if (_this->running) { if (_this->lnaAgc) { @@ -406,15 +410,15 @@ private: config.release(true); } } - ImGui::Columns(1, CONCAT("EndAirspyGainModeColumns##_", _this->name), false); - ImGui::EndGroup(); + SmGui::Columns(1, CONCAT("EndAirspyGainModeColumns##_", _this->name), false); + SmGui::EndGroup(); // Gain menus if (_this->gainMode == 0) { - ImGui::LeftLabel("Gain"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderInt(CONCAT("##_airspy_sens_gain_", _this->name), &_this->sensitiveGain, 0, 21)) { + SmGui::LeftLabel("Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_airspy_sens_gain_", _this->name), &_this->sensitiveGain, 0, 21)) { if (_this->running) { airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain); } @@ -426,9 +430,9 @@ private: } } else if (_this->gainMode == 1) { - ImGui::LeftLabel("Gain"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderInt(CONCAT("##_airspy_lin_gain_", _this->name), &_this->linearGain, 0, 21)) { + SmGui::LeftLabel("Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_airspy_lin_gain_", _this->name), &_this->linearGain, 0, 21)) { if (_this->running) { airspy_set_linearity_gain(_this->openDev, _this->linearGain); } @@ -440,14 +444,11 @@ private: } } else if (_this->gainMode == 2) { - // Calculate position of sliders - float pos = ImGui::CalcTextSize("Mixer Gain").x + 10; - - if (_this->lnaAgc) { style::beginDisabled(); } - ImGui::LeftLabel("LNA Gain"); - ImGui::SetCursorPosX(pos); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderInt(CONCAT("##_airspy_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) { + // TODO: Switch to a table for alignement + if (_this->lnaAgc) { SmGui::BeginDisabled(); } + SmGui::LeftLabel("LNA Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_airspy_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) { if (_this->running) { airspy_set_lna_gain(_this->openDev, _this->lnaGain); } @@ -457,13 +458,12 @@ private: config.release(true); } } - if (_this->lnaAgc) { style::endDisabled(); } + if (_this->lnaAgc) { SmGui::EndDisabled(); } - if (_this->mixerAgc) { style::beginDisabled(); } - ImGui::LeftLabel("Mixer Gain"); - ImGui::SetCursorPosX(pos); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderInt(CONCAT("##_airspy_mix_gain_", _this->name), &_this->mixerGain, 0, 15)) { + if (_this->mixerAgc) { SmGui::BeginDisabled(); } + SmGui::LeftLabel("Mixer Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_airspy_mix_gain_", _this->name), &_this->mixerGain, 0, 15)) { if (_this->running) { airspy_set_mixer_gain(_this->openDev, _this->mixerGain); } @@ -473,12 +473,11 @@ private: config.release(true); } } - if (_this->mixerAgc) { style::endDisabled(); } + if (_this->mixerAgc) { SmGui::EndDisabled(); } - ImGui::LeftLabel("VGA Gain"); - ImGui::SetCursorPosX(pos); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderInt(CONCAT("##_airspy_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) { + SmGui::LeftLabel("VGA Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_airspy_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) { if (_this->running) { airspy_set_vga_gain(_this->openDev, _this->vgaGain); } @@ -490,7 +489,8 @@ private: } // AGC Control - if (ImGui::Checkbox(CONCAT("LNA AGC##_airspy_", _this->name), &_this->lnaAgc)) { + SmGui::ForceSync(); + if (SmGui::Checkbox(CONCAT("LNA AGC##_airspy_", _this->name), &_this->lnaAgc)) { if (_this->running) { if (_this->lnaAgc) { airspy_set_lna_agc(_this->openDev, 1); @@ -506,7 +506,8 @@ private: config.release(true); } } - if (ImGui::Checkbox(CONCAT("Mixer AGC##_airspy_", _this->name), &_this->mixerAgc)) { + SmGui::ForceSync(); + if (SmGui::Checkbox(CONCAT("Mixer AGC##_airspy_", _this->name), &_this->mixerAgc)) { if (_this->running) { if (_this->mixerAgc) { airspy_set_mixer_agc(_this->openDev, 1); @@ -525,8 +526,7 @@ private: } // Bias T - - if (ImGui::Checkbox(CONCAT("Bias T##_airspy_", _this->name), &_this->biasT)) { + if (SmGui::Checkbox(CONCAT("Bias T##_airspy_", _this->name), &_this->biasT)) { if (_this->running) { airspy_set_rf_bias(_this->openDev, _this->biasT); } diff --git a/source_modules/airspyhf_source/src/main.cpp b/source_modules/airspyhf_source/src/main.cpp index 0e28c4ae..48f26bf0 100644 --- a/source_modules/airspyhf_source/src/main.cpp +++ b/source_modules/airspyhf_source/src/main.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -7,6 +6,7 @@ #include #include #include +#include #include #include @@ -265,12 +265,12 @@ private: static void menuHandler(void* ctx) { AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx; - float menuWidth = ImGui::GetContentRegionAvailWidth(); - if (_this->running) { style::beginDisabled(); } + if (_this->running) { SmGui::BeginDisabled(); } - ImGui::SetNextItemWidth(menuWidth); - if (ImGui::Combo(CONCAT("##_airspyhf_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) { + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Combo(CONCAT("##_airspyhf_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) { _this->selectBySerial(_this->devList[_this->devId]); core::setInputSampleRate(_this->sampleRate); if (_this->selectedSerStr != "") { @@ -280,7 +280,7 @@ private: } } - if (ImGui::Combo(CONCAT("##_airspyhf_sr_sel_", _this->name), &_this->srId, _this->sampleRateListTxt.c_str())) { + if (SmGui::Combo(CONCAT("##_airspyhf_sr_sel_", _this->name), &_this->srId, _this->sampleRateListTxt.c_str())) { _this->sampleRate = _this->sampleRateList[_this->srId]; core::setInputSampleRate(_this->sampleRate); if (_this->selectedSerStr != "") { @@ -290,9 +290,10 @@ private: } } - ImGui::SameLine(); - float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX(); - if (ImGui::Button(CONCAT("Refresh##_airspyhf_refr_", _this->name), ImVec2(refreshBtnWdith, 0))) { + SmGui::SameLine(); + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Button(CONCAT("Refresh##_airspyhf_refr_", _this->name))) { _this->refresh(); config.acquire(); std::string devSerial = config.conf["device"]; @@ -301,11 +302,11 @@ private: core::setInputSampleRate(_this->sampleRate); } - if (_this->running) { style::endDisabled(); } + if (_this->running) { SmGui::EndDisabled(); } - ImGui::LeftLabel("AGC Mode"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo(CONCAT("##_airspyhf_agc_", _this->name), &_this->agcMode, AGG_MODES_STR)) { + SmGui::LeftLabel("AGC Mode"); + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##_airspyhf_agc_", _this->name), &_this->agcMode, AGG_MODES_STR)) { if (_this->running) { airspyhf_set_hf_agc(_this->openDev, (_this->agcMode != 0)); if (_this->agcMode > 0) { @@ -319,9 +320,9 @@ private: } } - ImGui::LeftLabel("Attenuation"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderFloatWithSteps(CONCAT("##_airspyhf_attn_", _this->name), &_this->atten, 0, 48, 6, "%.0f dB")) { + SmGui::LeftLabel("Attenuation"); + SmGui::FillWidth(); + if (SmGui::SliderFloatWithSteps(CONCAT("##_airspyhf_attn_", _this->name), &_this->atten, 0, 48, 6, SmGui::FMT_STR_FLOAT_DB_NO_DECIMAL)) { if (_this->running) { airspyhf_set_hf_att(_this->openDev, _this->atten / 6.0f); } @@ -332,7 +333,7 @@ private: } } - if (ImGui::Checkbox(CONCAT("HF LNA##_airspyhf_lna_", _this->name), &_this->hfLNA)) { + if (SmGui::Checkbox(CONCAT("HF LNA##_airspyhf_lna_", _this->name), &_this->hfLNA)) { if (_this->running) { airspyhf_set_hf_lna(_this->openDev, _this->hfLNA); } diff --git a/source_modules/bladerf_source/src/main.cpp b/source_modules/bladerf_source/src/main.cpp index 8c82d566..b23fd4f3 100644 --- a/source_modules/bladerf_source/src/main.cpp +++ b/source_modules/bladerf_source/src/main.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -426,12 +427,12 @@ private: static void menuHandler(void* ctx) { BladeRFSourceModule* _this = (BladeRFSourceModule*)ctx; - float menuWidth = ImGui::GetContentRegionAvailWidth(); - if (_this->running) { style::beginDisabled(); } + if (_this->running) { SmGui::BeginDisabled(); } - ImGui::SetNextItemWidth(menuWidth); - if (ImGui::Combo(CONCAT("##_balderf_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) { + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Combo(CONCAT("##_balderf_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) { bladerf_devinfo info = _this->devInfoList[_this->devId]; _this->selectByInfo(&info); core::setInputSampleRate(_this->sampleRate); @@ -440,7 +441,7 @@ private: config.release(true); } - if (ImGui::Combo(CONCAT("##_balderf_sr_sel_", _this->name), &_this->srId, _this->sampleRatesTxt.c_str())) { + if (SmGui::Combo(CONCAT("##_balderf_sr_sel_", _this->name), &_this->srId, _this->sampleRatesTxt.c_str())) { _this->sampleRate = _this->sampleRates[_this->srId]; core::setInputSampleRate(_this->sampleRate); if (_this->selectedSerial != "") { @@ -451,9 +452,10 @@ private: } // Refresh button - ImGui::SameLine(); - float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX(); - if (ImGui::Button(CONCAT("Refresh##_balderf_refr_", _this->name), ImVec2(refreshBtnWdith, 0))) { + SmGui::SameLine(); + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Button(CONCAT("Refresh##_balderf_refr_", _this->name))) { _this->refresh(); _this->selectBySerial(_this->selectedSerial, false); core::setInputSampleRate(_this->sampleRate); @@ -461,9 +463,9 @@ private: // Channel selection (only show if more than one channel) if (_this->channelCount > 1) { - ImGui::LeftLabel("RX Channel"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - ImGui::Combo(CONCAT("##_balderf_ch_sel_", _this->name), &_this->chanId, _this->channelNamesTxt.c_str()); + SmGui::LeftLabel("RX Channel"); + SmGui::FillWidth(); + SmGui::Combo(CONCAT("##_balderf_ch_sel_", _this->name), &_this->chanId, _this->channelNamesTxt.c_str()); if (_this->selectedSerial != "") { config.acquire(); config.conf["devices"][_this->selectedSerial]["channelId"] = _this->chanId; @@ -471,11 +473,11 @@ private: } } - if (_this->running) { style::endDisabled(); } + if (_this->running) { SmGui::EndDisabled(); } - ImGui::LeftLabel("Bandwidth"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo(CONCAT("##_balderf_bw_sel_", _this->name), &_this->bwId, _this->bandwidthsTxt.c_str())) { + SmGui::LeftLabel("Bandwidth"); + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##_balderf_bw_sel_", _this->name), &_this->bwId, _this->bandwidthsTxt.c_str())) { if (_this->running) { bladerf_set_bandwidth(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), (_this->bwId == _this->bandwidths.size()) ? std::clamp(_this->sampleRate, _this->bwRange->min, _this->bwRange->max) : _this->bandwidths[_this->bwId], NULL); } @@ -487,9 +489,10 @@ private: } // General config BS - ImGui::LeftLabel("Gain control mode"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo(CONCAT("##_balderf_gm_sel_", _this->name), &_this->gainMode, _this->gainModesTxt.c_str()) && _this->selectedSerial != "") { + SmGui::LeftLabel("Gain control mode"); + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Combo(CONCAT("##_balderf_gm_sel_", _this->name), &_this->gainMode, _this->gainModesTxt.c_str()) && _this->selectedSerial != "") { if (_this->running) { bladerf_set_gain_mode(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->gainModes[_this->gainMode].mode); } @@ -505,11 +508,11 @@ private: } if (_this->selectedSerial != "") { - if (_this->gainModes[_this->gainMode].mode != BLADERF_GAIN_MANUAL) { style::beginDisabled(); } + if (_this->gainModes[_this->gainMode].mode != BLADERF_GAIN_MANUAL) { SmGui::BeginDisabled(); } } - ImGui::LeftLabel("Gain"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderInt("##_balderf_oag_sel_", &_this->overallGain, (_this->gainRange != NULL) ? _this->gainRange->min : 0, (_this->gainRange != NULL) ? _this->gainRange->max : 60)) { + SmGui::LeftLabel("Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt("##_balderf_oag_sel_", &_this->overallGain, (_this->gainRange != NULL) ? _this->gainRange->min : 0, (_this->gainRange != NULL) ? _this->gainRange->max : 60)) { if (_this->running) { spdlog::info("Setting gain to {0}", _this->overallGain); bladerf_set_gain(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->overallGain); @@ -521,11 +524,11 @@ private: } } if (_this->selectedSerial != "") { - if (_this->gainModes[_this->gainMode].mode != BLADERF_GAIN_MANUAL) { style::endDisabled(); } + if (_this->gainModes[_this->gainMode].mode != BLADERF_GAIN_MANUAL) { SmGui::EndDisabled(); } } if (_this->selectedBladeType == BLADERF_TYPE_V2) { - if (ImGui::Checkbox("Bias-T##_balderf_biast_", &_this->biasT)) { + if (SmGui::Checkbox("Bias-T##_balderf_biast_", &_this->biasT)) { if (_this->running) { bladerf_set_bias_tee(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->biasT); } diff --git a/source_modules/file_source/src/main.cpp b/source_modules/file_source/src/main.cpp index bc1a9105..5c23b2e6 100644 --- a/source_modules/file_source/src/main.cpp +++ b/source_modules/file_source/src/main.cpp @@ -28,6 +28,8 @@ public: FileSourceModule(std::string name) : fileSelect("", { "Wav IQ Files (*.wav)", "*.wav", "All Files", "*" }) { this->name = name; + if (options::opts.serverMode) { return; } + config.acquire(); fileSelect.setPath(config.conf["path"], true); config.release(); diff --git a/source_modules/hackrf_source/src/main.cpp b/source_modules/hackrf_source/src/main.cpp index bb22917c..593cb1c7 100644 --- a/source_modules/hackrf_source/src/main.cpp +++ b/source_modules/hackrf_source/src/main.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -9,6 +8,7 @@ #include #include #include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -275,19 +275,18 @@ private: static void menuHandler(void* ctx) { HackRFSourceModule* _this = (HackRFSourceModule*)ctx; - float menuWidth = ImGui::GetContentRegionAvailWidth(); - if (_this->running) { style::beginDisabled(); } - - ImGui::SetNextItemWidth(menuWidth); - if (ImGui::Combo(CONCAT("##_hackrf_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) { - _this->selectedSerial = _this->devList[_this->devId]; + if (_this->running) { SmGui::BeginDisabled(); } + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Combo(CONCAT("##_hackrf_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) { + _this->selectBySerial(_this->devList[_this->devId]); config.acquire(); config.conf["device"] = _this->selectedSerial; config.release(true); } - if (ImGui::Combo(CONCAT("##_hackrf_sr_sel_", _this->name), &_this->srId, sampleRatesTxt)) { + if (SmGui::Combo(CONCAT("##_hackrf_sr_sel_", _this->name), &_this->srId, sampleRatesTxt)) { _this->sampleRate = sampleRates[_this->srId]; core::setInputSampleRate(_this->sampleRate); config.acquire(); @@ -295,19 +294,20 @@ private: config.release(true); } - ImGui::SameLine(); - float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX(); - if (ImGui::Button(CONCAT("Refresh##_hackrf_refr_", _this->name), ImVec2(refreshBtnWdith, 0))) { + SmGui::SameLine(); + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Button(CONCAT("Refresh##_hackrf_refr_", _this->name))) { _this->refresh(); _this->selectBySerial(_this->selectedSerial); core::setInputSampleRate(_this->sampleRate); } - if (_this->running) { style::endDisabled(); } + if (_this->running) { SmGui::EndDisabled(); } - ImGui::LeftLabel("Bandwidth"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo(CONCAT("##_hackrf_bw_sel_", _this->name), &_this->bwId, bandwidthsTxt)) { + SmGui::LeftLabel("Bandwidth"); + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##_hackrf_bw_sel_", _this->name), &_this->bwId, bandwidthsTxt)) { if (_this->running) { hackrf_set_baseband_filter_bandwidth(_this->openDev, _this->bandwidthIdToBw(_this->bwId)); } @@ -316,9 +316,9 @@ private: config.release(true); } - ImGui::LeftLabel("LNA Gain"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderFloatWithSteps(CONCAT("##_hackrf_lna_", _this->name), &_this->lna, 0, 40, 8, "%.0fdB")) { + SmGui::LeftLabel("LNA Gain"); + SmGui::FillWidth(); + if (SmGui::SliderFloatWithSteps(CONCAT("##_hackrf_lna_", _this->name), &_this->lna, 0, 40, 8, SmGui::FMT_STR_FLOAT_DB_NO_DECIMAL)) { if (_this->running) { hackrf_set_lna_gain(_this->openDev, _this->lna); } @@ -327,9 +327,9 @@ private: config.release(true); } - ImGui::LeftLabel("VGA Gain"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderFloatWithSteps(CONCAT("##_hackrf_vga_", _this->name), &_this->vga, 0, 62, 2, "%.0fdB")) { + SmGui::LeftLabel("VGA Gain"); + SmGui::FillWidth(); + if (SmGui::SliderFloatWithSteps(CONCAT("##_hackrf_vga_", _this->name), &_this->vga, 0, 62, 2, SmGui::FMT_STR_FLOAT_DB_NO_DECIMAL)) { if (_this->running) { hackrf_set_vga_gain(_this->openDev, _this->vga); } @@ -338,7 +338,7 @@ private: config.release(true); } - if (ImGui::Checkbox(CONCAT("Bias-T##_hackrf_bt_", _this->name), &_this->biasT)) { + if (SmGui::Checkbox(CONCAT("Bias-T##_hackrf_bt_", _this->name), &_this->biasT)) { if (_this->running) { hackrf_set_antenna_enable(_this->openDev, _this->biasT); } @@ -347,7 +347,7 @@ private: config.release(true); } - if (ImGui::Checkbox(CONCAT("Amp Enabled##_hackrf_amp_", _this->name), &_this->amp)) { + if (SmGui::Checkbox(CONCAT("Amp Enabled##_hackrf_amp_", _this->name), &_this->amp)) { if (_this->running) { hackrf_set_amp_enable(_this->openDev, _this->amp); } diff --git a/source_modules/limesdr_source/src/main.cpp b/source_modules/limesdr_source/src/main.cpp index b20fbd09..762ac8c5 100644 --- a/source_modules/limesdr_source/src/main.cpp +++ b/source_modules/limesdr_source/src/main.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -7,6 +6,7 @@ #include #include #include +#include #include @@ -382,12 +382,12 @@ private: static void menuHandler(void* ctx) { LimeSDRSourceModule* _this = (LimeSDRSourceModule*)ctx; - float menuWidth = ImGui::GetContentRegionAvailWidth(); - if (_this->running) { style::beginDisabled(); } + if (_this->running) { SmGui::BeginDisabled(); } - ImGui::SetNextItemWidth(menuWidth); - if (ImGui::Combo("##limesdr_dev_sel", &_this->devId, _this->devListTxt.c_str())) { + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Combo("##limesdr_dev_sel", &_this->devId, _this->devListTxt.c_str())) { _this->selectByInfoStr(_this->devList[_this->devId]); core::setInputSampleRate(_this->sampleRate); config.acquire(); @@ -395,7 +395,7 @@ private: config.release(true); } - if (ImGui::Combo(CONCAT("##_limesdr_sr_sel_", _this->name), &_this->srId, _this->sampleRatesTxt.c_str())) { + if (SmGui::Combo(CONCAT("##_limesdr_sr_sel_", _this->name), &_this->srId, _this->sampleRatesTxt.c_str())) { _this->sampleRate = _this->sampleRates[_this->srId]; core::setInputSampleRate(_this->sampleRate); if (_this->selectedDevName != "") { @@ -406,29 +406,30 @@ private: } // Refresh button - ImGui::SameLine(); - float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX(); - if (ImGui::Button(CONCAT("Refresh##_limesdr_refr_", _this->name), ImVec2(refreshBtnWdith, 0))) { + SmGui::SameLine(); + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Button(CONCAT("Refresh##_limesdr_refr_", _this->name))) { _this->refresh(); _this->selectByName(_this->selectedDevName); core::setInputSampleRate(_this->sampleRate); } if (_this->channelCount > 1) { - ImGui::LeftLabel("RX Channel"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo("##limesdr_ch_sel", &_this->chanId, _this->channelNamesTxt.c_str()) && _this->selectedDevName != "") { + SmGui::LeftLabel("RX Channel"); + SmGui::FillWidth(); + if (SmGui::Combo("##limesdr_ch_sel", &_this->chanId, _this->channelNamesTxt.c_str()) && _this->selectedDevName != "") { config.acquire(); config.conf["devices"][_this->selectedDevName]["channel"] = _this->chanId; config.release(true); } } - if (_this->running) { style::endDisabled(); } + if (_this->running) { SmGui::EndDisabled(); } - ImGui::LeftLabel("Antenna"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo("##limesdr_ant_sel", &_this->antennaId, _this->antennaListTxt.c_str())) { + SmGui::LeftLabel("Antenna"); + SmGui::FillWidth(); + if (SmGui::Combo("##limesdr_ant_sel", &_this->antennaId, _this->antennaListTxt.c_str())) { if (_this->running) { LMS_SetAntenna(_this->openDev, false, _this->chanId, _this->antennaId); } @@ -439,9 +440,9 @@ private: } } - ImGui::LeftLabel("Bandwidth"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo("##limesdr_bw_sel", &_this->bwId, _this->bandwidthsTxt.c_str())) { + SmGui::LeftLabel("Bandwidth"); + SmGui::FillWidth(); + if (SmGui::Combo("##limesdr_bw_sel", &_this->bwId, _this->bandwidthsTxt.c_str())) { if (_this->running) { LMS_SetLPFBW(_this->openDev, false, _this->chanId, (_this->bwId == _this->bandwidths.size()) ? _this->getBestBandwidth(_this->sampleRate) : _this->bandwidths[_this->bwId]); } @@ -452,9 +453,9 @@ private: } } - ImGui::LeftLabel("Gain"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderInt("##limesdr_gain_sel", &_this->gain, 0, 73, "%ddB")) { + SmGui::LeftLabel("Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt("##limesdr_gain_sel", &_this->gain, 0, 73, SmGui::FMT_STR_INT_DB)) { if (_this->running) { LMS_SetGaindB(_this->openDev, false, _this->chanId, _this->gain); } diff --git a/source_modules/plutosdr_source/src/main.cpp b/source_modules/plutosdr_source/src/main.cpp index bcc255af..d0720c56 100644 --- a/source_modules/plutosdr_source/src/main.cpp +++ b/source_modules/plutosdr_source/src/main.cpp @@ -1,10 +1,10 @@ -#include #include #include #include #include #include #include +#include #include #include #include @@ -185,32 +185,31 @@ private: static void menuHandler(void* ctx) { PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx; - float menuWidth = ImGui::GetContentRegionAvailWidth(); - if (_this->running) { style::beginDisabled(); } - ImGui::LeftLabel("IP"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::InputText(CONCAT("##_pluto_ip_", _this->name), &_this->ip[3], 16)) { + if (_this->running) { SmGui::BeginDisabled(); } + SmGui::LeftLabel("IP"); + SmGui::FillWidth(); + if (SmGui::InputText(CONCAT("##_pluto_ip_", _this->name), &_this->ip[3], 16)) { config.acquire(); config.conf["IP"] = &_this->ip[3]; config.release(true); } - ImGui::LeftLabel("Samplerate"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - - if (ImGui::Combo(CONCAT("##_pluto_sr_", _this->name), &_this->srId, _this->sampleRatesTxt.c_str())) { + SmGui::LeftLabel("Samplerate"); + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##_pluto_sr_", _this->name), &_this->srId, _this->sampleRatesTxt.c_str())) { _this->sampleRate = _this->sampleRates[_this->srId]; core::setInputSampleRate(_this->sampleRate); config.acquire(); config.conf["sampleRate"] = _this->sampleRate; config.release(true); } - if (_this->running) { style::endDisabled(); } + if (_this->running) { SmGui::EndDisabled(); } - ImGui::LeftLabel("Gain Mode"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo(CONCAT("##_gainmode_select_", _this->name), &_this->gainMode, gainModesTxt)) { + SmGui::LeftLabel("Gain Mode"); + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Combo(CONCAT("##_gainmode_select_", _this->name), &_this->gainMode, gainModesTxt)) { if (_this->running) { iio_channel_attr_write(iio_device_find_channel(_this->phy, "voltage0", false), "gain_control_mode", gainModes[_this->gainMode]); } @@ -219,10 +218,10 @@ private: config.release(true); } - ImGui::LeftLabel("PGA Gain"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (_this->gainMode) { style::beginDisabled(); } - if (ImGui::SliderFloat(CONCAT("##_gain_select_", _this->name), &_this->gain, 0, 76)) { + SmGui::LeftLabel("PGA Gain"); + if (_this->gainMode) { SmGui::BeginDisabled(); } + SmGui::FillWidth(); + if (SmGui::SliderFloat(CONCAT("##_gain_select_", _this->name), &_this->gain, 0, 76)) { if (_this->running) { iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "voltage0", false), "hardwaregain", round(_this->gain)); } @@ -230,7 +229,7 @@ private: config.conf["gain"] = _this->gain; config.release(true); } - if (_this->gainMode) { style::endDisabled(); } + if (_this->gainMode) { SmGui::EndDisabled(); } } static void worker(void* ctx) { diff --git a/source_modules/rfspace_source/src/main.cpp b/source_modules/rfspace_source/src/main.cpp index f06e8d7d..b5c1d584 100644 --- a/source_modules/rfspace_source/src/main.cpp +++ b/source_modules/rfspace_source/src/main.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -23,9 +24,9 @@ SDRPP_MOD_INFO{ ConfigManager config; -class RFSpaceSource : public ModuleManager::Instance { +class RFSpaceSourceModule : public ModuleManager::Instance { public: - RFSpaceSource(std::string name) { + RFSpaceSourceModule(std::string name) { this->name = name; handler.ctx = this; @@ -47,7 +48,7 @@ public: sigpath::sourceManager.registerSource("RFspace", &handler); } - ~RFSpaceSource() { + ~RFSpaceSourceModule() { stop(this); sigpath::sourceManager.unregisterSource("RFspace"); } @@ -82,72 +83,73 @@ private: } static void menuSelected(void* ctx) { - RFSpaceSource* _this = (RFSpaceSource*)ctx; + RFSpaceSourceModule* _this = (RFSpaceSourceModule*)ctx; core::setInputSampleRate(_this->sampleRate); gui::mainWindow.playButtonLocked = !(_this->client && _this->client->isOpen()); - spdlog::info("RFSpaceSource '{0}': Menu Select!", _this->name); + spdlog::info("RFSpaceSourceModule '{0}': Menu Select!", _this->name); } static void menuDeselected(void* ctx) { - RFSpaceSource* _this = (RFSpaceSource*)ctx; + RFSpaceSourceModule* _this = (RFSpaceSourceModule*)ctx; gui::mainWindow.playButtonLocked = false; - spdlog::info("RFSpaceSource '{0}': Menu Deselect!", _this->name); + spdlog::info("RFSpaceSourceModule '{0}': Menu Deselect!", _this->name); } static void start(void* ctx) { - RFSpaceSource* _this = (RFSpaceSource*)ctx; + RFSpaceSourceModule* _this = (RFSpaceSourceModule*)ctx; if (_this->running) { return; } // TODO: Set configuration here if (_this->client) { _this->client->start(rfspace::RFSPACE_SAMP_FORMAT_COMPLEX, rfspace::RFSPACE_SAMP_FORMAT_16BIT); } _this->running = true; - spdlog::info("RFSpaceSource '{0}': Start!", _this->name); + spdlog::info("RFSpaceSourceModule '{0}': Start!", _this->name); } static void stop(void* ctx) { - RFSpaceSource* _this = (RFSpaceSource*)ctx; + RFSpaceSourceModule* _this = (RFSpaceSourceModule*)ctx; if (!_this->running) { return; } if (_this->client) { _this->client->stop(); } _this->running = false; - spdlog::info("RFSpaceSource '{0}': Stop!", _this->name); + spdlog::info("RFSpaceSourceModule '{0}': Stop!", _this->name); } static void tune(double freq, void* ctx) { - RFSpaceSource* _this = (RFSpaceSource*)ctx; + RFSpaceSourceModule* _this = (RFSpaceSourceModule*)ctx; if (_this->running && _this->client) { _this->client->setFrequency(freq); } _this->freq = freq; - spdlog::info("RFSpaceSource '{0}': Tune: {1}!", _this->name, freq); + spdlog::info("RFSpaceSourceModule '{0}': Tune: {1}!", _this->name, freq); } static void menuHandler(void* ctx) { - RFSpaceSource* _this = (RFSpaceSource*)ctx; - float menuWidth = ImGui::GetContentRegionAvailWidth(); + RFSpaceSourceModule* _this = (RFSpaceSourceModule*)ctx; bool connected = (_this->client && _this->client->isOpen()); gui::mainWindow.playButtonLocked = !connected; - if (connected) { style::beginDisabled(); } - if (ImGui::InputText(CONCAT("##_rfspace_srv_host_", _this->name), _this->hostname, 1023)) { + if (connected) { SmGui::BeginDisabled(); } + if (SmGui::InputText(CONCAT("##_rfspace_srv_host_", _this->name), _this->hostname, 1023)) { config.acquire(); config.conf["hostname"] = _this->hostname; config.release(true); } - ImGui::SameLine(); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::InputInt(CONCAT("##_rfspace_srv_port_", _this->name), &_this->port, 0, 0)) { + SmGui::SameLine(); + SmGui::FillWidth(); + if (SmGui::InputInt(CONCAT("##_rfspace_srv_port_", _this->name), &_this->port, 0, 0)) { config.acquire(); config.conf["port"] = _this->port; config.release(true); } - if (connected) { style::endDisabled(); } + if (connected) { SmGui::EndDisabled(); } - if (_this->running) { style::beginDisabled(); } - if (!connected && ImGui::Button("Connect##rfspace_source", ImVec2(menuWidth, 0))) { + if (_this->running) { SmGui::BeginDisabled(); } + SmGui::FillWidth(); + SmGui::ForceSync(); + if (!connected && SmGui::Button("Connect##rfspace_source")) { try { if (_this->client) { _this->client.reset(); } _this->client = rfspace::connect(_this->hostname, _this->port, &_this->stream); @@ -157,18 +159,18 @@ private: spdlog::error("Could not connect to SDR: {0}", e.what()); } } - else if (connected && ImGui::Button("Disconnect##rfspace_source", ImVec2(menuWidth, 0))) { + else if (connected && SmGui::Button("Disconnect##rfspace_source")) { _this->client->close(); } - if (_this->running) { style::endDisabled(); } + if (_this->running) { SmGui::EndDisabled(); } if (connected) { - if (_this->running) { style::beginDisabled(); } + if (_this->running) { SmGui::BeginDisabled(); } - ImGui::LeftLabel("Samplerate"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo("##rfspace_source_samp_rate", &_this->srId, _this->sampleRates.txt)) { + SmGui::LeftLabel("Samplerate"); + SmGui::FillWidth(); + if (SmGui::Combo("##rfspace_source_samp_rate", &_this->srId, _this->sampleRates.txt)) { _this->sampleRate = _this->sampleRates[_this->srId]; _this->client->setSampleRate(_this->sampleRate); core::setInputSampleRate(_this->sampleRate); @@ -178,12 +180,12 @@ private: config.release(true); } - if (_this->running) { style::endDisabled(); } + if (_this->running) { SmGui::EndDisabled(); } if (_this->client->deviceId == rfspace::RFSPACE_DEV_ID_CLOUD_IQ) { - ImGui::LeftLabel("Antenna Port"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo("##rfspace_source_rf_port", &_this->rfPortId, _this->rfPorts.txt)) { + SmGui::LeftLabel("Antenna Port"); + SmGui::FillWidth(); + if (SmGui::Combo("##rfspace_source_rf_port", &_this->rfPortId, _this->rfPorts.txt)) { _this->client->setPort(_this->rfPorts[_this->rfPortId]); config.acquire(); @@ -192,9 +194,9 @@ private: } } - ImGui::LeftLabel("Gain"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderFloatWithSteps("##rfspace_source_gain", &_this->gain, -30, 0, 10, "%.0f dB")) { + SmGui::LeftLabel("Gain"); + SmGui::FillWidth(); + if (SmGui::SliderFloatWithSteps("##rfspace_source_gain", &_this->gain, -30, 0, 10, SmGui::FMT_STR_FLOAT_DB_NO_DECIMAL)) { _this->client->setGain(_this->gain); config.acquire(); @@ -202,14 +204,14 @@ private: config.release(true); } - ImGui::Text("Status:"); - ImGui::SameLine(); - ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "Connected (%s)", _this->deviceName.c_str()); + SmGui::Text("Status:"); + SmGui::SameLine(); + SmGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), _this->connectedStr.c_str()); } else { - ImGui::Text("Status:"); - ImGui::SameLine(); - ImGui::Text("Not connected"); + SmGui::Text("Status:"); + SmGui::SameLine(); + SmGui::Text("Not connected"); } } @@ -218,6 +220,8 @@ private: char buf[4096]; sprintf(buf, "%s:%05d", hostname, port); devConfName = buf; + sprintf(buf, "Connected (%s:%05d)", hostname, port); + connectedStr = buf; // Get device name if (deviceNames.find(client->deviceId) != deviceNames.end()) { @@ -301,6 +305,7 @@ private: char hostname[1024]; int port = 50000; std::string devConfName = ""; + std::string connectedStr = ""; std::string deviceName = "Unknown"; std::map deviceNames = { @@ -321,7 +326,7 @@ MOD_EXPORT void _INIT_() { def["hostname"] = "192.168.0.111"; def["port"] = 50000; def["devices"] = json::object(); - config.setPath(options::opts.root + "/rfspace_config.json"); + config.setPath(options::opts.root + "/rfspace_source_config.json"); config.load(def); config.enableAutoSave(); @@ -336,11 +341,11 @@ MOD_EXPORT void _INIT_() { } MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { - return new RFSpaceSource(name); + return new RFSpaceSourceModule(name); } MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { - delete (RFSpaceSource*)instance; + delete (RFSpaceSourceModule*)instance; } MOD_EXPORT void _END_() { diff --git a/source_modules/rtl_sdr_source/src/main.cpp b/source_modules/rtl_sdr_source/src/main.cpp index fdf6bb5b..6265c05d 100644 --- a/source_modules/rtl_sdr_source/src/main.cpp +++ b/source_modules/rtl_sdr_source/src/main.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -7,6 +6,7 @@ #include #include #include +#include #include @@ -315,12 +315,11 @@ private: static void menuHandler(void* ctx) { RTLSDRSourceModule* _this = (RTLSDRSourceModule*)ctx; - float menuWidth = ImGui::GetContentRegionAvailWidth(); - if (_this->running) { style::beginDisabled(); } - - ImGui::SetNextItemWidth(menuWidth); - if (ImGui::Combo(CONCAT("##_rtlsdr_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) { + if (_this->running) { SmGui::BeginDisabled(); } + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Combo(CONCAT("##_rtlsdr_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) { _this->selectById(_this->devId); core::setInputSampleRate(_this->sampleRate); if (_this->selectedDevName != "") { @@ -330,7 +329,7 @@ private: } } - if (ImGui::Combo(CONCAT("##_rtlsdr_sr_sel_", _this->name), &_this->srId, _this->sampleRateListTxt.c_str())) { + if (SmGui::Combo(CONCAT("##_rtlsdr_sr_sel_", _this->name), &_this->srId, _this->sampleRateListTxt.c_str())) { _this->sampleRate = sampleRates[_this->srId]; core::setInputSampleRate(_this->sampleRate); if (_this->selectedDevName != "") { @@ -340,20 +339,21 @@ private: } } - ImGui::SameLine(); - float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX(); - if (ImGui::Button(CONCAT("Refresh##_rtlsdr_refr_", _this->name), ImVec2(refreshBtnWdith, 0))) { + SmGui::SameLine(); + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Button(CONCAT("Refresh##_rtlsdr_refr_", _this->name)/*, ImVec2(refreshBtnWdith, 0)*/)) { _this->refresh(); _this->selectByName(_this->selectedDevName); core::setInputSampleRate(_this->sampleRate); } - if (_this->running) { style::endDisabled(); } + if (_this->running) { SmGui::EndDisabled(); } // Rest of rtlsdr config here - ImGui::LeftLabel("Direct Sampling"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo(CONCAT("##_rtlsdr_ds_", _this->name), &_this->directSamplingMode, directSamplingModesTxt)) { + SmGui::LeftLabel("Direct Sampling"); + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##_rtlsdr_ds_", _this->name), &_this->directSamplingMode, directSamplingModesTxt)) { if (_this->running) { rtlsdr_set_direct_sampling(_this->openDev, _this->directSamplingMode); @@ -376,9 +376,9 @@ private: } } - ImGui::LeftLabel("PPM Correction"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::InputInt(CONCAT("##_rtlsdr_ppm_", _this->name), &_this->ppm, 1, 10)) { + SmGui::LeftLabel("PPM Correction"); + SmGui::FillWidth(); + if (SmGui::InputInt(CONCAT("##_rtlsdr_ppm_", _this->name), &_this->ppm, 1, 10)) { _this->ppm = std::clamp(_this->ppm, -1000000, 1000000); if (_this->running) { rtlsdr_set_freq_correction(_this->openDev, _this->ppm); @@ -390,22 +390,42 @@ private: } } - if (_this->tunerAgc || _this->gainList.size() == 0) { style::beginDisabled(); } - ImGui::SetNextItemWidth(menuWidth); - if (ImGui::SliderInt(CONCAT("##_rtlsdr_gain_", _this->name), &_this->gainId, 0, _this->gainList.size() - 1, _this->dbTxt)) { - _this->updateGainTxt(); - if (_this->running) { - rtlsdr_set_tuner_gain(_this->openDev, _this->gainList[_this->gainId]); - } - if (_this->selectedDevName != "") { - config.acquire(); - config.conf["devices"][_this->selectedDevName]["gain"] = _this->gainId; - config.release(true); + if (_this->tunerAgc || _this->gainList.size() == 0) { SmGui::BeginDisabled(); } + SmGui::FillWidth(); + SmGui::ForceSync(); + + // TODO: FIND ANOTHER WAY + if (options::opts.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) { + rtlsdr_set_tuner_gain(_this->openDev, _this->gainList[_this->gainId]); + } + if (_this->selectedDevName != "") { + config.acquire(); + config.conf["devices"][_this->selectedDevName]["gain"] = _this->gainId; + config.release(true); + } + } + } + else { + if (ImGui::SliderInt(CONCAT("##_rtlsdr_gain_", _this->name), &_this->gainId, 0, _this->gainList.size() - 1, _this->dbTxt)) { + _this->updateGainTxt(); + if (_this->running) { + rtlsdr_set_tuner_gain(_this->openDev, _this->gainList[_this->gainId]); + } + if (_this->selectedDevName != "") { + config.acquire(); + config.conf["devices"][_this->selectedDevName]["gain"] = _this->gainId; + config.release(true); + } } } - if (_this->tunerAgc || _this->gainList.size() == 0) { style::endDisabled(); } - if (ImGui::Checkbox(CONCAT("Bias T##_rtlsdr_rtl_biast_", _this->name), &_this->biasT)) { + + if (_this->tunerAgc || _this->gainList.size() == 0) { SmGui::EndDisabled(); } + + if (SmGui::Checkbox(CONCAT("Bias T##_rtlsdr_rtl_biast_", _this->name), &_this->biasT)) { if (_this->running) { rtlsdr_set_bias_tee(_this->openDev, _this->biasT); } @@ -416,7 +436,7 @@ private: } } - if (ImGui::Checkbox(CONCAT("Offset Tuning##_rtlsdr_rtl_ofs_", _this->name), &_this->offsetTuning)) { + if (SmGui::Checkbox(CONCAT("Offset Tuning##_rtlsdr_rtl_ofs_", _this->name), &_this->offsetTuning)) { if (_this->running) { rtlsdr_set_offset_tuning(_this->openDev, _this->offsetTuning); } @@ -427,7 +447,7 @@ private: } } - if (ImGui::Checkbox(CONCAT("RTL AGC##_rtlsdr_rtl_agc_", _this->name), &_this->rtlAgc)) { + if (SmGui::Checkbox(CONCAT("RTL AGC##_rtlsdr_rtl_agc_", _this->name), &_this->rtlAgc)) { if (_this->running) { rtlsdr_set_agc_mode(_this->openDev, _this->rtlAgc); } @@ -438,7 +458,8 @@ private: } } - if (ImGui::Checkbox(CONCAT("Tuner AGC##_rtlsdr_tuner_agc_", _this->name), &_this->tunerAgc)) { + SmGui::ForceSync(); + if (SmGui::Checkbox(CONCAT("Tuner AGC##_rtlsdr_tuner_agc_", _this->name), &_this->tunerAgc)) { if (_this->running) { if (_this->tunerAgc) { rtlsdr_set_tuner_gain_mode(_this->openDev, 0); diff --git a/source_modules/rtl_tcp_source/src/main.cpp b/source_modules/rtl_tcp_source/src/main.cpp index cd8af4b4..a3649a0c 100644 --- a/source_modules/rtl_tcp_source/src/main.cpp +++ b/source_modules/rtl_tcp_source/src/main.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -188,27 +189,24 @@ private: static void menuHandler(void* ctx) { RTLTCPSourceModule* _this = (RTLTCPSourceModule*)ctx; - float menuWidth = ImGui::GetContentRegionAvailWidth(); - float portWidth = ImGui::CalcTextSize("00000").x + 20; - if (_this->running) { style::beginDisabled(); } + if (_this->running) { SmGui::BeginDisabled(); } - ImGui::SetNextItemWidth(menuWidth - portWidth); - if (ImGui::InputText(CONCAT("##_ip_select_", _this->name), _this->ip, 1024)) { + if (SmGui::InputText(CONCAT("##_ip_select_", _this->name), _this->ip, 1024)) { config.acquire(); config.conf["host"] = std::string(_this->ip); config.release(true); } - ImGui::SameLine(); - ImGui::SetNextItemWidth(portWidth); - if (ImGui::InputInt(CONCAT("##_port_select_", _this->name), &_this->port, 0)) { + SmGui::SameLine(); + SmGui::FillWidth(); + if (SmGui::InputInt(CONCAT("##_port_select_", _this->name), &_this->port, 0)) { config.acquire(); config.conf["port"] = _this->port; config.release(true); } - ImGui::SetNextItemWidth(menuWidth); - if (ImGui::Combo(CONCAT("##_rtltcp_sr_", _this->name), &_this->srId, _this->srTxt.c_str())) { + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##_rtltcp_sr_", _this->name), &_this->srId, _this->srTxt.c_str())) { _this->sampleRate = sampleRates[_this->srId]; core::setInputSampleRate(_this->sampleRate); config.acquire(); @@ -216,11 +214,11 @@ private: config.release(true); } - if (_this->running) { style::endDisabled(); } + if (_this->running) { SmGui::EndDisabled(); } - ImGui::LeftLabel("Direct Sampling"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo(CONCAT("##_rtltcp_ds_", _this->name), &_this->directSamplingMode, "Disabled\0I branch\0Q branch\0")) { + SmGui::LeftLabel("Direct Sampling"); + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##_rtltcp_ds_", _this->name), &_this->directSamplingMode, "Disabled\0I branch\0Q branch\0")) { if (_this->running) { _this->client.setDirectSampling(_this->directSamplingMode); _this->client.setGainIndex(_this->gain); @@ -230,9 +228,9 @@ private: config.release(true); } - ImGui::LeftLabel("PPM Correction"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::InputInt(CONCAT("##_rtltcp_ppm_", _this->name), &_this->ppm, 1, 10)) { + SmGui::LeftLabel("PPM Correction"); + SmGui::FillWidth(); + if (SmGui::InputInt(CONCAT("##_rtltcp_ppm_", _this->name), &_this->ppm, 1, 10)) { if (_this->running) { _this->client.setPPM(_this->ppm); } @@ -241,9 +239,9 @@ private: config.release(true); } - if (_this->tunerAGC) { style::beginDisabled(); } - ImGui::SetNextItemWidth(menuWidth); - if (ImGui::SliderInt(CONCAT("##_gain_select_", _this->name), &_this->gain, 0, 28, "")) { + if (_this->tunerAGC) { SmGui::BeginDisabled(); } + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_gain_select_", _this->name), &_this->gain, 0, 28, SmGui::FMT_STR_NONE)) { if (_this->running) { _this->client.setGainIndex(_this->gain); } @@ -251,9 +249,9 @@ private: config.conf["gainIndex"] = _this->gain; config.release(true); } - if (_this->tunerAGC) { style::endDisabled(); } + if (_this->tunerAGC) { SmGui::EndDisabled(); } - if (ImGui::Checkbox(CONCAT("Bias-T##_biast_select_", _this->name), &_this->biasTee)) { + if (SmGui::Checkbox(CONCAT("Bias-T##_biast_select_", _this->name), &_this->biasTee)) { if (_this->running) { _this->client.setBiasTee(_this->biasTee); } @@ -262,7 +260,7 @@ private: config.release(true); } - if (ImGui::Checkbox(CONCAT("Offset Tuning##_biast_select_", _this->name), &_this->offsetTuning)) { + if (SmGui::Checkbox(CONCAT("Offset Tuning##_biast_select_", _this->name), &_this->offsetTuning)) { if (_this->running) { _this->client.setOffsetTuning(_this->offsetTuning); } @@ -271,7 +269,7 @@ private: config.release(true); } - if (ImGui::Checkbox("RTL AGC", &_this->rtlAGC)) { + if (SmGui::Checkbox("RTL AGC", &_this->rtlAGC)) { if (_this->running) { _this->client.setAGCMode(_this->rtlAGC); if (!_this->rtlAGC) { @@ -283,7 +281,8 @@ private: config.release(true); } - if (ImGui::Checkbox("Tuner AGC", &_this->tunerAGC)) { + SmGui::ForceSync(); + if (SmGui::Checkbox("Tuner AGC", &_this->tunerAGC)) { if (_this->running) { _this->client.setGainMode(!_this->tunerAGC); if (!_this->tunerAGC) { diff --git a/source_modules/sddc_source/src/main.cpp b/source_modules/sddc_source/src/main.cpp index e86fbde8..b0499111 100644 --- a/source_modules/sddc_source/src/main.cpp +++ b/source_modules/sddc_source/src/main.cpp @@ -29,6 +29,8 @@ public: AirspyHFSourceModule(std::string name) { this->name = name; + if (options::opts.serverMode) { return; } + sampleRate = 768000.0; handler.ctx = this; diff --git a/source_modules/sdrplay_source/src/main.cpp b/source_modules/sdrplay_source/src/main.cpp index 4e3f177c..c48fe734 100644 --- a/source_modules/sdrplay_source/src/main.cpp +++ b/source_modules/sdrplay_source/src/main.cpp @@ -8,7 +8,7 @@ #include #include #include - +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -697,14 +697,12 @@ private: static void menuHandler(void* ctx) { SDRPlaySourceModule* _this = (SDRPlaySourceModule*)ctx; - float menuWidth = ImGui::GetContentRegionAvailWidth(); - if (_this->running) { style::beginDisabled(); } + if (_this->running) { SmGui::BeginDisabled(); } - ImGui::SetNextItemWidth(menuWidth); - - - if (ImGui::Combo(CONCAT("##sdrplay_dev", _this->name), &_this->devId, _this->devListTxt.c_str())) { + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Combo(CONCAT("##sdrplay_dev", _this->name), &_this->devId, _this->devListTxt.c_str())) { _this->selectById(_this->devId); config.acquire(); config.conf["device"] = _this->devNameList[_this->devId]; @@ -712,7 +710,7 @@ private: } if (_this->ifModeId == 0) { - if (ImGui::Combo(CONCAT("##sdrplay_sr", _this->name), &_this->srId, sampleRatesTxt)) { + if (SmGui::Combo(CONCAT("##sdrplay_sr", _this->name), &_this->srId, sampleRatesTxt)) { _this->sampleRate = sampleRates[_this->srId]; if (_this->bandwidthId == 8) { _this->bandwidth = preferedBandwidth[_this->srId]; @@ -723,15 +721,16 @@ private: config.release(true); } - ImGui::SameLine(); - float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX(); - if (ImGui::Button(CONCAT("Refresh##sdrplay_refresh", _this->name), ImVec2(refreshBtnWdith, 0))) { + SmGui::SameLine(); + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Button(CONCAT("Refresh##sdrplay_refresh", _this->name))) { _this->refresh(); _this->selectByName(_this->selectedName); } - ImGui::SetNextItemWidth(menuWidth); - if (ImGui::Combo(CONCAT("##sdrplay_bw", _this->name), &_this->bandwidthId, bandwidthsTxt)) { + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##sdrplay_bw", _this->name), &_this->bandwidthId, bandwidthsTxt)) { _this->bandwidth = (_this->bandwidthId == 8) ? preferedBandwidth[_this->srId] : bandwidths[_this->bandwidthId]; if (_this->running) { _this->channelParams->tunerParams.bwType = _this->bandwidth; @@ -743,16 +742,18 @@ private: } } else { - if (ImGui::Button(CONCAT("Refresh##sdrplay_refresh", _this->name), ImVec2(menuWidth, 0))) { + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Button(CONCAT("Refresh##sdrplay_refresh", _this->name))) { _this->refresh(); _this->selectByName(_this->selectedName); } } - ImGui::Text("IF Mode"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo(CONCAT("##sdrplay_ifmode", _this->name), &_this->ifModeId, ifModeTxt)) { + SmGui::LeftLabel("IF Mode"); + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Combo(CONCAT("##sdrplay_ifmode", _this->name), &_this->ifModeId, ifModeTxt)) { if (_this->ifModeId != 0) { _this->bandwidth = ifModes[_this->ifModeId].bw; _this->sampleRate = ifModes[_this->ifModeId].effectiveSamplerate; @@ -770,13 +771,12 @@ private: config.release(true); } - if (_this->running) { style::endDisabled(); } + if (_this->running) { SmGui::EndDisabled(); } if (_this->selectedName != "") { - ImGui::PushItemWidth(menuWidth - ImGui::CalcTextSize("LNA Gain").x - 10); - ImGui::LeftLabel("LNA Gain"); - float pos = ImGui::GetCursorPosX(); - if (ImGui::SliderInt(CONCAT("##sdrplay_lna_gain", _this->name), &_this->lnaGain, _this->lnaSteps - 1, 0, "")) { + SmGui::LeftLabel("LNA Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##sdrplay_lna_gain", _this->name), &_this->lnaGain, _this->lnaSteps - 1, 0, SmGui::FMT_STR_NONE)) { if (_this->running) { _this->channelParams->tunerParams.gain.LNAstate = _this->lnaGain; sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Tuner_Gr, sdrplay_api_Update_Ext1_None); @@ -786,10 +786,10 @@ private: config.release(true); } - if (_this->agc > 0) { style::beginDisabled(); } - ImGui::LeftLabel("IF Gain"); - ImGui::SetCursorPosX(pos); - if (ImGui::SliderInt(CONCAT("##sdrplay_gain", _this->name), &_this->gain, 59, 20, "")) { + if (_this->agc > 0) { SmGui::BeginDisabled(); } + SmGui::LeftLabel("IF Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##sdrplay_gain", _this->name), &_this->gain, 59, 20, SmGui::FMT_STR_NONE)) { if (_this->running) { _this->channelParams->tunerParams.gain.gRdB = _this->gain; sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Tuner_Gr, sdrplay_api_Update_Ext1_None); @@ -798,8 +798,7 @@ private: config.conf["devices"][_this->selectedName]["ifGain"] = _this->gain; config.release(true); } - ImGui::PopItemWidth(); - if (_this->agc > 0) { style::endDisabled(); } + if (_this->agc > 0) { SmGui::EndDisabled(); } if (_this->agcParamEdit) { @@ -831,7 +830,8 @@ private: } } - if (ImGui::Checkbox(CONCAT("IF AGC##sdrplay_agc", _this->name), &_this->agc)) { + SmGui::ForceSync(); + if (SmGui::Checkbox(CONCAT("IF AGC##sdrplay_agc", _this->name), &_this->agc)) { if (_this->running) { _this->channelParams->ctrlParams.agc.enable = _this->agc ? sdrplay_api_AGC_CTRL_EN : sdrplay_api_AGC_DISABLE; if (_this->agc) { @@ -852,8 +852,10 @@ private: config.conf["devices"][_this->selectedName]["agc"] = _this->agc; config.release(true); } - ImGui::SameLine(); - if (ImGui::Button(CONCAT("Parameters##sdrplay_agc_edit_btn", _this->name), ImVec2(menuWidth - ImGui::GetCursorPosX(), 0))) { + SmGui::SameLine(); + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Button(CONCAT("Parameters##sdrplay_agc_edit_btn", _this->name))) { _this->agcParamEdit = true; _this->_agcAttack = _this->agcAttack; _this->_agcDecay = _this->agcDecay; @@ -864,100 +866,101 @@ private: switch (_this->openDev.hwVer) { case SDRPLAY_RSP1_ID: - _this->RSP1Menu(menuWidth); + _this->RSP1Menu(); break; case SDRPLAY_RSP1A_ID: - _this->RSP1AMenu(menuWidth); + _this->RSP1AMenu(); break; case SDRPLAY_RSP2_ID: - _this->RSP2Menu(menuWidth); + _this->RSP2Menu(); break; case SDRPLAY_RSPduo_ID: - _this->RSPduoMenu(menuWidth); + _this->RSPduoMenu(); break; case SDRPLAY_RSPdx_ID: - _this->RSPdxMenu(menuWidth); + _this->RSPdxMenu(); break; default: - _this->RSPUnsupportedMenu(menuWidth); + _this->RSPUnsupportedMenu(); break; } } else { - ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "No device available"); + SmGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "No device available"); } } bool agcParamMenu(bool& valid) { bool open = true; gui::mainWindow.lockWaterfallControls = true; - ImGui::OpenPopup("Edit##sdrplay_source_edit_agc_params_"); - if (ImGui::BeginPopup("Edit##sdrplay_source_edit_agc_params_", ImGuiWindowFlags_NoResize)) { + SmGui::OpenPopup("Edit##sdrplay_source_edit_agc_params_"); + if (SmGui::BeginPopup("Edit##sdrplay_source_edit_agc_params_", ImGuiWindowFlags_NoResize)) { + if (SmGui::BeginTable(("sdrplay_source_agc_param_tbl" + name).c_str(), 2)) { + SmGui::TableNextRow(); + SmGui::TableSetColumnIndex(0); + SmGui::LeftLabel("Attack"); + SmGui::TableSetColumnIndex(1); + SmGui::SetNextItemWidth(100); + SmGui::InputInt("ms##sdrplay_source_agc_attack", &_agcAttack); + _agcAttack = std::clamp(_agcAttack, 0, 65535); - ImGui::BeginTable(("sdrplay_source_agc_param_tbl" + name).c_str(), 2); + SmGui::TableNextRow(); + SmGui::TableSetColumnIndex(0); + SmGui::LeftLabel("Decay"); + SmGui::TableSetColumnIndex(1); + SmGui::SetNextItemWidth(100); + SmGui::InputInt("ms##sdrplay_source_agc_decay", &_agcDecay); + _agcDecay = std::clamp(_agcDecay, 0, 65535); - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::LeftLabel("Attack"); - ImGui::TableSetColumnIndex(1); - ImGui::SetNextItemWidth(100); - ImGui::InputInt("ms##sdrplay_source_agc_attack", &_agcAttack); - _agcAttack = std::clamp(_agcAttack, 0, 65535); + SmGui::TableNextRow(); + SmGui::TableSetColumnIndex(0); + SmGui::LeftLabel("Decay Delay"); + SmGui::TableSetColumnIndex(1); + SmGui::SetNextItemWidth(100); + SmGui::InputInt("ms##sdrplay_source_agc_decay_delay", &_agcDecayDelay); + _agcDecayDelay = std::clamp(_agcDecayDelay, 0, 65535); - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::LeftLabel("Decay"); - ImGui::TableSetColumnIndex(1); - ImGui::SetNextItemWidth(100); - ImGui::InputInt("ms##sdrplay_source_agc_decay", &_agcDecay); - _agcDecay = std::clamp(_agcDecay, 0, 65535); + SmGui::TableNextRow(); + SmGui::TableSetColumnIndex(0); + SmGui::LeftLabel("Decay Threshold"); + SmGui::TableSetColumnIndex(1); + SmGui::SetNextItemWidth(100); + SmGui::InputInt("dB##sdrplay_source_agc_decay_thresh", &_agcDecayThreshold); + _agcDecayThreshold = std::clamp(_agcDecayThreshold, 0, 100); - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::LeftLabel("Decay Delay"); - ImGui::TableSetColumnIndex(1); - ImGui::SetNextItemWidth(100); - ImGui::InputInt("ms##sdrplay_source_agc_decay_delay", &_agcDecayDelay); - _agcDecayDelay = std::clamp(_agcDecayDelay, 0, 65535); + SmGui::TableNextRow(); + SmGui::TableSetColumnIndex(0); + SmGui::LeftLabel("Setpoint"); + SmGui::TableSetColumnIndex(1); + SmGui::SetNextItemWidth(100); + SmGui::InputInt("dBFS##sdrplay_source_agc_setpoint", &_agcSetPoint); + _agcSetPoint = std::clamp(_agcSetPoint, -60, -20); - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::LeftLabel("Decay Threshold"); - ImGui::TableSetColumnIndex(1); - ImGui::SetNextItemWidth(100); - ImGui::InputInt("dB##sdrplay_source_agc_decay_thresh", &_agcDecayThreshold); - _agcDecayThreshold = std::clamp(_agcDecayThreshold, 0, 100); + SmGui::EndTable(); + } - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::LeftLabel("Setpoint"); - ImGui::TableSetColumnIndex(1); - ImGui::SetNextItemWidth(100); - ImGui::InputInt("dBFS##sdrplay_source_agc_setpoint", &_agcSetPoint); - _agcSetPoint = std::clamp(_agcSetPoint, -60, -20); - - ImGui::EndTable(); - - if (ImGui::Button("Apply")) { + SmGui::ForceSync(); + if (SmGui::Button("Apply")) { open = false; valid = true; } - ImGui::SameLine(); - if (ImGui::Button("Cancel")) { + SmGui::SameLine(); + SmGui::ForceSync(); + if (SmGui::Button("Cancel")) { open = false; valid = false; } - ImGui::EndPopup(); + SmGui::EndPopup(); } return open; } - void RSP1Menu(float menuWidth) { + void RSP1Menu() { // No options? } - void RSP1AMenu(float menuWidth) { - if (ImGui::Checkbox(CONCAT("FM Notch##sdrplay_rsp1a_fmnotch", name), &rsp1a_fmNotch)) { + void RSP1AMenu() { + if (SmGui::Checkbox(CONCAT("FM Notch##sdrplay_rsp1a_fmnotch", name), &rsp1a_fmNotch)) { if (running) { openDevParams->devParams->rsp1aParams.rfNotchEnable = rsp1a_fmNotch; sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_Rsp1a_RfNotchControl, sdrplay_api_Update_Ext1_None); @@ -966,7 +969,7 @@ private: config.conf["devices"][selectedName]["fmNotch"] = rsp1a_fmNotch; config.release(true); } - if (ImGui::Checkbox(CONCAT("DAB Notch##sdrplay_rsp1a_dabnotch", name), &rsp1a_dabNotch)) { + if (SmGui::Checkbox(CONCAT("DAB Notch##sdrplay_rsp1a_dabnotch", name), &rsp1a_dabNotch)) { if (running) { openDevParams->devParams->rsp1aParams.rfNotchEnable = rsp1a_dabNotch; sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_Rsp1a_RfDabNotchControl, sdrplay_api_Update_Ext1_None); @@ -975,7 +978,7 @@ private: config.conf["devices"][selectedName]["dabNotch"] = rsp1a_dabNotch; config.release(true); } - if (ImGui::Checkbox(CONCAT("Bias-T##sdrplay_rsp1a_biast", name), &rsp1a_biasT)) { + if (SmGui::Checkbox(CONCAT("Bias-T##sdrplay_rsp1a_biast", name), &rsp1a_biasT)) { if (running) { channelParams->rsp1aTunerParams.biasTEnable = rsp1a_biasT; sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_Rsp1a_BiasTControl, sdrplay_api_Update_Ext1_None); @@ -986,10 +989,10 @@ private: } } - void RSP2Menu(float menuWidth) { - ImGui::LeftLabel("Antenna"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo(CONCAT("##sdrplay_rsp2_ant", name), &rsp2_antennaPort, rsp2_antennaPortsTxt)) { + void RSP2Menu() { + SmGui::LeftLabel("Antenna"); + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##sdrplay_rsp2_ant", name), &rsp2_antennaPort, rsp2_antennaPortsTxt)) { if (running) { channelParams->rsp2TunerParams.antennaSel = rsp2_antennaPorts[rsp2_antennaPort]; channelParams->rsp2TunerParams.amPortSel = (rsp2_antennaPort == 2) ? sdrplay_api_Rsp2_AMPORT_1 : sdrplay_api_Rsp2_AMPORT_2; @@ -1000,7 +1003,7 @@ private: config.conf["devices"][selectedName]["antenna"] = rsp2_antennaPort; config.release(true); } - if (ImGui::Checkbox(CONCAT("MW/FM Notch##sdrplay_rsp2_notch", name), &rsp2_notch)) { + if (SmGui::Checkbox(CONCAT("MW/FM Notch##sdrplay_rsp2_notch", name), &rsp2_notch)) { if (running) { channelParams->rsp2TunerParams.rfNotchEnable = rsp2_notch; sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_Rsp2_RfNotchControl, sdrplay_api_Update_Ext1_None); @@ -1009,7 +1012,7 @@ private: config.conf["devices"][selectedName]["notch"] = rsp2_notch; config.release(true); } - if (ImGui::Checkbox(CONCAT("Bias-T##sdrplay_rsp2_biast", name), &rsp2_biasT)) { + if (SmGui::Checkbox(CONCAT("Bias-T##sdrplay_rsp2_biast", name), &rsp2_biasT)) { if (running) { channelParams->rsp2TunerParams.biasTEnable = rsp2_biasT; sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_Rsp2_BiasTControl, sdrplay_api_Update_Ext1_None); @@ -1020,11 +1023,10 @@ private: } } - void RSPduoMenu(float menuWidth) { - ImGui::LeftLabel("Antenna"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - - if (ImGui::Combo(CONCAT("##sdrplay_rspduo_ant", name), &rspduo_antennaPort, rspduo_antennaPortsTxt)) { + void RSPduoMenu() { + SmGui::LeftLabel("Antenna"); + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##sdrplay_rspduo_ant", name), &rspduo_antennaPort, rspduo_antennaPortsTxt)) { if (running) { rspDuoSelectAntennaPort(rspduo_antennaPort); } @@ -1032,7 +1034,7 @@ private: config.conf["devices"][selectedName]["antenna"] = rspduo_antennaPort; config.release(true); } - if (ImGui::Checkbox(CONCAT("FM Notch##sdrplay_rspduo_notch", name), &rspduo_fmNotch)) { + if (SmGui::Checkbox(CONCAT("FM Notch##sdrplay_rspduo_notch", name), &rspduo_fmNotch)) { if (running) { channelParams->rspDuoTunerParams.rfNotchEnable = rspduo_fmNotch; sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_RspDuo_RfNotchControl, sdrplay_api_Update_Ext1_None); @@ -1041,7 +1043,7 @@ private: config.conf["devices"][selectedName]["fmNotch"] = rspduo_fmNotch; config.release(true); } - if (ImGui::Checkbox(CONCAT("DAB Notch##sdrplay_rspduo_dabnotch", name), &rspduo_dabNotch)) { + if (SmGui::Checkbox(CONCAT("DAB Notch##sdrplay_rspduo_dabnotch", name), &rspduo_dabNotch)) { if (running) { channelParams->rspDuoTunerParams.rfDabNotchEnable = rspduo_dabNotch; sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_RspDuo_RfDabNotchControl, sdrplay_api_Update_Ext1_None); @@ -1050,7 +1052,7 @@ private: config.conf["devices"][selectedName]["dabNotch"] = rspduo_dabNotch; config.release(true); } - if (ImGui::Checkbox(CONCAT("AM Notch##sdrplay_rspduo_dabnotch", name), &rspduo_amNotch)) { + if (SmGui::Checkbox(CONCAT("AM Notch##sdrplay_rspduo_dabnotch", name), &rspduo_amNotch)) { if (running) { channelParams->rspDuoTunerParams.tuner1AmNotchEnable = rspduo_amNotch; sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_RspDuo_Tuner1AmNotchControl, sdrplay_api_Update_Ext1_None); @@ -1059,7 +1061,7 @@ private: config.conf["devices"][selectedName]["amNotch"] = rspduo_amNotch; config.release(true); } - if (ImGui::Checkbox(CONCAT("Bias-T##sdrplay_rspduo_biast", name), &rspduo_biasT)) { + if (SmGui::Checkbox(CONCAT("Bias-T##sdrplay_rspduo_biast", name), &rspduo_biasT)) { if (running) { channelParams->rspDuoTunerParams.biasTEnable = rspduo_biasT; sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_RspDuo_BiasTControl, sdrplay_api_Update_Ext1_None); @@ -1070,10 +1072,10 @@ private: } } - void RSPdxMenu(float menuWidth) { - ImGui::LeftLabel("Antenna"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo(CONCAT("##sdrplay_rspdx_ant", name), &rspdx_antennaPort, rspdx_antennaPortsTxt)) { + void RSPdxMenu() { + SmGui::LeftLabel("Antenna"); + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##sdrplay_rspdx_ant", name), &rspdx_antennaPort, rspdx_antennaPortsTxt)) { if (running) { openDevParams->devParams->rspDxParams.antennaSel = rspdx_antennaPorts[rspdx_antennaPort]; sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_None, sdrplay_api_Update_RspDx_AntennaControl); @@ -1083,7 +1085,7 @@ private: config.release(true); } - if (ImGui::Checkbox(CONCAT("FM Notch##sdrplay_rspdx_fmnotch", name), &rspdx_fmNotch)) { + if (SmGui::Checkbox(CONCAT("FM Notch##sdrplay_rspdx_fmnotch", name), &rspdx_fmNotch)) { if (running) { openDevParams->devParams->rspDxParams.rfNotchEnable = rspdx_fmNotch; sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_None, sdrplay_api_Update_RspDx_RfNotchControl); @@ -1092,7 +1094,7 @@ private: config.conf["devices"][selectedName]["fmNotch"] = rspdx_fmNotch; config.release(true); } - if (ImGui::Checkbox(CONCAT("DAB Notch##sdrplay_rspdx_dabnotch", name), &rspdx_dabNotch)) { + if (SmGui::Checkbox(CONCAT("DAB Notch##sdrplay_rspdx_dabnotch", name), &rspdx_dabNotch)) { if (running) { openDevParams->devParams->rspDxParams.rfDabNotchEnable = rspdx_dabNotch; sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_None, sdrplay_api_Update_RspDx_RfDabNotchControl); @@ -1101,7 +1103,7 @@ private: config.conf["devices"][selectedName]["dabNotch"] = rspdx_dabNotch; config.release(true); } - if (ImGui::Checkbox(CONCAT("Bias-T##sdrplay_rspdx_biast", name), &rspdx_biasT)) { + if (SmGui::Checkbox(CONCAT("Bias-T##sdrplay_rspdx_biast", name), &rspdx_biasT)) { if (running) { openDevParams->devParams->rspDxParams.biasTEnable = rspdx_biasT; sdrplay_api_Update(openDev.dev, openDev.tuner, sdrplay_api_Update_None, sdrplay_api_Update_RspDx_BiasTControl); @@ -1112,8 +1114,8 @@ private: } } - void RSPUnsupportedMenu(float menuWidth) { - ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Device currently unsupported"); + void RSPUnsupportedMenu() { + SmGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Device currently unsupported"); } static void streamCB(short* xi, short* xq, sdrplay_api_StreamCbParamsT* params, diff --git a/source_modules/sdrpp_server_source/CMakeLists.txt b/source_modules/sdrpp_server_source/CMakeLists.txt new file mode 100644 index 00000000..477e6484 --- /dev/null +++ b/source_modules/sdrpp_server_source/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.13) +project(sdrpp_server_source) + +file(GLOB SRC "src/*.cpp") + +add_library(sdrpp_server_source SHARED ${SRC}) +target_link_libraries(sdrpp_server_source PRIVATE sdrpp_core) +set_target_properties(sdrpp_server_source PROPERTIES PREFIX "") + +target_include_directories(sdrpp_server_source PRIVATE "src/") + +if (MSVC) + target_compile_options(sdrpp_server_source PRIVATE /O2 /Ob2 /std:c++17 /EHsc) +elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(sdrpp_server_source PRIVATE -O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup) +else () + target_compile_options(sdrpp_server_source PRIVATE -O3 -std=c++17) +endif () + +if(WIN32) + target_link_libraries(sdrpp_server_source PRIVATE wsock32 ws2_32) +endif() + +# Install directives +install(TARGETS sdrpp_server_source DESTINATION lib/sdrpp/plugins) \ No newline at end of file diff --git a/source_modules/sdrpp_server_source/src/main.cpp b/source_modules/sdrpp_server_source/src/main.cpp new file mode 100644 index 00000000..7a1c15c3 --- /dev/null +++ b/source_modules/sdrpp_server_source/src/main.cpp @@ -0,0 +1,279 @@ +#include "sdrpp_server_client.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CONCAT(a, b) ((std::string(a) + b).c_str()) + +SDRPP_MOD_INFO{ + /* Name: */ "sdrpp_server_source", + /* Description: */ "SDR++ Server source module for SDR++", + /* Author: */ "Ryzerth", + /* Version: */ 0, 1, 0, + /* Max instances */ 1 +}; + +ConfigManager config; + +class SDRPPServerSourceModule : public ModuleManager::Instance { +public: + SDRPPServerSourceModule(std::string name) { + this->name = name; + + // Yeah no server-ception, sorry... + if (options::opts.serverMode) { return; } + + // Initialize lists + sampleTypeList.define("Int8", dsp::PCM_TYPE_I8); + sampleTypeList.define("Int16", dsp::PCM_TYPE_I16); + sampleTypeList.define("Float32", dsp::PCM_TYPE_F32); + sampleTypeId = sampleTypeList.valueId(dsp::PCM_TYPE_I16); + + handler.ctx = this; + handler.selectHandler = menuSelected; + handler.deselectHandler = menuDeselected; + handler.menuHandler = menuHandler; + handler.startHandler = start; + handler.stopHandler = stop; + handler.tuneHandler = tune; + handler.stream = &stream; + + // Load config + config.acquire(); + std::string hostStr = config.conf["hostname"]; + strcpy(hostname, hostStr.c_str()); + port = config.conf["port"]; + config.release(); + + sigpath::sourceManager.registerSource("SDR++ Server", &handler); + } + + ~SDRPPServerSourceModule() { + stop(this); + sigpath::sourceManager.unregisterSource("SDR++ Server"); + } + + void postInit() {} + + void enable() { + enabled = true; + } + + void disable() { + enabled = false; + } + + bool isEnabled() { + return enabled; + } + +private: + std::string getBandwdithScaled(double bw) { + char buf[1024]; + if (bw >= 1000000.0) { + sprintf(buf, "%.1lfMHz", bw / 1000000.0); + } + else if (bw >= 1000.0) { + sprintf(buf, "%.1lfKHz", bw / 1000.0); + } + else { + sprintf(buf, "%.1lfHz", bw); + } + return std::string(buf); + } + + static void menuSelected(void* ctx) { + SDRPPServerSourceModule* _this = (SDRPPServerSourceModule*)ctx; + if (_this->client) { + core::setInputSampleRate(_this->client->getSampleRate()); + } + gui::mainWindow.playButtonLocked = !(_this->client && _this->client->isOpen()); + spdlog::info("SDRPPServerSourceModule '{0}': Menu Select!", _this->name); + } + + static void menuDeselected(void* ctx) { + SDRPPServerSourceModule* _this = (SDRPPServerSourceModule*)ctx; + gui::mainWindow.playButtonLocked = false; + spdlog::info("SDRPPServerSourceModule '{0}': Menu Deselect!", _this->name); + } + + static void start(void* ctx) { + SDRPPServerSourceModule* _this = (SDRPPServerSourceModule*)ctx; + if (_this->running) { return; } + + // TODO: Set configuration here + if (_this->client) { _this->client->start(); } + + _this->running = true; + spdlog::info("SDRPPServerSourceModule '{0}': Start!", _this->name); + } + + static void stop(void* ctx) { + SDRPPServerSourceModule* _this = (SDRPPServerSourceModule*)ctx; + if (!_this->running) { return; } + + if (_this->client) { _this->client->stop(); } + + _this->running = false; + spdlog::info("SDRPPServerSourceModule '{0}': Stop!", _this->name); + } + + static void tune(double freq, void* ctx) { + SDRPPServerSourceModule* _this = (SDRPPServerSourceModule*)ctx; + if (_this->running && _this->client) { + _this->client->setFrequency(freq); + } + _this->freq = freq; + spdlog::info("SDRPPServerSourceModule '{0}': Tune: {1}!", _this->name, freq); + } + + static void menuHandler(void* ctx) { + SDRPPServerSourceModule* _this = (SDRPPServerSourceModule*)ctx; + float menuWidth = ImGui::GetContentRegionAvailWidth(); + + bool connected = (_this->client && _this->client->isOpen()); + gui::mainWindow.playButtonLocked = !connected; + + if (connected) { style::beginDisabled(); } + if (ImGui::InputText(CONCAT("##sdrpp_srv_srv_host_", _this->name), _this->hostname, 1023)) { + config.acquire(); + config.conf["hostname"] = _this->hostname; + config.release(true); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); + if (ImGui::InputInt(CONCAT("##sdrpp_srv_srv_port_", _this->name), &_this->port, 0, 0)) { + config.acquire(); + config.conf["port"] = _this->port; + config.release(true); + } + if (connected) { style::endDisabled(); } + + if (_this->running) { style::beginDisabled(); } + if (!connected && ImGui::Button("Connect##sdrpp_srv_source", ImVec2(menuWidth, 0))) { + try { + if (_this->client) { _this->client.reset(); } + _this->client = server::connect(_this->hostname, _this->port, &_this->stream); + _this->deviceInit(); + } + catch (std::exception e) { + spdlog::error("Could not connect to SDR: {0}", e.what()); + } + } + else if (connected && ImGui::Button("Disconnect##sdrpp_srv_source", ImVec2(menuWidth, 0))) { + _this->client->close(); + } + if (_this->running) { style::endDisabled(); } + + + if (connected) { + ImGui::LeftLabel("Sample type"); + ImGui::FillWidth(); + if (ImGui::Combo("##sdrpp_srv_source_samp_type", &_this->sampleTypeId, _this->sampleTypeList.txt)) { + _this->client->setSampleType(_this->sampleTypeList[_this->sampleTypeId]); + + // Save config + config.acquire(); + config.conf["servers"][_this->devConfName]["sampleType"] = _this->sampleTypeList.key(_this->sampleTypeId); + config.release(true); + } + + bool dummy = false; + style::beginDisabled(); + ImGui::Checkbox("Compression", &dummy); + dummy = true; + ImGui::Checkbox("Full IQ", &dummy); + style::endDisabled(); + + // Calculate datarate + _this->frametimeCounter += ImGui::GetIO().DeltaTime; + if (_this->frametimeCounter >= 0.2f) { + _this->datarate = ((float)_this->client->bytes / (_this->frametimeCounter * 1024.0f * 1024.0f)) * 8; + _this->frametimeCounter = 0; + _this->client->bytes = 0; + } + + ImGui::Text("Status:"); + ImGui::SameLine(); + ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "Connected (%.3f Mbit/s)", _this->datarate); + + ImGui::CollapsingHeader("Source [REMOTE]", ImGuiTreeNodeFlags_DefaultOpen); + + _this->client->showMenu(); + } + else { + ImGui::Text("Status:"); + ImGui::SameLine(); + ImGui::Text("Not connected (--.--- Mbit/s)"); + } + } + + void deviceInit() { + // Generate the config name + char buf[4096]; + sprintf(buf, "%s:%05d", hostname, port); + devConfName = buf; + + // Load settings + sampleTypeId = sampleTypeList.valueId(dsp::PCM_TYPE_I16); + if (config.conf["servers"][devConfName].contains("sampleType")) { + std::string key = config.conf["servers"][devConfName]["sampleType"]; + if (sampleTypeList.keyExists(key)) { sampleTypeId = sampleTypeList.keyId(key); } + } + + // Set settings + client->setSampleType(sampleTypeList[sampleTypeId]); + } + + std::string name; + bool enabled = true; + bool running = false; + + double freq; + + float datarate = 0; + float frametimeCounter = 0; + + char hostname[1024]; + int port = 50000; + std::string devConfName = ""; + + dsp::stream stream; + SourceManager::SourceHandler handler; + + OptionList sampleTypeList; + int sampleTypeId; + + server::Client client; +}; + +MOD_EXPORT void _INIT_() { + json def = json({}); + def["hostname"] = "localhost"; + def["port"] = 5259; + def["servers"] = json::object(); + config.setPath(options::opts.root + "/sdrpp_server_source_config.json"); + config.load(def); + config.enableAutoSave(); +} + +MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { + return new SDRPPServerSourceModule(name); +} + +MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { + delete (SDRPPServerSourceModule*)instance; +} + +MOD_EXPORT void _END_() { + config.disableAutoSave(); + config.save(); +} \ No newline at end of file diff --git a/source_modules/sdrpp_server_source/src/sdrpp_server_client.cpp b/source_modules/sdrpp_server_source/src/sdrpp_server_client.cpp new file mode 100644 index 00000000..0aeb617d --- /dev/null +++ b/source_modules/sdrpp_server_source/src/sdrpp_server_client.cpp @@ -0,0 +1,233 @@ +#include "sdrpp_server_client.h" +#include +#include +#include +#include + +using namespace std::chrono_literals; + +namespace server { + ClientClass::ClientClass(net::Conn conn, dsp::stream* out) { + client = std::move(conn); + output = out; + + // Allocate buffers + rbuffer = new uint8_t[SERVER_MAX_PACKET_SIZE]; + sbuffer = new uint8_t[SERVER_MAX_PACKET_SIZE]; + + // Initialize headers + r_pkt_hdr = (PacketHeader*)rbuffer; + r_pkt_data = &rbuffer[sizeof(PacketHeader)]; + r_cmd_hdr = (CommandHeader*)r_pkt_data; + r_cmd_data = &rbuffer[sizeof(PacketHeader) + sizeof(CommandHeader)]; + + s_pkt_hdr = (PacketHeader*)sbuffer; + s_pkt_data = &sbuffer[sizeof(PacketHeader)]; + s_cmd_hdr = (CommandHeader*)s_pkt_data; + s_cmd_data = &sbuffer[sizeof(PacketHeader) + sizeof(CommandHeader)]; + + // Initialize DSP + decomp.init(&decompIn); + link.init(&decomp.out, output); + decomp.start(); + link.start(); + + // Start readers + client->readAsync(sizeof(PacketHeader), rbuffer, tcpHandler, this); + + // Default configuration + stop(); + + // Ask for a UI + getUI(); + } + + ClientClass::~ClientClass() { + close(); + delete[] rbuffer; + delete[] sbuffer; + } + + void ClientClass::showMenu() { + std::string diffId = ""; + SmGui::DrawListElem diffValue; + bool syncRequired = false; + { + std::lock_guard lck(dlMtx); + dl.draw(diffId, diffValue, syncRequired); + } + + if (!diffId.empty()) { + // Save ID + SmGui::DrawListElem elemId; + elemId.type = SmGui::DRAW_LIST_ELEM_TYPE_STRING; + elemId.str = diffId; + + // Encore packet + int size = 0; + s_cmd_data[size++] = syncRequired; + size += SmGui::DrawList::storeItem(elemId, &s_cmd_data[size], SERVER_MAX_PACKET_SIZE - size); + size += SmGui::DrawList::storeItem(diffValue, &s_cmd_data[size], SERVER_MAX_PACKET_SIZE - size); + + // Send + if (syncRequired) { + spdlog::warn("Action requires resync"); + auto waiter = awaitCommandAck(COMMAND_UI_ACTION); + sendCommand(COMMAND_UI_ACTION, size); + if (waiter->await(PROTOCOL_TIMEOUT_MS)) { + std::lock_guard lck(dlMtx); + dl.load(r_cmd_data, r_pkt_hdr->size - sizeof(PacketHeader) - sizeof(CommandHeader)); + } + else { + spdlog::error("Timeout out after asking for UI"); + } + waiter->handled(); + spdlog::warn("Resync done"); + } + else { + spdlog::warn("Action does not require resync"); + sendCommand(COMMAND_UI_ACTION, size); + } + } + } + + void ClientClass::setFrequency(double freq) { + if (!client || !client->isOpen()) { return; } + *(double*)s_cmd_data = freq; + sendCommand(COMMAND_SET_FREQUENCY, sizeof(double)); + auto waiter = awaitCommandAck(COMMAND_SET_FREQUENCY); + waiter->await(PROTOCOL_TIMEOUT_MS); + waiter->handled(); + } + + double ClientClass::getSampleRate() { + return currentSampleRate; + } + + void ClientClass::setSampleType(dsp::PCMType type) { + s_cmd_data[0] = type; + sendCommand(COMMAND_SET_SAMPLE_TYPE, 1); + } + + void ClientClass::start() { + if (!client || !client->isOpen()) { return; } + sendCommand(COMMAND_START, 0); + getUI(); + } + + void ClientClass::stop() { + if (!client || !client->isOpen()) { return; } + sendCommand(COMMAND_STOP, 0); + getUI(); + } + + void ClientClass::close() { + decomp.stop(); + link.stop(); + client->close(); + } + + bool ClientClass::isOpen() { + return client->isOpen(); + } + + void ClientClass::tcpHandler(int count, uint8_t* buf, void* ctx) { + ClientClass* _this = (ClientClass*)ctx; + + // Read the rest of the data (TODO: CHECK SIZE OR SHIT WILL BE FUCKED) + int len = 0; + int read = 0; + int goal = _this->r_pkt_hdr->size - sizeof(PacketHeader); + while (len < goal) { + read = _this->client->read(goal - len, &buf[sizeof(PacketHeader) + len]); + if (read < 0) { + return; + }; + len += read; + } + _this->bytes += _this->r_pkt_hdr->size; + + if (_this->r_pkt_hdr->type == PACKET_TYPE_COMMAND) { + // TODO: Move to command handler + if (_this->r_cmd_hdr->cmd == COMMAND_SET_SAMPLERATE && _this->r_pkt_hdr->size == sizeof(PacketHeader) + sizeof(CommandHeader) + sizeof(double)) { + _this->currentSampleRate = *(double*)_this->r_cmd_data; + core::setInputSampleRate(_this->currentSampleRate); + } + } + else if (_this->r_pkt_hdr->type == PACKET_TYPE_COMMAND_ACK) { + // Notify waiters + std::vector toBeRemoved; + for (auto& [waiter, cmd] : _this->commandAckWaiters) { + if (cmd != _this->r_cmd_hdr->cmd) { continue; } + waiter->notify(); + toBeRemoved.push_back(waiter); + } + + // Remove handled waiters + for (auto& waiter : toBeRemoved) { + _this->commandAckWaiters.erase(waiter); + delete waiter; + } + } + else if (_this->r_pkt_hdr->type == PACKET_TYPE_BASEBAND) { + memcpy(_this->decompIn.writeBuf, &buf[sizeof(PacketHeader)], _this->r_pkt_hdr->size - sizeof(PacketHeader)); + _this->decompIn.swap(_this->r_pkt_hdr->size - sizeof(PacketHeader)); + } + else if (_this->r_pkt_hdr->type == PACKET_TYPE_ERROR) { + spdlog::error("SDR++ Server Error: {0}", buf[sizeof(PacketHeader)]); + } + else { + spdlog::error("Invalid packet type: {0}", _this->r_pkt_hdr->type); + } + + // Restart an async read + _this->client->readAsync(sizeof(PacketHeader), _this->rbuffer, tcpHandler, _this); + } + + void ClientClass::getUI() { + auto waiter = awaitCommandAck(COMMAND_GET_UI); + sendCommand(COMMAND_GET_UI, 0); + if (waiter->await(PROTOCOL_TIMEOUT_MS)) { + std::lock_guard lck(dlMtx); + dl.load(r_cmd_data, r_pkt_hdr->size - sizeof(PacketHeader) - sizeof(CommandHeader)); + } + else { + spdlog::error("Timeout out after asking for UI"); + } + waiter->handled(); + } + + void ClientClass::sendPacket(PacketType type, int len) { + s_pkt_hdr->type = type; + s_pkt_hdr->size = sizeof(PacketHeader) + len; + client->write(s_pkt_hdr->size, sbuffer); + } + + void ClientClass::sendCommand(Command cmd, int len) { + s_cmd_hdr->cmd = cmd; + sendPacket(PACKET_TYPE_COMMAND, sizeof(CommandHeader) + len); + } + + void ClientClass::sendCommandAck(Command cmd, int len) { + s_cmd_hdr->cmd = cmd; + sendPacket(PACKET_TYPE_COMMAND_ACK, sizeof(CommandHeader) + len); + } + + PacketWaiter* ClientClass::awaitCommandAck(Command cmd) { + PacketWaiter* waiter = new PacketWaiter; + commandAckWaiters[waiter] = cmd; + return waiter; + } + + void ClientClass::dHandler(dsp::complex_t *data, int count, void *ctx) { + ClientClass* _this = (ClientClass*)ctx; + memcpy(_this->output->writeBuf, data, count * sizeof(dsp::complex_t)); + _this->output->swap(count); + } + + Client connect(std::string host, uint16_t port, dsp::stream* out) { + net::Conn conn = net::connect(host, port); + if (!conn) { return NULL; } + return Client(new ClientClass(std::move(conn), out)); + } +} diff --git a/source_modules/sdrpp_server_source/src/sdrpp_server_client.h b/source_modules/sdrpp_server_source/src/sdrpp_server_client.h new file mode 100644 index 00000000..abf34626 --- /dev/null +++ b/source_modules/sdrpp_server_source/src/sdrpp_server_client.h @@ -0,0 +1,134 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RFSPACE_MAX_SIZE 8192 +#define RFSPACE_HEARTBEAT_INTERVAL_MS 1000 +#define RFSPACE_TIMEOUT_MS 3000 + +#define PROTOCOL_TIMEOUT_MS 10000 + +namespace server { + class PacketWaiter { + public: + bool await(int timeout) { + std::unique_lock lck(readyMtx); + return readyCnd.wait_for(lck, std::chrono::milliseconds(timeout), [=](){ return dataReady; }); + } + + void handled() { + { + std::lock_guard lck(handledMtx); + dataHandled = true; + } + handledCnd.notify_all(); + } + + void notify() { + // Tell waiter that data is ready + { + std::lock_guard lck(readyMtx); + dataReady = true; + } + readyCnd.notify_all(); + + // Wait for waiter to handle the request + { + std::unique_lock lck(readyMtx); + handledCnd.wait(lck, [=](){ return dataHandled; }); + } + } + + void reset() { + std::lock_guard lck1(readyMtx); + std::lock_guard lck2(handledMtx); + dataReady = false; + dataHandled = false; + } + + private: + bool dataReady = false; + bool dataHandled = false; + + std::condition_variable readyCnd; + std::condition_variable handledCnd; + + std::mutex readyMtx; + std::mutex handledMtx; + }; + + class ClientClass { + public: + ClientClass(net::Conn conn, dsp::stream* out); + ~ClientClass(); + + void showMenu(); + + void setFrequency(double freq); + double getSampleRate(); + + void setSampleType(dsp::PCMType type); + + void start(); + void stop(); + + void close(); + bool isOpen(); + + int bytes = 0; + + private: + static void tcpHandler(int count, uint8_t* buf, void* ctx); + + void getUI(); + + void sendPacket(PacketType type, int len); + void sendCommand(Command cmd, int len); + void sendCommandAck(Command cmd, int len); + + PacketWaiter* awaitCommandAck(Command cmd); + void commandAckHandled(PacketWaiter* waiter); + std::map commandAckWaiters; + + static void dHandler(dsp::complex_t *data, int count, void *ctx); + + net::Conn client; + + dsp::stream decompIn; + dsp::DynamicRangeDecompressor decomp; + dsp::Link link; + dsp::stream* output; + + uint8_t* rbuffer = NULL; + uint8_t* sbuffer = NULL; + + PacketHeader* r_pkt_hdr = NULL; + uint8_t* r_pkt_data = NULL; + CommandHeader* r_cmd_hdr = NULL; + uint8_t* r_cmd_data = NULL; + + PacketHeader* s_pkt_hdr = NULL; + uint8_t* s_pkt_data = NULL; + CommandHeader* s_cmd_hdr = NULL; + uint8_t* s_cmd_data = NULL; + + SmGui::DrawList dl; + std::mutex dlMtx; + + double currentSampleRate = 1000000.0; + }; + + typedef std::unique_ptr Client; + + Client connect(std::string host, uint16_t port, dsp::stream* out); +} diff --git a/source_modules/soapy_source/src/main.cpp b/source_modules/soapy_source/src/main.cpp index cb92fe05..64941337 100644 --- a/source_modules/soapy_source/src/main.cpp +++ b/source_modules/soapy_source/src/main.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -365,65 +366,66 @@ private: static void menuHandler(void* ctx) { SoapyModule* _this = (SoapyModule*)ctx; - float menuWidth = ImGui::GetContentRegionAvailWidth(); - // If no device is selected, draw only the refresh button if (_this->devId < 0) { - if (ImGui::Button(CONCAT("Refresh##_dev_select_", _this->name), ImVec2(menuWidth, 0))) { + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Button(CONCAT("Refresh##_dev_select_", _this->name))) { _this->refresh(); _this->selectDevice(config.conf["device"]); } return; } - if (_this->running) { style::beginDisabled(); } + if (_this->running) { SmGui::BeginDisabled(); } - ImGui::SetNextItemWidth(menuWidth); - if (ImGui::Combo(CONCAT("##_dev_select_", _this->name), &_this->devId, _this->txtDevList.c_str())) { + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Combo(CONCAT("##_dev_select_", _this->name), &_this->devId, _this->txtDevList.c_str())) { _this->selectDevice(_this->devList[_this->devId]["label"]); config.acquire(); config.conf["device"] = _this->devList[_this->devId]["label"]; config.release(true); } - if (ImGui::Combo(CONCAT("##_sr_select_", _this->name), &_this->srId, _this->txtSrList.c_str())) { + if (SmGui::Combo(CONCAT("##_sr_select_", _this->name), &_this->srId, _this->txtSrList.c_str())) { _this->selectSampleRate(_this->sampleRates[_this->srId]); if (_this->bandwidthList.size() > 2 && _this->running && _this->bandwidthList[_this->uiBandwidthId] == -1) _this->dev->setBandwidth(SOAPY_SDR_RX, _this->channelId, _this->selectBwBySr(_this->sampleRates[_this->srId])); _this->saveCurrent(); } - ImGui::SameLine(); - float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX(); - if (ImGui::Button(CONCAT("Refresh##_dev_select_", _this->name), ImVec2(refreshBtnWdith, 0))) { + SmGui::SameLine(); + SmGui::FillWidth(); + if (SmGui::Button(CONCAT("Refresh##_dev_select_", _this->name))) { _this->refresh(); _this->selectDevice(config.conf["device"]); } - if (_this->running) { style::endDisabled(); } + if (_this->running) { SmGui::EndDisabled(); } if (_this->antennaList.size() > 1) { - ImGui::LeftLabel("Antenna"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo(CONCAT("##_antenna_select_", _this->name), &_this->uiAntennaId, _this->txtAntennaList.c_str())) { + SmGui::LeftLabel("Antenna"); + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##_antenna_select_", _this->name), &_this->uiAntennaId, _this->txtAntennaList.c_str())) { if (_this->running) _this->dev->setAntenna(SOAPY_SDR_RX, _this->channelId, _this->antennaList[_this->uiAntennaId]); _this->saveCurrent(); } } - float gainNameLen = 0; - float len; - for (auto gain : _this->gainList) { - len = ImGui::CalcTextSize((gain + " gain").c_str()).x; - if (len > gainNameLen) { - gainNameLen = len; - } - } - gainNameLen += 5.0f; + // float gainNameLen = 0; + // float len; + // for (auto gain : _this->gainList) { + // len = ImGui::CalcTextSize((gain + " gain").c_str()).x; + // if (len > gainNameLen) { + // gainNameLen = len; + // } + // } + // gainNameLen += 5.0f; if (_this->hasAgc) { - if (ImGui::Checkbox((std::string("AGC##_agc_sel_") + _this->name).c_str(), &_this->agc)) { + if (SmGui::Checkbox((std::string("AGC##_agc_sel_") + _this->name).c_str(), &_this->agc)) { if (_this->running) { _this->dev->setGainMode(SOAPY_SDR_RX, _this->channelId, _this->agc); } // When disabled, reset the gains if (!_this->agc) { @@ -441,16 +443,17 @@ private: char buf[128]; for (auto gain : _this->gainList) { sprintf(buf, "%s gain", gain.c_str()); - ImGui::LeftLabel(buf); - ImGui::SetCursorPosX(gainNameLen); - ImGui::SetNextItemWidth(menuWidth - gainNameLen); + SmGui::LeftLabel(buf); + // ImGui::SetCursorPosX(gainNameLen); + // ImGui::SetNextItemWidth(menuWidth - gainNameLen); float step = _this->gainRanges[i].step(); bool res; + SmGui::FillWidth(); if (step == 0.0f) { - res = ImGui::SliderFloat((std::string("##_gain_sel_") + _this->name + gain).c_str(), &_this->uiGains[i], _this->gainRanges[i].minimum(), _this->gainRanges[i].maximum()); + res = SmGui::SliderFloat((std::string("##_gain_sel_") + _this->name + gain).c_str(), &_this->uiGains[i], _this->gainRanges[i].minimum(), _this->gainRanges[i].maximum()); } else { - res = ImGui::SliderFloatWithSteps((std::string("##_gain_sel_") + _this->name + gain).c_str(), &_this->uiGains[i], _this->gainRanges[i].minimum(), _this->gainRanges[i].maximum(), step); + res = SmGui::SliderFloatWithSteps((std::string("##_gain_sel_") + _this->name + gain).c_str(), &_this->uiGains[i], _this->gainRanges[i].minimum(), _this->gainRanges[i].maximum(), step); } if (res) { if (_this->running) { @@ -461,9 +464,9 @@ private: i++; } if (_this->bandwidthList.size() > 2) { - ImGui::LeftLabel("Bandwidth"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo(CONCAT("##_bw_select_", _this->name), &_this->uiBandwidthId, _this->txtBwList.c_str())) { + SmGui::LeftLabel("Bandwidth"); + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##_bw_select_", _this->name), &_this->uiBandwidthId, _this->txtBwList.c_str())) { if (_this->running) { if (_this->bandwidthList[_this->uiBandwidthId] == -1) _this->dev->setBandwidth(SOAPY_SDR_RX, _this->channelId, _this->selectBwBySr(_this->sampleRates[_this->srId])); diff --git a/source_modules/spyserver_source/src/main.cpp b/source_modules/spyserver_source/src/main.cpp index 48ababab..dcfe558f 100644 --- a/source_modules/spyserver_source/src/main.cpp +++ b/source_modules/spyserver_source/src/main.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -155,28 +156,29 @@ private: static void menuHandler(void* ctx) { SpyServerSourceModule* _this = (SpyServerSourceModule*)ctx; - float menuWidth = ImGui::GetContentRegionAvailWidth(); bool connected = (_this->client && _this->client->isOpen()); gui::mainWindow.playButtonLocked = !connected; - if (connected) { style::beginDisabled(); } - if (ImGui::InputText(CONCAT("##_spyserver_srv_host_", _this->name), _this->hostname, 1023)) { + if (connected) { SmGui::BeginDisabled(); } + if (SmGui::InputText(CONCAT("##_spyserver_srv_host_", _this->name), _this->hostname, 1023)) { config.acquire(); config.conf["hostname"] = _this->hostname; config.release(true); } - ImGui::SameLine(); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::InputInt(CONCAT("##_spyserver_srv_port_", _this->name), &_this->port, 0, 0)) { + SmGui::SameLine(); + SmGui::FillWidth(); + if (SmGui::InputInt(CONCAT("##_spyserver_srv_port_", _this->name), &_this->port, 0, 0)) { config.acquire(); config.conf["port"] = _this->port; config.release(true); } - if (connected) { style::endDisabled(); } + if (connected) { SmGui::EndDisabled(); } - if (_this->running) { style::beginDisabled(); } - if (!connected && ImGui::Button("Connect##spyserver_source", ImVec2(menuWidth, 0))) { + if (_this->running) { SmGui::BeginDisabled(); } + SmGui::FillWidth(); + SmGui::ForceSync(); + if (!connected && SmGui::Button("Connect##spyserver_source")) { try { if (_this->client) { _this->client.reset(); } _this->client = spyserver::connect(_this->hostname, _this->port, &_this->stream); @@ -223,17 +225,17 @@ private: spdlog::error("Could not connect to spyserver {0}", e.what()); } } - else if (connected && ImGui::Button("Disconnect##spyserver_source", ImVec2(menuWidth, 0))) { + else if (connected && SmGui::Button("Disconnect##spyserver_source")) { _this->client->close(); } - if (_this->running) { style::endDisabled(); } + if (_this->running) { SmGui::EndDisabled(); } if (connected) { if (_this->running) { style::beginDisabled(); } - ImGui::LeftLabel("Samplerate"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo("##spyserver_source_sr", &_this->srId, _this->sampleRatesTxt.c_str())) { + SmGui::LeftLabel("Samplerate"); + SmGui::FillWidth(); + if (SmGui::Combo("##spyserver_source_sr", &_this->srId, _this->sampleRatesTxt.c_str())) { _this->sampleRate = _this->sampleRates[_this->srId]; core::setInputSampleRate(_this->sampleRate); config.acquire(); @@ -242,9 +244,9 @@ private: } if (_this->running) { style::endDisabled(); } - ImGui::LeftLabel("Sample bit depth"); - ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::Combo("##spyserver_source_type", &_this->iqType, streamFormatStr)) { + SmGui::LeftLabel("Sample bit depth"); + SmGui::FillWidth(); + if (SmGui::Combo("##spyserver_source_type", &_this->iqType, streamFormatStr)) { int srvBits = streamFormatsBitCount[_this->iqType]; _this->client->setSetting(SPYSERVER_SETTING_IQ_FORMAT, streamFormats[_this->iqType]); _this->client->setSetting(SPYSERVER_SETTING_IQ_DIGITAL_GAIN, _this->client->computeDigitalGain(srvBits, _this->gain, _this->srId + _this->client->devInfo.MinimumIQDecimation)); @@ -255,8 +257,8 @@ private: } if (_this->client->devInfo.MaximumGainIndex) { - ImGui::SetNextItemWidth(menuWidth); - if (ImGui::SliderInt("##spyserver_source_gain", (int*)&_this->gain, 0, _this->client->devInfo.MaximumGainIndex)) { + SmGui::FillWidth(); + if (SmGui::SliderInt("##spyserver_source_gain", (int*)&_this->gain, 0, _this->client->devInfo.MaximumGainIndex)) { int srvBits = streamFormatsBitCount[_this->iqType]; _this->client->setSetting(SPYSERVER_SETTING_GAIN, _this->gain); _this->client->setSetting(SPYSERVER_SETTING_IQ_DIGITAL_GAIN, _this->client->computeDigitalGain(srvBits, _this->gain, _this->srId + _this->client->devInfo.MinimumIQDecimation)); @@ -266,14 +268,14 @@ private: } } - ImGui::Text("Status:"); - ImGui::SameLine(); + SmGui::Text("Status:"); + SmGui::SameLine(); ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "Connected (%s)", deviceTypesStr[_this->client->devInfo.DeviceType]); } else { - ImGui::Text("Status:"); - ImGui::SameLine(); - ImGui::Text("Not connected"); + SmGui::Text("Status:"); + SmGui::SameLine(); + SmGui::Text("Not connected"); } }