SDR++ server beta :)

This commit is contained in:
AlexandreRouma
2022-01-21 20:22:13 +01:00
parent 1185e4e114
commit 74b9d13360
37 changed files with 2731 additions and 498 deletions

View File

@ -1,3 +1,4 @@
#include <server.h>
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
@ -18,7 +19,6 @@
#include <options.h>
#include <filesystem>
#include <gui/menus/theme.h>
#include <server.h>
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include <stb_image_resize.h>
@ -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"];

View File

@ -2,16 +2,16 @@
#include <dsp/block.h>
namespace dsp {
enum PCMType {
PCM_TYPE_I8,
PCM_TYPE_I16,
PCM_TYPE_F32
};
class DynamicRangeCompressor : public generic_block<DynamicRangeCompressor> {
public:
DynamicRangeCompressor() {}
enum PCMType {
PCM_TYPE_I8,
PCM_TYPE_I16,
PCM_TYPE_F32
};
DynamicRangeCompressor(stream<complex_t>* in, PCMType pcmType) { init(in, pcmType); }
void init(stream<complex_t>* 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;

54
core/src/dsp/link.h Normal file
View File

@ -0,0 +1,54 @@
#pragma once
#include <dsp/block.h>
namespace dsp {
template <class T>
class Link : public generic_block<Link<T>> {
public:
Link() {}
Link(stream<T>* a, stream<T>* b) { init(a, b); }
void init(stream<T>* in, stream<T>* out) {
_in = in;
_out = out;
generic_block<Link<T>>::registerInput(_in);
generic_block<Link<T>>::registerOutput(_out);
generic_block<Link<T>>::_block_init = true;
}
void setInput(stream<T>* in) {
assert(generic_block<Link<T>>::_block_init);
std::lock_guard<std::mutex> lck(generic_block<Link<T>>::ctrlMtx);
generic_block<Link<T>>::tempStop();
generic_block<Link<T>>::unregisterInput(_in);
_in = in;
generic_block<Link<T>>::registerInput(_in);
generic_block<Link<T>>::tempStart();
}
void setOutput(stream<T>* out) {
assert(generic_block<Link<T>>::_block_init);
std::lock_guard<std::mutex> lck(generic_block<Link<T>>::ctrlMtx);
generic_block<Link<T>>::tempStop();
generic_block<Link<T>>::unregisterOutput(_out);
_out = out;
generic_block<Link<T>>::registerOutput(_out);
generic_block<Link<T>>::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<T>* _in;
stream<T>* _out;
};
}

View File

@ -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());

745
core/src/gui/smgui.cpp Normal file
View File

@ -0,0 +1,745 @@
#include "smgui.h"
#include "style.h"
#include <options.h>
#include <gui/widgets/stepped_slider.h>
#include <gui/gui.h>
namespace SmGui {
std::map<FormatString, const char*> 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;
}
}

146
core/src/gui/smgui.h Normal file
View File

@ -0,0 +1,146 @@
#pragma once
#include <imgui.h>
#include <string>
#include <vector>
#include <map>
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<FormatString, const char*> 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<DrawListElem> 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();
}

View File

@ -53,4 +53,8 @@ namespace ImGui {
ImGui::SameLine();
ImGui::SetCursorPosY(vpos);
}
void FillWidth() {
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth());
}
}

View File

@ -16,4 +16,5 @@ namespace style {
namespace ImGui {
void LeftLabel(const char* text);
void FillWidth();
}

View File

@ -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;

View File

@ -7,6 +7,8 @@ namespace options {
std::string root;
bool showConsole;
bool serverMode;
std::string serverHost;
int serverPort;
};
SDRPP_EXPORT CMDLineOptions opts;

View File

@ -1,7 +1,354 @@
#include <server.h>
#include "server.h"
#include "core.h"
#include <spdlog/spdlog.h>
#include <version.h>
#include <config.h>
#include <options.h>
#include <filesystem>
#include <dsp/types.h>
#include <signal_path/signal_path.h>
#include <gui/smgui.h>
#include <utils/optionlist.h>
#include <dsp/compression.h>
int server_main() {
spdlog::error("Server mode is not implemented yet.");
return 0;
}
namespace server {
dsp::stream<dsp::complex_t> dummyInput;
dsp::DynamicRangeCompressor comp;
dsp::HandlerSink<uint8_t> 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<std::string, std::string> 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<std::string> 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<dsp::complex_t>* 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);
}
}

View File

@ -1,3 +1,27 @@
#pragma once
#include <utils/networking.h>
#include <dsp/stream.h>
#include <dsp/types.h>
#include <server_protocol.h>
int server_main();
namespace server {
void setInput(dsp::stream<dsp::complex_t>* 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);
}

View File

@ -0,0 +1,49 @@
#pragma once
#include <stdint.h>
#include <gui/smgui.h>
#include <dsp/types.h>
#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;
};
}

View File

@ -1,6 +1,8 @@
#include <server.h>
#include <signal_path/source.h>
#include <spdlog/spdlog.h>
#include <signal_path/signal_path.h>
#include <options.h>
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() {

View File

@ -1,5 +1,6 @@
#include <utils/networking.h>
#include <assert.h>
#include <spdlog/spdlog.h>
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) {

View File

@ -0,0 +1,2 @@
#include "new_networking.h"

View File

@ -0,0 +1,95 @@
#pragma once
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
/*
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<ReadHandler> readQueue;
std::thread readThread;
std::mutex readMtx;
std::condition_variable readCnd;
};
typedef std::shared_ptr<SocketClass> 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<AcceptHandler> acceptQueue;
std::thread acceptThread;
std::mutex acceptMtx;
std::condition_variable acceptCnd;
};
typedef std::shared_ptr<ListenerClass> 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);
}
}