Merge pull request #26 from AlexandreRouma/better_modules

Better modules
This commit is contained in:
AlexandreRouma 2020-10-22 11:03:32 +02:00 committed by GitHub
commit fa1e647235
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
223 changed files with 109621 additions and 2609 deletions

3
.gitignore vendored
View File

@ -2,4 +2,5 @@ build/
.vscode/
*.old
*.dll
*.exe
*.exe
*.zip

View File

@ -1,65 +1,35 @@
cmake_minimum_required(VERSION 3.9)
project(sdrpp)
cmake_minimum_required(VERSION 3.13)
project(sdrpp_core)
add_subdirectory("modules/radio")
add_subdirectory("modules/recorder")
# Cross platform modules
add_subdirectory("core")
add_subdirectory("radio")
add_subdirectory("recorder")
add_subdirectory("soapy")
add_subdirectory("file_source")
add_subdirectory("rtl_tcp_source")
add_subdirectory("demo")
if (MSVC)
set(CMAKE_CXX_FLAGS "-O2 /std:c++17")
link_directories(sdrpp "C:/Program Files/PothosSDR/lib/")
include_directories(sdrpp "C:/Program Files/PothosSDR/include/volk/")
include_directories(sdrpp "C:/Program Files/PothosSDR/include/")
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
else()
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive -fsanitize=address -g")
# set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive")
include_directories(sdrpp "/usr/include/volk")
link_libraries(pthread)
link_libraries(GL)
link_libraries(GLEW)
link_libraries(glfw)
link_libraries(fftw3)
link_libraries(fftw3f)
link_libraries(portaudio)
link_libraries(X11)
link_libraries(Xxf86vm)
link_libraries(dl)
set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
endif (MSVC)
link_libraries(volk)
link_libraries(SoapySDR)
add_executable(sdrpp "src/main.cpp" "win32/resources.rc")
target_link_libraries(sdrpp PRIVATE sdrpp_core)
# Main code
include_directories(sdrpp "src/")
include_directories(sdrpp "src/imgui")
file(GLOB SRC "src/*.cpp")
file(GLOB IMGUI "src/imgui/*.cpp")
# If on windows, set the executable icon
# Copy dynamic libs over
if (MSVC)
set(SRC ${SRC} "win32/resources.rc")
add_custom_target(do_always ALL xcopy /s \"$<TARGET_FILE_DIR:sdrpp_core>\\*.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y)
endif (MSVC)
add_executable(sdrpp ${SRC} ${IMGUI})
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
add_custom_target(do_always ALL cp \"$<TARGET_FILE_DIR:sdrpp_core>/sdrpp_core.so\" \"$<TARGET_FILE_DIR:sdrpp>\")
endif ()
if (MSVC)
# Glew
find_package(GLEW REQUIRED)
target_link_libraries(sdrpp PRIVATE GLEW::GLEW)
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
add_custom_target(do_always ALL cp \"$<TARGET_FILE_DIR:sdrpp_core>/sdrpp_core.dylib\" \"$<TARGET_FILE_DIR:sdrpp>\")
endif ()
# GLFW3
find_package(glfw3 CONFIG REQUIRED)
target_link_libraries(sdrpp PRIVATE glfw)
# FFTW3
find_package(FFTW3 CONFIG REQUIRED)
target_link_libraries(sdrpp PRIVATE FFTW3::fftw3)
find_package(FFTW3f CONFIG REQUIRED)
target_link_libraries(sdrpp PRIVATE FFTW3::fftw3f)
# PortAudio
find_package(portaudio CONFIG REQUIRED)
target_link_libraries(sdrpp PRIVATE portaudio portaudio_static)
endif (MSVC)
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/Users/Alex/vcpkg/scripts/buildsystems/vcpkg.cmake" -G "Visual Studio 15 2017 Win64"
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/Users/Alex/vcpkg/scripts/buildsystems/vcpkg.cmake" -G "Visual Studio 15 2017 Win64"

116
core/CMakeLists.txt Normal file
View File

@ -0,0 +1,116 @@
cmake_minimum_required(VERSION 3.13)
project(sdrpp_core)
# Set compiler options
if (MSVC)
set(CMAKE_CXX_FLAGS "-O2 /std:c++17")
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
else()
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive")
endif (MSVC)
add_definitions(-DSDRPP_IS_CORE)
# Main code
file(GLOB SRC "src/*.cpp")
file(GLOB GUI "src/gui/*.cpp")
file(GLOB MENUS "src/gui/menus/*.cpp")
file(GLOB DIALOGS "src/gui/dialogs/*.cpp")
file(GLOB SIGPATH "src/signal_path/*.cpp")
file(GLOB IMGUI "src/imgui/*.cpp")
file(GLOB DUKTAPE "src/duktape/*.c")
# Add code to dyn lib
add_library(sdrpp_core SHARED ${SRC} ${GUI} ${MENUS} ${DIALOGS} ${SIGPATH} ${IMGUI} ${DUKTAPE})
set_target_properties(sdrpp_core PROPERTIES PREFIX "")
# Include core headers
target_include_directories(sdrpp_core PUBLIC "src/")
target_include_directories(sdrpp_core PUBLIC "src/imgui")
if (MSVC)
# Lib path
target_link_directories(sdrpp_core PUBLIC "C:/Program Files/PothosSDR/lib/")
# Misc headers
target_include_directories(sdrpp_core PUBLIC "C:/Program Files/PothosSDR/include/")
# Volk
target_include_directories(sdrpp_core PUBLIC "C:/Program Files/PothosSDR/include/volk/")
target_link_libraries(sdrpp_core PUBLIC volk)
# SoapySDR
target_link_libraries(sdrpp_core PUBLIC SoapySDR)
# Glew
find_package(GLEW REQUIRED)
target_link_libraries(sdrpp_core PUBLIC GLEW::GLEW)
# GLFW3
find_package(glfw3 CONFIG REQUIRED)
target_link_libraries(sdrpp_core PUBLIC glfw)
# FFTW3
find_package(FFTW3 CONFIG REQUIRED)
target_link_libraries(sdrpp_core PUBLIC FFTW3::fftw3)
find_package(FFTW3f CONFIG REQUIRED)
target_link_libraries(sdrpp_core PUBLIC FFTW3::fftw3f)
# PortAudio
find_package(portaudio CONFIG REQUIRED)
target_link_libraries(sdrpp_core PUBLIC portaudio portaudio_static)
endif (MSVC)
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
target_include_directories(sdrpp_core PUBLIC "/usr/include/volk")
target_link_libraries(sdrpp_core PUBLIC pthread)
target_link_libraries(sdrpp_core PUBLIC GL)
target_link_libraries(sdrpp_core PUBLIC GLEW)
target_link_libraries(sdrpp_core PUBLIC glfw)
target_link_libraries(sdrpp_core PUBLIC fftw3)
target_link_libraries(sdrpp_core PUBLIC fftw3f)
target_link_libraries(sdrpp_core PUBLIC portaudio)
target_link_libraries(sdrpp_core PUBLIC X11)
target_link_libraries(sdrpp_core PUBLIC Xxf86vm)
target_link_libraries(sdrpp_core PUBLIC dl)
endif ()
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
find_package(OpenGL REQUIRED)
# find_package(GLEW REQUIRED)
# find_package(fftw3 REQUIRED)
# find_package(glfw3 REQUIRED)
# find_package(volk REQUIRED)
target_include_directories(sdrpp_core PUBLIC /usr/local/opt/glew/include)
target_include_directories(sdrpp_core PUBLIC /usr/local/opt/glfw/include)
target_include_directories(sdrpp_core PUBLIC /usr/local/opt/fftw/include)
target_include_directories(sdrpp_core PUBLIC /usr/local/opt/portaudio/include)
target_include_directories(sdrpp_core PUBLIC /usr/local/opt/volk/include)
target_include_directories(sdrpp_core PUBLIC /usr/local/opt/volk/include/volk)
target_link_directories(sdrpp_core PUBLIC /usr/local/opt/glew/lib)
target_link_directories(sdrpp_core PUBLIC /usr/local/opt/volk/lib)
target_link_directories(sdrpp_core PUBLIC /usr/local/opt/glfw/lib)
target_link_directories(sdrpp_core PUBLIC /usr/local/opt/fftw/lib)
target_link_directories(sdrpp_core PUBLIC /usr/local/opt/portaudio/lib)
target_link_libraries(sdrpp_core PUBLIC ${OPENGL_LIBRARIES})
target_link_libraries(sdrpp_core PUBLIC volk)
target_link_libraries(sdrpp_core PUBLIC glew)
target_link_libraries(sdrpp_core PUBLIC glfw)
target_link_libraries(sdrpp_core PUBLIC fftw3)
target_link_libraries(sdrpp_core PUBLIC fftw3f)
target_link_libraries(sdrpp_core PUBLIC portaudio)
endif ()
target_link_libraries(sdrpp_core PUBLIC volk)
set(CORE_FILES ${RUNTIME_OUTPUT_DIRECTORY} PARENT_SCOPE)
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/Users/Alex/vcpkg/scripts/buildsystems/vcpkg.cmake" -G "Visual Studio 15 2017 Win64"

77
core/src/config.cpp Normal file
View File

@ -0,0 +1,77 @@
#include <config.h>
#include <spdlog/spdlog.h>
#include <fstream>
#include <filesystem>
ConfigManager::ConfigManager() {
}
void ConfigManager::setPath(std::string file) {
path = file;
}
void ConfigManager::load(json def, bool lock) {
if (lock) { mtx.lock(); }
if (path == "") {
spdlog::error("Config manager tried to load file with no path specified");
return;
}
if (!std::filesystem::exists(path)) {
spdlog::warn("Config file '{0}' does not exist, creating it", path);
conf = def;
save(false);
}
if (!std::filesystem::is_regular_file(path)) {
spdlog::error("Config file '{0}' isn't a file", path);
return;
}
std::ifstream file(path.c_str());
file >> conf;
file.close();
if (lock) { mtx.unlock(); }
}
void ConfigManager::save(bool lock) {
if (lock) { mtx.lock(); }
std::ofstream file(path.c_str());
file << conf.dump(4);
file.close();
if (lock) { mtx.unlock(); }
}
void ConfigManager::enableAutoSave() {
autoSaveEnabled = true;
autoSaveThread = std::thread(autoSaveWorker, this);
}
void ConfigManager::disableAutoSave() {
autoSaveEnabled = false;
autoSaveThread.join();
}
void ConfigManager::aquire() {
mtx.lock();
}
void ConfigManager::release(bool changed) {
this->changed |= changed;
mtx.unlock();
}
void ConfigManager::autoSaveWorker(ConfigManager* _this) {
while (_this->autoSaveEnabled) {
if (!_this->mtx.try_lock()) {
spdlog::warn("ConfigManager locked, waiting...");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
continue;
}
if (_this->changed) {
_this->changed = false;
_this->save(false);
}
_this->mtx.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}

43
core/src/config.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include <json.hpp>
#include <thread>
#include <string>
#include <mutex>
using nlohmann::json;
#define DEV_BUILD
#ifndef ROOT_DIR
#ifdef DEV_BUILD
#define ROOT_DIR "../root_dev"
#elif _WIN32
#define ROOT_DIR "."
#else
#define ROOT_DIR "/etc/sdrpp"
#endif
#endif
class ConfigManager {
public:
ConfigManager();
void setPath(std::string file);
void load(json def, bool lock = true);
void save(bool lock = true);
void enableAutoSave();
void disableAutoSave();
void aquire();
void release(bool changed = false);
json conf;
private:
static void autoSaveWorker(ConfigManager* _this);
std::string path = "";
bool changed = false;
bool autoSaveEnabled = false;
std::thread autoSaveThread;
std::mutex mtx;
};

303
core/src/core.cpp Normal file
View File

@ -0,0 +1,303 @@
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <gui/main_window.h>
#include <gui/style.h>
#include <gui/icons.h>
#include <version.h>
#include <spdlog/spdlog.h>
#include <gui/bandplan.h>
#include <module.h>
#include <stb_image.h>
#include <config.h>
#include <core.h>
#include <duktape/duktape.h>
#include <duktape/duk_console.h>
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include <stb_image_resize.h>
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#ifdef _WIN32
#include <Windows.h>
#endif
namespace core {
ConfigManager configManager;
ScriptManager scriptManager;
void setInputSampleRate(double samplerate) {
// NOTE: Zoom controls won't work
gui::waterfall.setBandwidth(samplerate);
sigpath::signalPath.setSampleRate(samplerate);
}
};
bool maximized = false;
bool fullScreen = false;
static void glfw_error_callback(int error, const char* description) {
spdlog::error("Glfw Error {0}: {1}", error, description);
}
static void maximized_callback(GLFWwindow* window, int n) {
if (n == GLFW_TRUE) {
maximized = true;
}
else {
maximized = false;
}
}
duk_ret_t test_func(duk_context *ctx) {
printf("Hello from C++\n");
return 1;
}
// main
int sdrpp_main() {
#ifdef _WIN32
//FreeConsole();
#endif
// TESTING
// duk_context* ctx = duk_create_heap_default();
// duk_console_init(ctx, DUK_CONSOLE_PROXY_WRAPPER);
// std::ifstream file("test.js", std::ios::in);
// std::stringstream ss;
// ss << file.rdbuf();
// std::string code = ss.str();
// duk_idx_t baseObj = duk_push_object(ctx);
// duk_push_int(ctx, 42);
// duk_put_prop_string(ctx, baseObj, "my_property");
// duk_push_c_function(ctx, test_func, 0);
// duk_put_prop_string(ctx, baseObj, "my_func");
// duk_put_global_string(ctx, "my_object");
// duk_push_object(ctx);
// if (duk_peval_string(ctx, code.c_str()) != 0) {
// printf("Error: %s\n", duk_safe_to_string(ctx, -1));
// return -1;
// }
// duk_pop(ctx);
core::scriptManager.createScript("TestScript 1", "test.js");
//core::scriptManager.createScript("TestScript 2", "test.js");
//core::scriptManager.createScript("TestScript 3", "test.js");
//core::scriptManager.createScript("TestScript 4", "test.js");
// TESTING
spdlog::info("SDR++ v" VERSION_STR);
// Load config
spdlog::info("Loading config");
core::configManager.setPath(ROOT_DIR "/config.json");
core::configManager.load(json());
core::configManager.enableAutoSave();
// Setup window
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit()) {
return 1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
core::configManager.aquire();
int winWidth = core::configManager.conf["windowSize"]["w"];
int winHeight = core::configManager.conf["windowSize"]["h"];
maximized = core::configManager.conf["maximized"];
core::configManager.release();
// Create window with graphics context
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
GLFWwindow* window = glfwCreateWindow(winWidth, winHeight, "SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")", NULL, NULL);
if (window == NULL)
return 1;
glfwMakeContextCurrent(window);
glfwSwapInterval(1); // Enable vsync
if (maximized) {
glfwMaximizeWindow(window);
}
glfwSetWindowMaximizeCallback(window, maximized_callback);
// Load app icon
GLFWimage icons[10];
icons[0].pixels = stbi_load(((std::string)(ROOT_DIR "/res/icons/sdrpp.png")).c_str(), &icons[0].width, &icons[0].height, 0, 4);
icons[1].pixels = (unsigned char*)malloc(16 * 16 * 4); icons[1].width = icons[1].height = 16;
icons[2].pixels = (unsigned char*)malloc(24 * 24 * 4); icons[2].width = icons[2].height = 24;
icons[3].pixels = (unsigned char*)malloc(32 * 32 * 4); icons[3].width = icons[3].height = 32;
icons[4].pixels = (unsigned char*)malloc(48 * 48 * 4); icons[4].width = icons[4].height = 48;
icons[5].pixels = (unsigned char*)malloc(64 * 64 * 4); icons[5].width = icons[5].height = 64;
icons[6].pixels = (unsigned char*)malloc(96 * 96 * 4); icons[6].width = icons[6].height = 96;
icons[7].pixels = (unsigned char*)malloc(128 * 128 * 4); icons[7].width = icons[7].height = 128;
icons[8].pixels = (unsigned char*)malloc(196 * 196 * 4); icons[8].width = icons[8].height = 196;
icons[9].pixels = (unsigned char*)malloc(256 * 256 * 4); icons[9].width = icons[9].height = 256;
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[1].pixels, 16, 16, 16 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[2].pixels, 24, 24, 24 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[3].pixels, 32, 32, 32 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[4].pixels, 48, 48, 48 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[5].pixels, 64, 64, 64 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[6].pixels, 96, 96, 96 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[7].pixels, 128, 128, 128 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[8].pixels, 196, 196, 196 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[9].pixels, 256, 256, 256 * 4, 4);
glfwSetWindowIcon(window, 10, icons);
stbi_image_free(icons[0].pixels);
for (int i = 1; i < 10; i++) {
free(icons[i].pixels);
}
if (glewInit() != GLEW_OK) {
spdlog::error("Failed to initialize OpenGL loader!");
return 1;
}
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.IniFilename = NULL;
// Setup Platform/Renderer bindings
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init("#version 150");
style::setDarkStyle();
// ====================================================
// // glfwPollEvents();
// ImGui_ImplOpenGL3_NewFrame();
// // ImGui_ImplGlfw_NewFrame();
// // ImGui::NewFrame();
// // ImGui::ShowDemoWindow();
// // ImGui::Render();
// int display_w, display_h;
// glfwGetFramebufferSize(window, &display_w, &display_h);
// glViewport(0, 0, display_w, display_h);
// glClearColor(0.0666f, 0.0666f, 0.0666f, 1.0f);
// //glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
// glClear(GL_COLOR_BUFFER_BIT);
// // ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
// glfwSwapBuffers(window);
// ====================================================
spdlog::info("Loading icons");
icons::load();
spdlog::info("Loading band plans");
bandplan::loadFromDir(ROOT_DIR "/bandplans");
spdlog::info("Loading band plans color table");
bandplan::loadColorTable(ROOT_DIR "/band_colors.json");
windowInit();
spdlog::info("Ready.");
bool _maximized = maximized;
int fsWidth, fsHeight, fsPosX, fsPosY;
// Main loop
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
//ImGui::ShowDemoWindow();
if (_maximized != maximized) {
_maximized = maximized;
core::configManager.aquire();
core::configManager.conf["maximized"]= _maximized;
if (!maximized) {
glfwSetWindowSize(window, core::configManager.conf["windowSize"]["w"], core::configManager.conf["windowSize"]["h"]);
}
core::configManager.release(true);
}
int _winWidth, _winHeight;
glfwGetWindowSize(window, &_winWidth, &_winHeight);
if (ImGui::IsKeyPressed(GLFW_KEY_F11)) {
fullScreen = !fullScreen;
if (fullScreen) {
spdlog::info("Fullscreen: ON");
fsWidth = _winWidth;
fsHeight = _winHeight;
glfwGetWindowPos(window, &fsPosX, &fsPosY);
const GLFWvidmode * mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, 0);
}
else {
spdlog::info("Fullscreen: OFF");
glfwSetWindowMonitor(window, nullptr, fsPosX, fsPosY, fsWidth, fsHeight, 0);
}
}
if ((_winWidth != winWidth || _winHeight != winHeight) && !maximized && _winWidth > 0 && _winHeight > 0) {
winWidth = _winWidth;
winHeight = _winHeight;
core::configManager.aquire();
core::configManager.conf["windowSize"]["w"] = winWidth;
core::configManager.conf["windowSize"]["h"] = winHeight;
core::configManager.release(true);
}
if (winWidth > 0 && winHeight > 0) {
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(ImVec2(_winWidth, _winHeight));
drawWindow();
}
// Rendering
ImGui::Render();
int display_w, display_h;
glfwGetFramebufferSize(window, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
glClearColor(0.0666f, 0.0666f, 0.0666f, 1.0f);
//glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(window);
}
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}

13
core/src/core.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include <module.h>
#include <config.h>
#include <scripting.h>
namespace core {
SDRPP_EXPORT ConfigManager configManager;
SDRPP_EXPORT ScriptManager scriptManager;
void setInputSampleRate(double samplerate);
};
int sdrpp_main();

View File

@ -1,6 +1,7 @@
#pragma once
#include <vector>
#include <dsp/stream.h>
#include <volk.h>
namespace dsp {
template <class D, class I, class O, int IC, int OC>
@ -118,4 +119,61 @@ namespace dsp {
}
};
class Squelch : public Block<Squelch, complex_t, complex_t, 1, 1> {
public:
Squelch() : Block({1}, {1}, this, worker) {}
void init(stream<complex_t>* input, int blockSize) {
in[0] = input;
inputBlockSize[0] = blockSize;
out[0]->setMaxLatency(blockSize * 2);
outputBlockSize[0] = blockSize;
level = -50.0f;
}
float level;
int onCount;
int offCount;
private:
static void worker(Squelch* _this) {
int blockSize = _this->inputBlockSize[0];
stream<complex_t>* in = _this->in[0];
stream<complex_t>* out = _this->out[0];
complex_t* buf = new complex_t[blockSize];
int _on = 0, _off = 0;
bool active = false;
while (true) {
if (in->read(buf, blockSize) < 0) { break; };
for (int i = 0; i < blockSize; i++) {
if (log10(sqrt((buf[i].i*buf[i].i) + (buf[i].q*buf[i].q))) * 10.0f > _this->level) {
_on++;
_off = 0;
}
else {
_on = 0;
_off++;
}
if (_on >= _this->onCount && !active) {
_on = _this->onCount;
active = true;
}
if (_off >= _this->offCount && active) {
_off = _this->offCount;
active = false;
}
if (!active) {
buf[i].i = 0.0f;
buf[i].q = 0.0f;
}
}
if (out->write(buf, blockSize) < 0) { break; };
}
delete[] buf;
}
};
};

View File

@ -52,30 +52,106 @@ namespace dsp {
output.setMaxLatency(blockSize * 2);
}
void setInput(stream<complex_t>* input) {
if (running) {
return;
}
_in = input;
}
stream<complex_t> output;
bool bypass;
private:
// static void _worker(DCBiasRemover* _this) {
// complex_t* buf = new complex_t[_this->_bufferSize];
// float ibias = 0.0f;
// float qbias = 0.0f;
// while (true) {
// if (_this->_in->read(buf, _this->_bufferSize) < 0) { break; };
// if (_this->bypass) {
// if (_this->output.write(buf, _this->_bufferSize) < 0) { break; };
// continue;
// }
// for (int i = 0; i < _this->_bufferSize; i++) {
// ibias += buf[i].i;
// qbias += buf[i].q;
// }
// ibias /= _this->_bufferSize;
// qbias /= _this->_bufferSize;
// for (int i = 0; i < _this->_bufferSize; i++) {
// buf[i].i -= ibias;
// buf[i].q -= qbias;
// }
// if (_this->output.write(buf, _this->_bufferSize) < 0) { break; };
// }
// delete[] buf;
// }
static void _worker(DCBiasRemover* _this) {
complex_t* buf = new complex_t[_this->_bufferSize];
float ibias = 0.0f;
float qbias = 0.0f;
complex_t* mixBuf = new complex_t[_this->_bufferSize];
float currentPhase = 0.0f;
float lastPhase = 0.0f;
double phase = 0.0f;
while (true) {
float ibias = 0.0f;
float qbias = 0.0f;
if (_this->_in->read(buf, _this->_bufferSize) < 0) { break; };
if (_this->bypass) {
if (_this->output.write(buf, _this->_bufferSize) < 0) { break; };
continue;
}
// Detect the frequency of the signal
double avgDiff = 0.0f;
// for (int i = 0; i < _this->_bufferSize; i++) {
// currentPhase = fast_arctan2(buf[i].i, buf[i].q);
// float diff = currentPhase - lastPhase;
// if (diff > 3.1415926535f) { diff -= 2 * 3.1415926535f; }
// else if (diff <= -3.1415926535f) { diff += 2 * 3.1415926535f; }
// avgDiff += diff;
// lastPhase = currentPhase;
// }
// avgDiff /= (double)_this->_bufferSize;
// avgDiff /= (double)_this->_bufferSize;
// Average the samples to "filter" the signal to the block frequency
for (int i = 0; i < _this->_bufferSize; i++) {
ibias += buf[i].i;
qbias += buf[i].q;
}
ibias /= _this->_bufferSize;
qbias /= _this->_bufferSize;
// Get the phase difference from the last block
currentPhase = fast_arctan2(ibias, qbias);
float diff = currentPhase - lastPhase;
if (diff > 3.1415926535f) { diff -= 2 * 3.1415926535f; }
else if (diff <= -3.1415926535f) { diff += 2 * 3.1415926535f; }
avgDiff += diff;
lastPhase = currentPhase;
avgDiff /= (double)_this->_bufferSize;
// Generate a correction signal using the phase difference
for (int i = 0; i < _this->_bufferSize; i++) {
buf[i].i -= ibias;
buf[i].q -= qbias;
mixBuf[i].i = sin(phase);
mixBuf[i].q = cos(phase);
phase -= avgDiff;
phase = fmodl(phase, 2.0 * 3.1415926535);
}
// Mix the correction signal with the original signal to shift the unwanted signal
// to the center. Also, null out the real component so that symetric
// frequencies are removed (at least I hope...)
float tq;
for (int i = 0; i < _this->_bufferSize; i++) {
buf[i].i = ((mixBuf[i].i * buf[i].q) + (mixBuf[i].q * buf[i].i)) * 1.4142;
buf[i].q = 0;
}
if (_this->output.write(buf, _this->_bufferSize) < 0) { break; };
}
delete[] buf;

View File

@ -48,7 +48,8 @@ namespace dsp {
}
void setFrequency(float frequency) {
_phasorSpeed = (2 * 3.1415926535) / (_sampleRate / frequency);
_frequency = frequency;
_phasorSpeed = (2 * 3.1415926535 * frequency) / _sampleRate;
}
void setBlockSize(int blockSize) {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,185 @@
/*
* Minimal 'console' binding.
*
* https://github.com/DeveloperToolsWG/console-object/blob/master/api.md
* https://developers.google.com/web/tools/chrome-devtools/debug/console/console-reference
* https://developer.mozilla.org/en/docs/Web/API/console
*/
#include <stdio.h>
#include <stdarg.h>
#include "duktape.h"
#include "duk_console.h"
/* XXX: Add some form of log level filtering. */
/* XXX: Should all output be written via e.g. console.write(formattedMsg)?
* This would make it easier for user code to redirect all console output
* to a custom backend.
*/
/* XXX: Init console object using duk_def_prop() when that call is available. */
static duk_ret_t duk__console_log_helper(duk_context *ctx, const char *error_name) {
duk_uint_t flags = (duk_uint_t) duk_get_current_magic(ctx);
FILE *output = (flags & DUK_CONSOLE_STDOUT_ONLY) ? stdout : stderr;
duk_idx_t n = duk_get_top(ctx);
duk_idx_t i;
duk_get_global_string(ctx, "console");
duk_get_prop_string(ctx, -1, "format");
for (i = 0; i < n; i++) {
if (duk_check_type_mask(ctx, i, DUK_TYPE_MASK_OBJECT)) {
/* Slow path formatting. */
duk_dup(ctx, -1); /* console.format */
duk_dup(ctx, i);
duk_call(ctx, 1);
duk_replace(ctx, i); /* arg[i] = console.format(arg[i]); */
}
}
duk_pop_2(ctx);
duk_push_string(ctx, " ");
duk_insert(ctx, 0);
duk_join(ctx, n);
if (error_name) {
duk_push_error_object(ctx, DUK_ERR_ERROR, "%s", duk_require_string(ctx, -1));
duk_push_string(ctx, "name");
duk_push_string(ctx, error_name);
duk_def_prop(ctx, -3, DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_VALUE); /* to get e.g. 'Trace: 1 2 3' */
duk_get_prop_string(ctx, -1, "stack");
}
fprintf(output, "%s\n", duk_to_string(ctx, -1));
if (flags & DUK_CONSOLE_FLUSH) {
fflush(output);
}
return 0;
}
static duk_ret_t duk__console_assert(duk_context *ctx) {
if (duk_to_boolean(ctx, 0)) {
return 0;
}
duk_remove(ctx, 0);
return duk__console_log_helper(ctx, "AssertionError");
}
static duk_ret_t duk__console_log(duk_context *ctx) {
return duk__console_log_helper(ctx, NULL);
}
static duk_ret_t duk__console_trace(duk_context *ctx) {
return duk__console_log_helper(ctx, "Trace");
}
static duk_ret_t duk__console_info(duk_context *ctx) {
return duk__console_log_helper(ctx, NULL);
}
static duk_ret_t duk__console_warn(duk_context *ctx) {
return duk__console_log_helper(ctx, NULL);
}
static duk_ret_t duk__console_error(duk_context *ctx) {
return duk__console_log_helper(ctx, "Error");
}
static duk_ret_t duk__console_dir(duk_context *ctx) {
/* For now, just share the formatting of .log() */
return duk__console_log_helper(ctx, 0);
}
static void duk__console_reg_vararg_func(duk_context *ctx, duk_c_function func, const char *name, duk_uint_t flags) {
duk_push_c_function(ctx, func, DUK_VARARGS);
duk_push_string(ctx, "name");
duk_push_string(ctx, name);
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE); /* Improve stacktraces by displaying function name */
duk_set_magic(ctx, -1, (duk_int_t) flags);
duk_put_prop_string(ctx, -2, name);
}
void duk_console_init(duk_context *ctx, duk_uint_t flags) {
duk_uint_t flags_orig;
/* If both DUK_CONSOLE_STDOUT_ONLY and DUK_CONSOLE_STDERR_ONLY where specified,
* just turn off DUK_CONSOLE_STDOUT_ONLY and keep DUK_CONSOLE_STDERR_ONLY.
*/
if ((flags & DUK_CONSOLE_STDOUT_ONLY) && (flags & DUK_CONSOLE_STDERR_ONLY)) {
flags &= ~DUK_CONSOLE_STDOUT_ONLY;
}
/* Remember the (possibly corrected) flags we received. */
flags_orig = flags;
duk_push_object(ctx);
/* Custom function to format objects; user can replace.
* For now, try JX-formatting and if that fails, fall back
* to ToString(v).
*/
duk_eval_string(ctx,
"(function (E) {"
"return function format(v){"
"try{"
"return E('jx',v);"
"}catch(e){"
"return String(v);" /* String() allows symbols, ToString() internal algorithm doesn't. */
"}"
"};"
"})(Duktape.enc)");
duk_put_prop_string(ctx, -2, "format");
flags = flags_orig;
if (!(flags & DUK_CONSOLE_STDOUT_ONLY) && !(flags & DUK_CONSOLE_STDERR_ONLY)) {
/* No output indicators were specified; these levels go to stdout. */
flags |= DUK_CONSOLE_STDOUT_ONLY;
}
duk__console_reg_vararg_func(ctx, duk__console_assert, "assert", flags);
duk__console_reg_vararg_func(ctx, duk__console_log, "log", flags);
duk__console_reg_vararg_func(ctx, duk__console_log, "debug", flags); /* alias to console.log */
duk__console_reg_vararg_func(ctx, duk__console_trace, "trace", flags);
duk__console_reg_vararg_func(ctx, duk__console_info, "info", flags);
flags = flags_orig;
if (!(flags & DUK_CONSOLE_STDOUT_ONLY) && !(flags & DUK_CONSOLE_STDERR_ONLY)) {
/* No output indicators were specified; these levels go to stderr. */
flags |= DUK_CONSOLE_STDERR_ONLY;
}
duk__console_reg_vararg_func(ctx, duk__console_warn, "warn", flags);
duk__console_reg_vararg_func(ctx, duk__console_error, "error", flags);
duk__console_reg_vararg_func(ctx, duk__console_error, "exception", flags); /* alias to console.error */
duk__console_reg_vararg_func(ctx, duk__console_dir, "dir", flags);
duk_put_global_string(ctx, "console");
/* Proxy wrapping: ensures any undefined console method calls are
* ignored silently. This was required specifically by the
* DeveloperToolsWG proposal (and was implemented also by Firefox:
* https://bugzilla.mozilla.org/show_bug.cgi?id=629607). This is
* apparently no longer the preferred way of implementing console.
* When Proxy is enabled, whitelist at least .toJSON() to avoid
* confusing JX serialization of the console object.
*/
if (flags & DUK_CONSOLE_PROXY_WRAPPER) {
/* Tolerate failure to initialize Proxy wrapper in case
* Proxy support is disabled.
*/
(void) duk_peval_string_noresult(ctx,
"(function(){"
"var D=function(){};"
"var W={toJSON:true};" /* whitelisted */
"console=new Proxy(console,{"
"get:function(t,k){"
"var v=t[k];"
"return typeof v==='function'||W[k]?v:D;"
"}"
"});"
"})();"
);
}
}

View File

@ -0,0 +1,29 @@
#if !defined(DUK_CONSOLE_H_INCLUDED)
#define DUK_CONSOLE_H_INCLUDED
#include "duktape.h"
#if defined(__cplusplus)
extern "C" {
#endif
/* Use a proxy wrapper to make undefined methods (console.foo()) no-ops. */
#define DUK_CONSOLE_PROXY_WRAPPER (1U << 0)
/* Flush output after every call. */
#define DUK_CONSOLE_FLUSH (1U << 1)
/* Send output to stdout only (default is mixed stdout/stderr). */
#define DUK_CONSOLE_STDOUT_ONLY (1U << 2)
/* Send output to stderr only (default is mixed stdout/stderr). */
#define DUK_CONSOLE_STDERR_ONLY (1U << 3)
/* Initialize the console system */
extern void duk_console_init(duk_context *ctx, duk_uint_t flags);
#if defined(__cplusplus)
}
#endif /* end 'extern "C"' wrapper */
#endif /* DUK_CONSOLE_H_INCLUDED */

99755
core/src/duktape/duktape.c Normal file

File diff suppressed because it is too large Load Diff

1450
core/src/duktape/duktape.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,9 @@
#include <bandplan.h>
#include <gui/bandplan.h>
#include <fstream>
#include <spdlog/spdlog.h>
#include <filesystem>
#include <sstream>
#include <iomanip>
namespace bandplan {
std::map<std::string, BandPlan_t> bandplans;
@ -71,7 +76,7 @@ namespace bandplan {
void loadBandPlan(std::string path) {
std::ifstream file(path.c_str());
json data;
data << file;
file >> data;
file.close();
BandPlan_t plan = data.get<BandPlan_t>();
@ -114,7 +119,7 @@ namespace bandplan {
}
std::ifstream file(path.c_str());
json data;
data << file;
file >> data;
file.close();
colorTable = data.get<std::map<std::string, BandPlanColor_t>>();

View File

@ -1,11 +1,7 @@
#pragma once
#include <json.hpp>
#include <fstream>
#include <spdlog/spdlog.h>
#include <filesystem>
#include <sstream>
#include <iomanip>
#include <imgui/imgui.h>
#include <stdint.h>
using nlohmann::json;
@ -13,8 +9,8 @@ namespace bandplan {
struct Band_t {
std::string name;
std::string type;
float start;
float end;
double start;
double end;
};
void to_json(json& j, const Band_t& b);

View File

@ -0,0 +1,59 @@
#include <gui/dialogs/credits.h>
#include <imgui.h>
#include <gui/icons.h>
#include <config.h>
namespace credits {
ImFont* bigFont;
void init() {
// TODO: Have a font manager instead
bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(ROOT_DIR "/res/fonts/Roboto-Medium.ttf")).c_str(), 128.0f);
}
void show() {
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20.0f, 20.0f));
ImGui::OpenPopup("Credits");
ImGui::BeginPopupModal("Credits", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove);
ImGui::PushFont(bigFont);
ImGui::Text("SDR++ ");
ImGui::PopFont();
ImGui::SameLine();
ImGui::Image(icons::LOGO, ImVec2(128, 128));
ImGui::Spacing();
ImGui::Spacing();
ImGui::Spacing();
ImGui::Text("This software is brought to you by\n\n");
ImGui::Columns(3, "CreditColumns", true);
// Contributors
ImGui::Text("Contributors");
ImGui::BulletText("Ryzerth (Creator)");
ImGui::BulletText("aosync");
ImGui::BulletText("Benjamin Kyd");
ImGui::BulletText("Tobias Mädel");
ImGui::BulletText("Raov");
ImGui::BulletText("Howard0su");
// Libraries
ImGui::NextColumn();
ImGui::Text("Libraries");
ImGui::BulletText("SoapySDR (PothosWare)");
ImGui::BulletText("Dear ImGui (ocornut)");
ImGui::BulletText("spdlog (gabime)");
ImGui::BulletText("json (nlohmann)");
ImGui::BulletText("portaudio (PA Comm.)");
// Patrons
ImGui::NextColumn();
ImGui::Text("Patrons");
ImGui::BulletText("SignalsEverywhere");
ImGui::BulletText("Lee Donaghy");
ImGui::EndPopup();
ImGui::PopStyleVar(1);
}
}

View File

@ -0,0 +1,6 @@
#pragma once
namespace credits {
void init();
void show();
}

View File

@ -1,4 +1,5 @@
#include <frequency_select.h>
#include <gui/frequency_select.h>
#include <config.h>
bool isInArea(ImVec2 val, ImVec2 min, ImVec2 max) {
return val.x >= min.x && val.x < max.x && val.y >= min.y && val.y < max.y;
@ -9,9 +10,10 @@ FrequencySelect::FrequencySelect() {
}
void FrequencySelect::init() {
font = ImGui::GetIO().Fonts->AddFontFromFileTTF((config::getRootDirectory() + "/res/fonts/Roboto-Medium.ttf").c_str(), 42.0f);
font = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(ROOT_DIR "/res/fonts/Roboto-Medium.ttf")).c_str(), 42.0f);
for (int i = 0; i < 12; i++) {
digits[i] = 0;
}
}

View File

@ -2,7 +2,6 @@
#include <imgui.h>
#include <imgui_internal.h>
#include <stdint.h>
#include <config.h>
class FrequencySelect {
public:

7
core/src/gui/gui.cpp Normal file
View File

@ -0,0 +1,7 @@
#include <gui/gui.h>
namespace gui {
ImGui::WaterFall waterfall;
FrequencySelect freqSelect;
Menu menu;
};

13
core/src/gui/gui.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include <gui/waterfall.h>
#include <gui/frequency_select.h>
#include <gui/menu.h>
#include <module.h>
namespace gui {
SDRPP_EXPORT ImGui::WaterFall waterfall;
SDRPP_EXPORT FrequencySelect freqSelect;
SDRPP_EXPORT Menu menu;
void selectSource(std::string name);
};

View File

@ -1,4 +1,7 @@
#include <icons.h>
#include <gui/icons.h>
#include <stdint.h>
#include <GL/glew.h>
#include <config.h>
#define STB_IMAGE_IMPLEMENTATION
#include <imgui/stb_image.h>
@ -11,7 +14,7 @@ namespace icons {
GLuint loadTexture(std::string path) {
int w,h,n;
stbi_uc* data = stbi_load(path.c_str(), &w, &h, &n, NULL);
stbi_uc* data = stbi_load(path.c_str(), &w, &h, &n, 0);
GLuint texId;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);
@ -24,9 +27,9 @@ namespace icons {
}
void load() {
LOGO = (ImTextureID)loadTexture(config::getRootDirectory() + "/res/icons/sdrpp.png");
PLAY = (ImTextureID)loadTexture(config::getRootDirectory() + "/res/icons/play.png");
STOP = (ImTextureID)loadTexture(config::getRootDirectory() + "/res/icons/stop.png");
MENU = (ImTextureID)loadTexture(config::getRootDirectory() + "/res/icons/menu.png");
LOGO = (ImTextureID)(uintptr_t)loadTexture(ROOT_DIR "/res/icons/sdrpp.png");
PLAY = (ImTextureID)(uintptr_t)loadTexture(ROOT_DIR "/res/icons/play.png");
STOP = (ImTextureID)(uintptr_t)loadTexture(ROOT_DIR "/res/icons/stop.png");
MENU = (ImTextureID)(uintptr_t)loadTexture(ROOT_DIR "/res/icons/menu.png");
}
}

View File

@ -1,8 +1,7 @@
#pragma once
#include <imgui/imgui.h>
#include <stdint.h>
#include <GL/glew.h>
#include <config.h>
#include <string>
namespace icons {
extern ImTextureID LOGO;
@ -10,6 +9,6 @@ namespace icons {
extern ImTextureID STOP;
extern ImTextureID MENU;
GLuint loadTexture(char* path);
GLuint loadTexture(std::string path);
void load();
}

View File

@ -0,0 +1,495 @@
#include <gui/main_window.h>
#include <gui/gui.h>
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <imgui_plot.h>
#include <dsp/resampling.h>
#include <dsp/demodulator.h>
#include <dsp/filter.h>
#include <thread>
#include <complex>
#include <dsp/source.h>
#include <dsp/math.h>
#include <gui/waterfall.h>
#include <gui/frequency_select.h>
#include <fftw3.h>
#include <signal_path/dsp.h>
#include <gui/icons.h>
#include <gui/bandplan.h>
#include <watcher.h>
#include <module.h>
#include <signal_path/vfo_manager.h>
#include <signal_path/audio.h>
#include <gui/style.h>
#include <config.h>
#include <signal_path/signal_path.h>
#include <core.h>
#include <gui/menus/source.h>
#include <gui/menus/display.h>
#include <gui/menus/bandplan.h>
#include <gui/menus/audio.h>
#include <gui/menus/scripting.h>
#include <gui/dialogs/credits.h>
#include <signal_path/source.h>
std::thread worker;
std::mutex fft_mtx;
fftwf_complex *fft_in, *fft_out;
fftwf_plan p;
float* tempData;
char buf[1024];
int fftSize = 8192 * 8;
std::vector<float> _data;
std::vector<float> fftTaps;
void fftHandler(dsp::complex_t* samples) {
fftwf_execute(p);
int half = fftSize / 2;
for (int i = 0; i < half; i++) {
_data.push_back(log10(std::abs(std::complex<float>(fft_out[half + i][0], fft_out[half + i][1])) / (float)fftSize) * 10.0);
}
for (int i = 0; i < half; i++) {
_data.push_back(log10(std::abs(std::complex<float>(fft_out[i][0], fft_out[i][1])) / (float)fftSize) * 10.0);
}
for (int i = 5; i < fftSize; i++) {
_data[i] = (_data[i - 4] + _data[i - 3] + _data[i - 2] + _data[i - 1] + _data[i]) / 5.0;
}
gui::waterfall.pushFFT(_data, fftSize);
_data.clear();
}
dsp::NullSink sink;
watcher<uint64_t> freq((uint64_t)90500000);
watcher<double> vfoFreq(92000000.0);
float dummyVolume = 1.0;
float* volume = &dummyVolume;
float fftMin = -70.0;
float fftMax = 0.0;
watcher<double> offset(0.0, true);
watcher<float> bw(8000000.0, true);
bool playing = false;
watcher<bool> dcbias(false, false);
bool showCredits = false;
std::string audioStreamName = "";
std::string sourceName = "";
int menuWidth = 300;
bool grabbingMenu = false;
int newWidth = 300;
int fftHeight = 300;
bool showMenu = true;
dsp::stream<dsp::complex_t> dummyStream;
void windowInit() {
gui::waterfall.init();
credits::init();
core::configManager.aquire();
gui::menu.order = core::configManager.conf["menuOrder"].get<std::vector<std::string>>();
core::configManager.release();
gui::menu.registerEntry("Source", sourecmenu::draw, NULL);
gui::menu.registerEntry("Audio", audiomenu::draw, NULL);
gui::menu.registerEntry("Scripting", scriptingmenu::draw, NULL);
gui::menu.registerEntry("Band Plan", bandplanmenu::draw, NULL);
gui::menu.registerEntry("Display", displaymenu::draw, NULL);
gui::freqSelect.init();
fft_in = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSize);
fft_out = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSize);
p = fftwf_plan_dft_1d(fftSize, fft_in, fft_out, FFTW_FORWARD, FFTW_ESTIMATE);
sigpath::signalPath.init(8000000, 20, fftSize, &dummyStream, (dsp::complex_t*)fft_in, fftHandler);
sigpath::signalPath.start();
spdlog::info("Loading modules");
mod::loadFromList(ROOT_DIR "/module_list.json");
sourecmenu::init();
audiomenu::init();
scriptingmenu::init();
bandplanmenu::init();
displaymenu::init();
// Load last source configuration
// Also add a loading screen
// Adjustable "snap to grid" for each VFO
// Finish the recorder module
// Add squelsh
// Bandwidth ajustment
// CW and RAW modes;
// Bring VFO to a visible place when changing sample rate if it's smaller
// Add save config for modules
// Do VFO in two steps: First sample rate conversion, then filtering
// And a module add/remove/change order menu
// Update UI settings
core::configManager.aquire();
fftMin = core::configManager.conf["min"];
fftMax = core::configManager.conf["max"];
gui::waterfall.setFFTMin(fftMin);
gui::waterfall.setWaterfallMin(fftMin);
gui::waterfall.setFFTMax(fftMax);
gui::waterfall.setWaterfallMax(fftMax);
double frequency = core::configManager.conf["frequency"];
gui::freqSelect.setFrequency(frequency);
gui::freqSelect.frequencyChanged = false;
sigpath::sourceManager.tune(frequency);
gui::waterfall.setCenterFrequency(frequency);
gui::waterfall.setBandwidth(8000000);
gui::waterfall.setViewBandwidth(8000000);
bw.val = 8000000;
gui::waterfall.vfoFreqChanged = false;
gui::waterfall.centerFreqMoved = false;
gui::waterfall.selectFirstVFO();
menuWidth = core::configManager.conf["menuWidth"];
newWidth = menuWidth;
fftHeight = core::configManager.conf["fftHeight"];
gui::waterfall.setFFTHeight(fftHeight);
core::configManager.release();
}
void setVFO(double freq) {
ImGui::WaterfallVFO* vfo = gui::waterfall.vfos[gui::waterfall.selectedVFO];
double currentOff = vfo->centerOffset;
double currentTune = gui::waterfall.getCenterFrequency() + vfo->generalOffset;
double delta = freq - currentTune;
double newVFO = currentOff + delta;
double vfoBW = vfo->bandwidth;
double vfoBottom = newVFO - (vfoBW / 2.0);
double vfoTop = newVFO + (vfoBW / 2.0);
double view = gui::waterfall.getViewOffset();
double viewBW = gui::waterfall.getViewBandwidth();
double viewBottom = view - (viewBW / 2.0);
double viewTop = view + (viewBW / 2.0);
double wholeFreq = gui::waterfall.getCenterFrequency();
double BW = gui::waterfall.getBandwidth();
double bottom = -(BW / 2.0);
double top = (BW / 2.0);
// VFO still fints in the view
if (vfoBottom > viewBottom && vfoTop < viewTop) {
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFO);
return;
}
// VFO too low for current SDR tuning
if (vfoBottom < bottom) {
gui::waterfall.setViewOffset((BW / 2.0) - (viewBW / 2.0));
double newVFOOffset = (BW / 2.0) - (vfoBW / 2.0) - (viewBW / 10.0);
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFOOffset);
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
sigpath::sourceManager.tune(freq - newVFOOffset);
return;
}
// VFO too high for current SDR tuning
if (vfoTop > top) {
gui::waterfall.setViewOffset((viewBW / 2.0) - (BW / 2.0));
double newVFOOffset = (vfoBW / 2.0) - (BW / 2.0) + (viewBW / 10.0);
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFOOffset);
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
sigpath::sourceManager.tune(freq - newVFOOffset);
return;
}
// VFO is still without the SDR's bandwidth
if (delta < 0) {
double newViewOff = vfoTop - (viewBW / 2.0) + (viewBW / 10.0);
double newViewBottom = newViewOff - (viewBW / 2.0);
double newViewTop = newViewOff + (viewBW / 2.0);
if (newViewBottom > bottom) {
gui::waterfall.setViewOffset(newViewOff);
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFO);
return;
}
gui::waterfall.setViewOffset((BW / 2.0) - (viewBW / 2.0));
double newVFOOffset = (BW / 2.0) - (vfoBW / 2.0) - (viewBW / 10.0);
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFOOffset);
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
sigpath::sourceManager.tune(freq - newVFOOffset);
}
else {
double newViewOff = vfoBottom + (viewBW / 2.0) - (viewBW / 10.0);
double newViewBottom = newViewOff - (viewBW / 2.0);
double newViewTop = newViewOff + (viewBW / 2.0);
if (newViewTop < top) {
gui::waterfall.setViewOffset(newViewOff);
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFO);
return;
}
gui::waterfall.setViewOffset((viewBW / 2.0) - (BW / 2.0));
double newVFOOffset = (vfoBW / 2.0) - (BW / 2.0) + (viewBW / 10.0);
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFOOffset);
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
sigpath::sourceManager.tune(freq - newVFOOffset);
}
}
void drawWindow() {
ImGui::Begin("Main", NULL, WINDOW_FLAGS);
ImGui::WaterfallVFO* vfo = gui::waterfall.vfos[gui::waterfall.selectedVFO];
if (vfo->centerOffsetChanged) {
gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency() + vfo->generalOffset);
gui::freqSelect.frequencyChanged = false;
core::configManager.aquire();
core::configManager.conf["frequency"] = gui::freqSelect.frequency;
core::configManager.release(true);
}
sigpath::vfoManager.updateFromWaterfall(&gui::waterfall);
if (gui::waterfall.selectedVFOChanged) {
gui::waterfall.selectedVFOChanged = false;
gui::freqSelect.setFrequency(vfo->generalOffset + gui::waterfall.getCenterFrequency());
gui::freqSelect.frequencyChanged = false;
audioStreamName = audio::getNameFromVFO(gui::waterfall.selectedVFO);
if (audioStreamName != "") {
volume = &audio::streams[audioStreamName]->volume;
}
core::configManager.aquire();
core::configManager.conf["frequency"] = gui::freqSelect.frequency;
core::configManager.release(true);
}
if (gui::freqSelect.frequencyChanged) {
gui::freqSelect.frequencyChanged = false;
setVFO(gui::freqSelect.frequency);
vfo->centerOffsetChanged = false;
vfo->lowerOffsetChanged = false;
vfo->upperOffsetChanged = false;
core::configManager.aquire();
core::configManager.conf["frequency"] = gui::freqSelect.frequency;
core::configManager.release(true);
}
if (gui::waterfall.centerFreqMoved) {
gui::waterfall.centerFreqMoved = false;
sigpath::sourceManager.tune(gui::waterfall.getCenterFrequency());
gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency() + vfo->generalOffset);
core::configManager.aquire();
core::configManager.conf["frequency"] = gui::freqSelect.frequency;
core::configManager.release(true);
}
int _fftHeight = gui::waterfall.getFFTHeight();
if (fftHeight != _fftHeight) {
fftHeight = _fftHeight;
core::configManager.aquire();
core::configManager.conf["fftHeight"] = fftHeight;
core::configManager.release(true);
}
ImVec2 vMin = ImGui::GetWindowContentRegionMin();
ImVec2 vMax = ImGui::GetWindowContentRegionMax();
int width = vMax.x - vMin.x;
int height = vMax.y - vMin.y;
// To Bar
if (ImGui::ImageButton(icons::MENU, ImVec2(40, 40), ImVec2(0, 0), ImVec2(1, 1), 0)) {
showMenu = !showMenu;
}
ImGui::SameLine();
if (playing) {
if (ImGui::ImageButton(icons::STOP, ImVec2(40, 40), ImVec2(0, 0), ImVec2(1, 1), 0)) {
sigpath::sourceManager.stop();
playing = false;
}
}
else { // TODO: Might need to check if there even is a device
if (ImGui::ImageButton(icons::PLAY, ImVec2(40, 40), ImVec2(0, 0), ImVec2(1, 1), 0)) {
sigpath::sourceManager.start();
// TODO: tune in module instead
sigpath::sourceManager.tune(gui::waterfall.getCenterFrequency());
playing = true;
}
}
ImGui::SameLine();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 8);
ImGui::SetNextItemWidth(200);
if (ImGui::SliderFloat("##_2_", volume, 0.0, 1.0, "")) {
if (audioStreamName != "") {
core::configManager.aquire();
if (!core::configManager.conf["audio"].contains(audioStreamName)) {
//saveAudioConfig(audioStreamName);
// TODO: FIX THIS SHIT
}
audio::streams[audioStreamName]->audio->setVolume(*volume);
core::configManager.conf["audio"][audioStreamName]["volume"] = *volume;
core::configManager.release(true);
}
}
ImGui::SameLine();
gui::freqSelect.draw();
ImGui::SameLine();
// Logo button
ImGui::SetCursorPosX(ImGui::GetWindowSize().x - 48);
ImGui::SetCursorPosY(10);
if (ImGui::ImageButton(icons::LOGO, ImVec2(32, 32), ImVec2(0, 0), ImVec2(1, 1), 0)) {
showCredits = true;
}
if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
showCredits = false;
}
if (ImGui::IsKeyPressed(GLFW_KEY_ESCAPE)) {
showCredits = false;
}
// Handle menu resize
float curY = ImGui::GetCursorPosY();
ImVec2 winSize = ImGui::GetWindowSize();
ImVec2 mousePos = ImGui::GetMousePos();
bool click = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
bool down = ImGui::IsMouseDown(ImGuiMouseButton_Left);
if (grabbingMenu) {
newWidth = mousePos.x;
newWidth = std::clamp<float>(newWidth, 250, winSize.x - 250);
ImGui::GetForegroundDrawList()->AddLine(ImVec2(newWidth, curY), ImVec2(newWidth, winSize.y - 10), ImGui::GetColorU32(ImGuiCol_SeparatorActive));
}
if (mousePos.x >= newWidth - 2 && mousePos.x <= newWidth + 2 && mousePos.y > curY) {
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
if (click) {
grabbingMenu = true;
}
}
else {
ImGui::SetMouseCursor(ImGuiMouseCursor_Arrow);
}
if(!down && grabbingMenu) {
grabbingMenu = false;
menuWidth = newWidth;
core::configManager.aquire();
core::configManager.conf["menuWidth"] = menuWidth;
core::configManager.release(true);
}
// Left Column
if (showMenu) {
ImGui::Columns(3, "WindowColumns", false);
ImGui::SetColumnWidth(0, menuWidth);
ImGui::SetColumnWidth(1, winSize.x - menuWidth - 60);
ImGui::SetColumnWidth(2, 60);
ImGui::BeginChild("Left Column");
float menuColumnWidth = ImGui::GetContentRegionAvailWidth();
gui::menu.draw();
if(ImGui::CollapsingHeader("Debug")) {
ImGui::Text("Frame time: %.3f ms/frame", 1000.0 / ImGui::GetIO().Framerate);
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());
if (ImGui::Checkbox("Test technique", &dcbias.val)) {
sigpath::signalPath.setDCBiasCorrection(dcbias.val);
}
ImGui::Spacing();
}
ImGui::EndChild();
}
else {
// When hiding the menu bar
ImGui::Columns(3, "WindowColumns", false);
ImGui::SetColumnWidth(0, 8);
ImGui::SetColumnWidth(1, winSize.x - 8 - 60);
ImGui::SetColumnWidth(2, 60);
}
// Right Column
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
ImGui::NextColumn();
ImGui::PopStyleVar();
ImGui::BeginChild("Waterfall");
gui::waterfall.draw();
ImGui::EndChild();
ImGui::NextColumn();
ImGui::BeginChild("WaterfallControls");
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Zoom").x / 2.0));
ImGui::Text("Zoom");
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10);
ImGui::VSliderFloat("##_7_", ImVec2(20.0, 150.0), &bw.val, gui::waterfall.getBandwidth(), 1000.0, "");
ImGui::NewLine();
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Max").x / 2.0));
ImGui::Text("Max");
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10);
if (ImGui::VSliderFloat("##_8_", ImVec2(20.0, 150.0), &fftMax, 0.0, -100.0, "")) {
fftMax = std::max<float>(fftMax, fftMin + 10);
core::configManager.aquire();
core::configManager.conf["max"] = fftMax;
core::configManager.release(true);
}
ImGui::NewLine();
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Min").x / 2.0));
ImGui::Text("Min");
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10);
if (ImGui::VSliderFloat("##_9_", ImVec2(20.0, 150.0), &fftMin, 0.0, -100.0, "")) {
fftMin = std::min<float>(fftMax - 10, fftMin);
core::configManager.aquire();
core::configManager.conf["min"] = fftMin;
core::configManager.release(true);
}
ImGui::EndChild();
if (bw.changed()) {
gui::waterfall.setViewBandwidth(bw.val);
gui::waterfall.setViewOffset(vfo->centerOffset);
}
gui::waterfall.setFFTMin(fftMin);
gui::waterfall.setFFTMax(fftMax);
gui::waterfall.setWaterfallMin(fftMin);
gui::waterfall.setWaterfallMax(fftMax);
ImGui::End();
if (showCredits) {
credits::show();
}
}

View File

@ -0,0 +1,7 @@
#pragma once
#include "imgui.h"
#define WINDOW_FLAGS ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground
void windowInit();
void drawWindow();

43
core/src/gui/menu.cpp Normal file
View File

@ -0,0 +1,43 @@
#include <gui/menu.h>
#include <imgui/imgui.h>
Menu::Menu() {
}
void Menu::registerEntry(std::string name, void (*drawHandler)(void* ctx), void* ctx) {
MenuItem_t item;
item.drawHandler = drawHandler;
item.ctx = ctx;
items[name] = item;
if (!isInOrderList(name)) {
order.push_back(name);
}
}
void Menu::removeEntry(std::string name) {
items.erase(name);
}
void Menu::draw() {
MenuItem_t item;
for (std::string name : order) {
if (items.find(name) == items.end()) {
continue;
}
item = items[name];
if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
item.drawHandler(item.ctx);
ImGui::Spacing();
}
}
}
bool Menu::isInOrderList(std::string name) {
for (std::string _name : order) {
if (_name == name) {
return true;
}
}
return false;
}

25
core/src/gui/menu.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include <string>
#include <vector>
#include <map>
class Menu {
public:
Menu();
struct MenuItem_t {
void (*drawHandler)(void* ctx);
void* ctx;
};
void registerEntry(std::string name, void (*drawHandler)(void* ctx), void* ctx = NULL);
void removeEntry(std::string name);
void draw();
std::vector<std::string> order;
private:
bool isInOrderList(std::string name);
std::map<std::string, MenuItem_t> items;
};

View File

@ -0,0 +1,138 @@
#include <gui/menus/audio.h>
#include <gui/bandplan.h>
#include <gui/gui.h>
#include <core.h>
#include <signal_path/audio.h>
namespace audiomenu {
// Note: Those are supposed to be the ones from the volume slider
std::string audioStreamName;
float* volume;
void loadAudioConfig(std::string name) {
json audioSettings = core::configManager.conf["audio"][name];
std::string devName = audioSettings["device"];
auto _devIt = std::find(audio::streams[name]->audio->deviceNames.begin(), audio::streams[name]->audio->deviceNames.end(), devName);
// If audio device doesn't exist anymore
if (_devIt == audio::streams[name]->audio->deviceNames.end()) {
audio::streams[name]->audio->setToDefault();
int deviceId = audio::streams[name]->audio->getDeviceId();
audio::setAudioDevice(name, deviceId, audio::streams[name]->audio->devices[deviceId].sampleRates[0]);
audio::streams[name]->sampleRateId = 0;
audio::streams[name]->volume = audioSettings["volume"];
audio::streams[name]->audio->setVolume(audio::streams[name]->volume);
return;
}
int deviceId = std::distance(audio::streams[name]->audio->deviceNames.begin(), _devIt);
float sr = audioSettings["sampleRate"];
auto _srIt = std::find(audio::streams[name]->audio->devices[deviceId].sampleRates.begin(), audio::streams[name]->audio->devices[deviceId].sampleRates.end(), sr);
// If sample rate doesn't exist anymore
if (_srIt == audio::streams[name]->audio->devices[deviceId].sampleRates.end()) {
audio::streams[name]->sampleRateId = 0;
audio::setAudioDevice(name, deviceId, audio::streams[name]->audio->devices[deviceId].sampleRates[0]);
audio::streams[name]->volume = audioSettings["volume"];
audio::streams[name]->audio->setVolume(audio::streams[name]->volume);
return;
}
int samplerateId = std::distance(audio::streams[name]->audio->devices[deviceId].sampleRates.begin(), _srIt);
audio::streams[name]->sampleRateId = samplerateId;
audio::setAudioDevice(name, deviceId, audio::streams[name]->audio->devices[deviceId].sampleRates[samplerateId]);
audio::streams[name]->deviceId = deviceId;
audio::streams[name]->volume = audioSettings["volume"];
audio::streams[name]->audio->setVolume(audio::streams[name]->volume);
}
void saveAudioConfig(std::string name) {
core::configManager.conf["audio"][name]["device"] = audio::streams[name]->audio->deviceNames[audio::streams[name]->deviceId];
core::configManager.conf["audio"][name]["volume"] = audio::streams[name]->volume;
core::configManager.conf["audio"][name]["sampleRate"] = audio::streams[name]->sampleRate;
}
void init() {
for (auto [name, stream] : audio::streams) {
if (core::configManager.conf["audio"].contains(name)) {
bool running = audio::streams[name]->running;
audio::stopStream(name);
loadAudioConfig(name);
if (running) {
audio::startStream(name);
}
}
}
audioStreamName = audio::getNameFromVFO(gui::waterfall.selectedVFO);
if (audioStreamName != "") {
volume = &audio::streams[audioStreamName]->volume;
}
}
void draw(void* ctx) {
float menuColumnWidth = ImGui::GetContentRegionAvailWidth();
int count = 0;
int maxCount = audio::streams.size();
for (auto const& [name, stream] : audio::streams) {
int deviceId;
float vol = 1.0f;
deviceId = stream->audio->getDeviceId();
ImGui::SetCursorPosX((menuColumnWidth / 2.0f) - (ImGui::CalcTextSize(name.c_str()).x / 2.0f));
ImGui::Text("%s", name.c_str());
ImGui::PushItemWidth(menuColumnWidth);
bool running = stream->running;
if (ImGui::Combo(("##_audio_dev_0_"+ name).c_str(), &stream->deviceId, stream->audio->devTxtList.c_str())) {
audio::stopStream(name);
audio::setAudioDevice(name, stream->deviceId, stream->audio->devices[deviceId].sampleRates[0]);
if (running) {
audio::startStream(name);
}
stream->sampleRateId = 0;
// Create config if it doesn't exist
core::configManager.aquire();
if (!core::configManager.conf["audio"].contains(name)) {
saveAudioConfig(name);
}
core::configManager.conf["audio"][name]["device"] = stream->audio->deviceNames[stream->deviceId];
core::configManager.conf["audio"][name]["sampleRate"] = stream->audio->devices[stream->deviceId].sampleRates[0];
core::configManager.release(true);
}
if (ImGui::Combo(("##_audio_sr_0_" + name).c_str(), &stream->sampleRateId, stream->audio->devices[deviceId].txtSampleRates.c_str())) {
audio::stopStream(name);
audio::setSampleRate(name, stream->audio->devices[deviceId].sampleRates[stream->sampleRateId]);
if (running) {
audio::startStream(name);
}
// Create config if it doesn't exist
core::configManager.aquire();
if (!core::configManager.conf["audio"].contains(name)) {
saveAudioConfig(name);
}
core::configManager.conf["audio"][name]["sampleRate"] = stream->audio->devices[deviceId].sampleRates[stream->sampleRateId];
core::configManager.release(true);
}
if (ImGui::SliderFloat(("##_audio_vol_0_" + name).c_str(), &stream->volume, 0.0f, 1.0f, "")) {
stream->audio->setVolume(stream->volume);
// Create config if it doesn't exist
core::configManager.aquire();
if (!core::configManager.conf["audio"].contains(name)) {
saveAudioConfig(name);
}
core::configManager.conf["audio"][name]["volume"] = stream->volume;
core::configManager.release(true);
}
ImGui::PopItemWidth();
count++;
if (count < maxCount) {
ImGui::Spacing();
ImGui::Separator();
}
ImGui::Spacing();
}
}
};

View File

@ -0,0 +1,6 @@
#pragma once
namespace audiomenu {
void init();
void draw(void* ctx);
};

View File

@ -0,0 +1,51 @@
#include <gui/menus/bandplan.h>
#include <gui/bandplan.h>
#include <gui/gui.h>
#include <core.h>
namespace bandplanmenu {
int bandplanId;
bool bandPlanEnabled;
void init() {
// todo: check if the bandplan wasn't removed
if (bandplan::bandplanNames.size() == 0) {
gui::waterfall.hideBandplan();
return;
}
if (bandplan::bandplans.find(core::configManager.conf["bandPlan"]) != bandplan::bandplans.end()) {
std::string name = core::configManager.conf["bandPlan"];
bandplanId = std::distance(bandplan::bandplanNames.begin(), std::find(bandplan::bandplanNames.begin(),
bandplan::bandplanNames.end(), name));
gui::waterfall.bandplan = &bandplan::bandplans[name];
}
else {
gui::waterfall.bandplan = &bandplan::bandplans[bandplan::bandplanNames[0]];
}
bandPlanEnabled = core::configManager.conf["bandPlanEnabled"];
bandPlanEnabled ? gui::waterfall.showBandplan() : gui::waterfall.hideBandplan();
}
void draw(void* ctx) {
float menuColumnWidth = ImGui::GetContentRegionAvailWidth();
ImGui::PushItemWidth(menuColumnWidth);
if (ImGui::Combo("##_4_", &bandplanId, bandplan::bandplanNameTxt.c_str())) {
gui::waterfall.bandplan = &bandplan::bandplans[bandplan::bandplanNames[bandplanId]];
core::configManager.aquire();
core::configManager.conf["bandPlan"] = bandplan::bandplanNames[bandplanId];
core::configManager.release(true);
}
ImGui::PopItemWidth();
if (ImGui::Checkbox("Enabled", &bandPlanEnabled)) {
bandPlanEnabled ? gui::waterfall.showBandplan() : gui::waterfall.hideBandplan();
core::configManager.aquire();
core::configManager.conf["bandPlanEnabled"] = bandPlanEnabled;
core::configManager.release(true);
}
bandplan::BandPlan_t plan = bandplan::bandplans[bandplan::bandplanNames[bandplanId]];
ImGui::Text("Country: %s (%s)", plan.countryName.c_str(), plan.countryCode.c_str());
ImGui::Text("Author: %s", plan.authorName.c_str());
}
};

View File

@ -0,0 +1,6 @@
#pragma once
namespace bandplanmenu {
void init();
void draw(void* ctx);
};

View File

@ -0,0 +1,22 @@
#include <gui/menus/display.h>
#include <imgui.h>
#include <gui/gui.h>
#include <core.h>
namespace displaymenu {
bool showWaterfall;
void init() {
showWaterfall = core::configManager.conf["showWaterfall"];
showWaterfall ? gui::waterfall.showWaterfall() : gui::waterfall.hideWaterfall();
}
void draw(void* ctx) {
if (ImGui::Checkbox("Show Waterfall", &showWaterfall)) {
showWaterfall ? gui::waterfall.showWaterfall() : gui::waterfall.hideWaterfall();
core::configManager.aquire();
core::configManager.conf["showWaterfall"] = showWaterfall;
core::configManager.release(true);
}
}
}

View File

@ -0,0 +1,6 @@
#pragma once
namespace displaymenu {
void init();
void draw(void* ctx);
}

View File

@ -0,0 +1,22 @@
#include <gui/menus/scripting.h>
#include <core.h>
#include <gui/style.h>
#include <imgui/imgui.h>
namespace scriptingmenu {
void init() {
}
void draw(void* ctx) {
float menuWidth = ImGui::GetContentRegionAvailWidth();
for (const auto& [name, script] : core::scriptManager.scripts) {
bool running = script->running;
if (running) { style::beginDisabled(); }
if (ImGui::Button(name.c_str(), ImVec2(menuWidth, 0))) {
script->run();
}
if (running) { style::endDisabled(); }
}
}
}

View File

@ -0,0 +1,6 @@
#pragma once
namespace scriptingmenu {
void init();
void draw(void* ctx);
}

View File

@ -0,0 +1,39 @@
#include <gui/menus/source.h>
#include <imgui.h>
#include <gui/gui.h>
#include <core.h>
#include <signal_path/signal_path.h>
namespace sourecmenu {
int sourceId = 0;
double freqOffset = 0.0;
void init() {
// Select default
// TODO: Replace by setting
if (sigpath::sourceManager.sourceNames.size() > 0) {
sigpath::sourceManager.selectSource(sigpath::sourceManager.sourceNames[0]);
}
sigpath::sourceManager.setTuningOffset(0);
}
void draw(void* ctx) {
std::string items = "";
for (std::string name : sigpath::sourceManager.sourceNames) {
items += name;
items += '\0';
}
float itemWidth = ImGui::GetContentRegionAvailWidth();
ImGui::SetNextItemWidth(itemWidth);
if (ImGui::Combo("##source", &sourceId, items.c_str())) {
sigpath::sourceManager.selectSource(sigpath::sourceManager.sourceNames[sourceId]);
}
sigpath::sourceManager.showSelectedMenu();
ImGui::SetNextItemWidth(itemWidth - ImGui::CalcTextSize("Offset (Hz)").x - 10);
if (ImGui::InputDouble("Offset (Hz)##freq_offset", &freqOffset, 1.0, 100.0)) {
sigpath::sourceManager.setTuningOffset(freqOffset);
}
}
}

View File

@ -0,0 +1,6 @@
#pragma once
namespace sourecmenu {
void init();
void draw(void* ctx);
}

View File

@ -1,4 +1,7 @@
#include <style.h>
#include <gui/style.h>
#include <imgui.h>
#include <imgui_internal.h>
#include <config.h>
namespace style {
void setDefaultStyle() {
@ -9,12 +12,16 @@ namespace style {
ImGui::GetStyle().PopupRounding = 0.0f;
ImGui::GetStyle().ScrollbarRounding = 0.0f;
ImGui::GetIO().Fonts->AddFontFromFileTTF((config::getRootDirectory() + "/res/fonts/Roboto-Medium.ttf").c_str(), 16.0f);
ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(ROOT_DIR "/res/fonts/Roboto-Medium.ttf")).c_str(), 16.0f);
ImGui::StyleColorsDark();
//ImGui::StyleColorsLight();
}
void testtt() {
ImGui::StyleColorsLight();
}
void setDarkStyle() {
ImGui::GetStyle().WindowRounding = 0.0f;
ImGui::GetStyle().ChildRounding = 0.0f;
@ -23,7 +30,7 @@ namespace style {
ImGui::GetStyle().PopupRounding = 0.0f;
ImGui::GetStyle().ScrollbarRounding = 0.0f;
ImGui::GetIO().Fonts->AddFontFromFileTTF((config::getRootDirectory() + "/res/fonts/Roboto-Medium.ttf").c_str(), 16.0f);
ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(ROOT_DIR "/res/fonts/Roboto-Medium.ttf")).c_str(), 16.0f);
ImGui::StyleColorsDark();

View File

@ -1,11 +1,9 @@
#pragma once
#include <imgui.h>
#include <imgui_internal.h>
#include <config.h>
namespace style {
void setDefaultStyle();
void setDarkStyle();
void beginDisabled();
void endDisabled();
void testtt();
}

View File

@ -1,4 +1,9 @@
#include <waterfall.h>
#include <gui/waterfall.h>
#include <imgui.h>
#include <imgui_internal.h>
#include <GL/glew.h>
#include <imutils.h>
#include <algorithm>
float COLOR_MAP[][3] = {
{0x00, 0x00, 0x20},
@ -31,38 +36,40 @@ void doZoom(int offset, int width, int outWidth, std::vector<float> data, float*
}
}
float freq_ranges[] = {
1.0f, 2.0f, 2.5f, 5.0f,
10.0f, 20.0f, 25.0f, 50.0f,
100.0f, 200.0f, 250.0f, 500.0f,
1000.0f, 2000.0f, 2500.0f, 5000.0f,
10000.0f, 20000.0f, 25000.0f, 50000.0f,
100000.0f, 200000.0f, 250000.0f, 500000.0f,
1000000.0f, 2000000.0f, 2500000.0f, 5000000.0f,
10000000.0f, 20000000.0f, 25000000.0f, 50000000.0f
// TODO: Fix this hacky BS
double freq_ranges[] = {
1.0, 2.0, 2.5, 5.0,
10.0, 20.0, 25.0, 50.0,
100.0, 200.0, 250.0, 500.0,
1000.0, 2000.0, 2500.0, 5000.0,
10000.0, 20000.0, 25000.0, 50000.0,
100000.0, 200000.0, 250000.0, 500000.0,
1000000.0, 2000000.0, 2500000.0, 5000000.0,
10000000.0, 20000000.0, 25000000.0, 50000000.0
};
float findBestRange(float bandwidth, int maxSteps) {
double findBestRange(double bandwidth, int maxSteps) {
for (int i = 0; i < 32; i++) {
if (bandwidth / freq_ranges[i] < (float)maxSteps) {
if (bandwidth / freq_ranges[i] < (double)maxSteps) {
return freq_ranges[i];
}
}
return 50000000.0f;
return 50000000.0;
}
void printAndScale(float freq, char* buf) {
void printAndScale(double freq, char* buf) {
if (freq < 1000) {
sprintf(buf, "%.3f", freq);
sprintf(buf, "%.3lf", freq);
}
else if (freq < 1000000) {
sprintf(buf, "%.3fK", freq / 1000.0f);
sprintf(buf, "%.3lfK", freq / 1000.0);
}
else if (freq < 1000000000) {
sprintf(buf, "%.3fM", freq / 1000000.0f);
sprintf(buf, "%.3lfM", freq / 1000000.0);
}
else if (freq < 1000000000000) {
sprintf(buf, "%.3fG", freq / 1000000000.0f);
sprintf(buf, "%.3lfG", freq / 1000000000.0);
}
for (int i = strlen(buf) - 2; i >= 0; i--) {
if (buf[i] != '0') {
@ -79,10 +86,10 @@ void printAndScale(float freq, char* buf) {
namespace ImGui {
WaterFall::WaterFall() {
fftMin = -70.0f;
fftMax = 0.0f;
waterfallMin = -70.0f;
waterfallMax = 0.0f;
fftMin = -70.0;
fftMax = 0.0;
waterfallMin = -70.0;
waterfallMax = 0.0;
FFTAreaHeight = 300;
newFFTAreaHeight = FFTAreaHeight;
fftHeight = FFTAreaHeight - 50;
@ -94,14 +101,16 @@ namespace ImGui {
latestFFT = new float[1];
waterfallFb = new uint32_t[1];
viewBandwidth = 1.0f;
wholeBandwidth = 1.0f;
glGenTextures(1, &textureId);
viewBandwidth = 1.0;
wholeBandwidth = 1.0;
updatePallette(COLOR_MAP, 13);
}
void WaterFall::init() {
glGenTextures(1, &textureId);
}
void WaterFall::drawFFT() {
// Calculate scaling factor
float startLine = floorf(fftMax / vRange) * vRange;
@ -117,51 +126,51 @@ namespace ImGui {
float yPos = widgetPos.y + fftHeight + 10 - ((line - fftMin) * scaleFactor);
window->DrawList->AddLine(ImVec2(roundf(widgetPos.x + 50), roundf(yPos)),
ImVec2(roundf(widgetPos.x + dataWidth + 50), roundf(yPos)),
IM_COL32(50, 50, 50, 255), 1.0f);
IM_COL32(50, 50, 50, 255), 1.0);
sprintf(buf, "%d", (int)line);
ImVec2 txtSz = ImGui::CalcTextSize(buf);
window->DrawList->AddText(ImVec2(widgetPos.x + 40 - txtSz.x, roundf(yPos - (txtSz.y / 2))), IM_COL32( 255, 255, 255, 255 ), buf);
window->DrawList->AddText(ImVec2(widgetPos.x + 40 - txtSz.x, roundf(yPos - (txtSz.y / 2.0))), IM_COL32( 255, 255, 255, 255 ), buf);
}
// Horizontal scale
float startFreq = ceilf(lowerFreq / range) * range;
float horizScale = (float)dataWidth / viewBandwidth;
for (float freq = startFreq; freq < upperFreq; freq += range) {
float xPos = widgetPos.x + 50 + ((freq - lowerFreq) * horizScale);
double startFreq = ceilf(lowerFreq / range) * range;
double horizScale = (double)dataWidth / viewBandwidth;
for (double freq = startFreq; freq < upperFreq; freq += range) {
double xPos = widgetPos.x + 50 + ((freq - lowerFreq) * horizScale);
window->DrawList->AddLine(ImVec2(roundf(xPos), widgetPos.y + 10),
ImVec2(roundf(xPos), widgetPos.y + fftHeight + 10),
IM_COL32(50, 50, 50, 255), 1.0f);
IM_COL32(50, 50, 50, 255), 1.0);
window->DrawList->AddLine(ImVec2(roundf(xPos), widgetPos.y + fftHeight + 10),
ImVec2(roundf(xPos), widgetPos.y + fftHeight + 17),
IM_COL32(255, 255, 255, 255), 1.0f);
IM_COL32(255, 255, 255, 255), 1.0);
printAndScale(freq, buf);
ImVec2 txtSz = ImGui::CalcTextSize(buf);
window->DrawList->AddText(ImVec2(roundf(xPos - (txtSz.x / 2.0f)), widgetPos.y + fftHeight + 10 + txtSz.y), IM_COL32( 255, 255, 255, 255 ), buf);
window->DrawList->AddText(ImVec2(roundf(xPos - (txtSz.x / 2.0)), widgetPos.y + fftHeight + 10 + txtSz.y), IM_COL32( 255, 255, 255, 255 ), buf);
}
// Data
for (int i = 1; i < dataWidth; i++) {
float aPos = widgetPos.y + fftHeight + 10 - ((latestFFT[i - 1] - fftMin) * scaleFactor);
float bPos = widgetPos.y + fftHeight + 10 - ((latestFFT[i] - fftMin) * scaleFactor);
double aPos = widgetPos.y + fftHeight + 10 - ((latestFFT[i - 1] - fftMin) * scaleFactor);
double bPos = widgetPos.y + fftHeight + 10 - ((latestFFT[i] - fftMin) * scaleFactor);
if (aPos < fftMin && bPos < fftMin) {
continue;
}
aPos = std::clamp<float>(aPos, widgetPos.y + 10, widgetPos.y + fftHeight + 10);
bPos = std::clamp<float>(bPos, widgetPos.y + 10, widgetPos.y + fftHeight + 10);
aPos = std::clamp<double>(aPos, widgetPos.y + 10, widgetPos.y + fftHeight + 10);
bPos = std::clamp<double>(bPos, widgetPos.y + 10, widgetPos.y + fftHeight + 10);
window->DrawList->AddLine(ImVec2(widgetPos.x + 49 + i, roundf(aPos)),
ImVec2(widgetPos.x + 50 + i, roundf(bPos)), trace, 1.0f);
ImVec2(widgetPos.x + 50 + i, roundf(bPos)), trace, 1.0);
window->DrawList->AddLine(ImVec2(widgetPos.x + 50 + i, roundf(bPos)),
ImVec2(widgetPos.x + 50 + i, widgetPos.y + fftHeight + 10), shadow, 1.0f);
ImVec2(widgetPos.x + 50 + i, widgetPos.y + fftHeight + 10), shadow, 1.0);
}
// X Axis
window->DrawList->AddLine(ImVec2(widgetPos.x + 50, widgetPos.y + fftHeight + 10),
ImVec2(widgetPos.x + dataWidth + 50, widgetPos.y + fftHeight + 10),
IM_COL32(255, 255, 255, 255), 1.0f);
IM_COL32(255, 255, 255, 255), 1.0);
// Y Axis
window->DrawList->AddLine(ImVec2(widgetPos.x + 50, widgetPos.y + 9),
ImVec2(widgetPos.x + 50, widgetPos.y + fftHeight + 9),
IM_COL32(255, 255, 255, 255), 1.0f);
IM_COL32(255, 255, 255, 255), 1.0);
}
@ -199,7 +208,7 @@ namespace ImGui {
ImVec2 dragOrigin(mousePos.x - drag.x, mousePos.y - drag.y);
bool mouseHovered, mouseHeld;
bool mouseClicked = ImGui::ButtonBehavior(ImRect(fftAreaMin, fftAreaMax), ImGuiID("WaterfallID"), &mouseHovered, &mouseHeld,
bool mouseClicked = ImGui::ButtonBehavior(ImRect(fftAreaMin, fftAreaMax), GetID("WaterfallID"), &mouseHovered, &mouseHeld,
ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_PressedOnClick);
bool draging = ImGui::IsMouseDragging(ImGuiMouseButton_Left) && ImGui::IsWindowFocused();
@ -222,7 +231,10 @@ namespace ImGui {
}
int refCenter = mousePos.x - (widgetPos.x + 50);
if (refCenter >= 0 && refCenter < dataWidth && mousePos.y > widgetPos.y && mousePos.y < (widgetPos.y + widgetSize.y)) {
vfo->setOffset(((((float)refCenter / ((float)dataWidth / 2.0f)) - 1.0f) * (viewBandwidth / 2.0f)) + viewOffset);
double off = ((((double)refCenter / ((double)dataWidth / 2.0)) - 1.0) * (viewBandwidth / 2.0)) + viewOffset;
off += centerFreq;
off = (round(off / vfo->snapInterval) * vfo->snapInterval) - centerFreq;
vfo->setOffset(off);
}
}
@ -230,33 +242,36 @@ namespace ImGui {
if (draging && mouseInFFT) {
int refCenter = mousePos.x - (widgetPos.x + 50);
if (refCenter >= 0 && refCenter < dataWidth && mousePos.y > widgetPos.y && mousePos.y < (widgetPos.y + widgetSize.y)) {
vfo->setOffset(((((float)refCenter / ((float)dataWidth / 2.0f)) - 1.0f) * (viewBandwidth / 2.0f)) + viewOffset);
double off = ((((double)refCenter / ((double)dataWidth / 2.0)) - 1.0) * (viewBandwidth / 2.0)) + viewOffset;
off += centerFreq;
off = (round(off / vfo->snapInterval) * vfo->snapInterval) - centerFreq;
vfo->setOffset(off);
}
}
// Dragon frequency scale
// Draging frequency scale
if (draging && mouseInFreq) {
float deltax = drag.x - lastDrag;
double deltax = drag.x - lastDrag;
lastDrag = drag.x;
float viewDelta = deltax * (viewBandwidth / (float)dataWidth);
double viewDelta = deltax * (viewBandwidth / (double)dataWidth);
viewOffset -= viewDelta;
if (viewOffset + (viewBandwidth / 2.0f) > wholeBandwidth / 2.0f) {
float freqOffset = (viewOffset + (viewBandwidth / 2.0f)) - (wholeBandwidth / 2.0f);
viewOffset = (wholeBandwidth / 2.0f) - (viewBandwidth / 2.0f);
if (viewOffset + (viewBandwidth / 2.0) > wholeBandwidth / 2.0) {
double freqOffset = (viewOffset + (viewBandwidth / 2.0)) - (wholeBandwidth / 2.0);
viewOffset = (wholeBandwidth / 2.0) - (viewBandwidth / 2.0);
centerFreq += freqOffset;
centerFreqMoved = true;
}
if (viewOffset - (viewBandwidth / 2.0f) < -(wholeBandwidth / 2.0f)) {
float freqOffset = (viewOffset - (viewBandwidth / 2.0f)) + (wholeBandwidth / 2.0f);
viewOffset = (viewBandwidth / 2.0f) - (wholeBandwidth / 2.0f);
if (viewOffset - (viewBandwidth / 2.0) < -(wholeBandwidth / 2.0)) {
double freqOffset = (viewOffset - (viewBandwidth / 2.0)) + (wholeBandwidth / 2.0);
viewOffset = (viewBandwidth / 2.0) - (wholeBandwidth / 2.0);
centerFreq += freqOffset;
centerFreqMoved = true;
}
lowerFreq = (centerFreq + viewOffset) - (viewBandwidth / 2.0f);
upperFreq = (centerFreq + viewOffset) + (viewBandwidth / 2.0f);
lowerFreq = (centerFreq + viewOffset) - (viewBandwidth / 2.0);
upperFreq = (centerFreq + viewOffset) + (viewBandwidth / 2.0);
updateWaterfallFb();
}
else {
@ -268,7 +283,7 @@ namespace ImGui {
if (!waterfallVisible) {
return;
}
float offsetRatio = viewOffset / (wholeBandwidth / 2.0f);
double offsetRatio = viewOffset / (wholeBandwidth / 2.0);
int drawDataSize;
int drawDataStart;
int count = std::min<int>(waterfallHeight, rawFFTs.size());
@ -279,7 +294,7 @@ namespace ImGui {
for (int i = 0; i < count; i++) {
size = rawFFTs[i].size();
drawDataSize = (viewBandwidth / wholeBandwidth) * size;
drawDataStart = (((float)size / 2.0f) * (offsetRatio + 1)) - (drawDataSize / 2);
drawDataStart = (((double)size / 2.0) * (offsetRatio + 1)) - (drawDataSize / 2);
doZoom(drawDataStart, drawDataSize, dataWidth, rawFFTs[i], tempData);
for (int j = 0; j < dataWidth; j++) {
pixel = (std::clamp<float>(tempData[j], waterfallMin, waterfallMax) - waterfallMin) / dataRange;
@ -292,8 +307,8 @@ namespace ImGui {
void WaterFall::drawBandPlan() {
int count = bandplan->bands.size();
float horizScale = (float)dataWidth / viewBandwidth;
float start, end, center, aPos, bPos, cPos, width;
double horizScale = (double)dataWidth / viewBandwidth;
double start, end, center, aPos, bPos, cPos, width;
ImVec2 txtSz;
bool startVis, endVis;
uint32_t color, colorTrans;
@ -308,9 +323,9 @@ namespace ImGui {
}
startVis = (start > lowerFreq);
endVis = (end < upperFreq);
start = std::clamp<float>(start, lowerFreq, upperFreq);
end = std::clamp<float>(end, lowerFreq, upperFreq);
center = (start + end) / 2.0f;
start = std::clamp<double>(start, lowerFreq, upperFreq);
end = std::clamp<double>(end, lowerFreq, upperFreq);
center = (start + end) / 2.0;
aPos = widgetPos.x + 50 + ((start - lowerFreq) * horizScale);
bPos = widgetPos.x + 50 + ((end - lowerFreq) * horizScale);
cPos = widgetPos.x + 50 + ((center - lowerFreq) * horizScale);
@ -330,7 +345,7 @@ namespace ImGui {
if (bPos <= widgetPos.x + 50) {
bPos = widgetPos.x + 51;
}
if (width >= 1.0f) {
if (width >= 1.0) {
window->DrawList->AddRectFilled(ImVec2(roundf(aPos), widgetPos.y + fftHeight - 25),
ImVec2(roundf(bPos), widgetPos.y + fftHeight + 10), colorTrans);
if (startVis) {
@ -343,7 +358,7 @@ namespace ImGui {
}
}
if (txtSz.x <= width) {
window->DrawList->AddText(ImVec2(cPos - (txtSz.x / 2.0f), widgetPos.y + fftHeight - 17),
window->DrawList->AddText(ImVec2(cPos - (txtSz.x / 2.0), widgetPos.y + fftHeight - 17),
IM_COL32(255, 255, 255, 255), bandplan->bands[i].name.c_str());
}
}
@ -375,7 +390,7 @@ namespace ImGui {
else {
fftHeight = widgetSize.y - 50;
}
dataWidth = widgetSize.x - 60.0f;
dataWidth = widgetSize.x - 60.0;
delete[] latestFFT;
if (waterfallVisible) {
@ -388,7 +403,7 @@ namespace ImGui {
memset(waterfallFb, 0, dataWidth * waterfallHeight * sizeof(uint32_t));
}
for (int i = 0; i < dataWidth; i++) {
latestFFT[i] = -1000.0f; // Hide everything
latestFFT[i] = -1000.0; // Hide everything
}
fftAreaMin = ImVec2(widgetPos.x + 50, widgetPos.y + 9);
@ -401,7 +416,7 @@ namespace ImGui {
range = findBestRange(viewBandwidth, maxHSteps);
vRange = findBestRange(fftMax - fftMin, maxVSteps);
vRange = 10.0f;
vRange = 10.0;
updateWaterfallFb();
updateAllVFOs();
@ -432,7 +447,7 @@ namespace ImGui {
window->DrawList->AddRectFilled(widgetPos, widgetEndPos, IM_COL32( 0, 0, 0, 255 ));
window->DrawList->AddRect(widgetPos, widgetEndPos, IM_COL32( 50, 50, 50, 255 ));
window->DrawList->AddLine(ImVec2(widgetPos.x, widgetPos.y + fftHeight + 50), ImVec2(widgetPos.x + widgetSize.x, widgetPos.y + fftHeight + 50), IM_COL32(50, 50, 50, 255), 1.0f);
window->DrawList->AddLine(ImVec2(widgetPos.x, widgetPos.y + fftHeight + 50), ImVec2(widgetPos.x + widgetSize.x, widgetPos.y + fftHeight + 50), IM_COL32(50, 50, 50, 255), 1.0);
processInputs();
@ -441,7 +456,7 @@ namespace ImGui {
drawWaterfall();
}
drawVFOs();
if (bandplan != NULL) {
if (bandplan != NULL && bandplanVisible) {
drawBandPlan();
}
@ -480,9 +495,9 @@ namespace ImGui {
void WaterFall::pushFFT(std::vector<float> data, int n) {
buf_mtx.lock();
float offsetRatio = viewOffset / (wholeBandwidth / 2.0f);
double offsetRatio = viewOffset / (wholeBandwidth / 2.0);
int drawDataSize = (viewBandwidth / wholeBandwidth) * data.size();
int drawDataStart = (((float)data.size() / 2.0f) * (offsetRatio + 1)) - (drawDataSize / 2);
int drawDataStart = (((double)data.size() / 2.0) * (offsetRatio + 1)) - (drawDataSize / 2);
doZoom(drawDataStart, drawDataSize, dataWidth, data, latestFFT);
rawFFTs.insert(rawFFTs.begin(), data);
@ -512,9 +527,9 @@ namespace ImGui {
lowerId = std::clamp<int>(lowerId, 0, colorCount - 1);
upperId = std::clamp<int>(upperId, 0, colorCount - 1);
float ratio = (((float)i / (float)WATERFALL_RESOLUTION) * colorCount) - lowerId;
float r = (colors[lowerId][0] * (1.0f - ratio)) + (colors[upperId][0] * (ratio));
float g = (colors[lowerId][1] * (1.0f - ratio)) + (colors[upperId][1] * (ratio));
float b = (colors[lowerId][2] * (1.0f - ratio)) + (colors[upperId][2] * (ratio));
float r = (colors[lowerId][0] * (1.0 - ratio)) + (colors[upperId][0] * (ratio));
float g = (colors[lowerId][1] * (1.0 - ratio)) + (colors[upperId][1] * (ratio));
float b = (colors[lowerId][2] * (1.0 - ratio)) + (colors[upperId][2] * (ratio));
waterfallPallet[i] = ((uint32_t)255 << 24) | ((uint32_t)b << 16) | ((uint32_t)g << 8) | (uint32_t)r;
}
}
@ -534,70 +549,70 @@ namespace ImGui {
fftMax = max + 5;
}
void WaterFall::setCenterFrequency(float freq) {
void WaterFall::setCenterFrequency(double freq) {
centerFreq = freq;
lowerFreq = (centerFreq + viewOffset) - (viewBandwidth / 2.0f);
upperFreq = (centerFreq + viewOffset) + (viewBandwidth / 2.0f);
lowerFreq = (centerFreq + viewOffset) - (viewBandwidth / 2.0);
upperFreq = (centerFreq + viewOffset) + (viewBandwidth / 2.0);
updateAllVFOs();
}
float WaterFall::getCenterFrequency() {
double WaterFall::getCenterFrequency() {
return centerFreq;
}
void WaterFall::setBandwidth(float bandWidth) {
float currentRatio = viewBandwidth / wholeBandwidth;
void WaterFall::setBandwidth(double bandWidth) {
double currentRatio = viewBandwidth / wholeBandwidth;
wholeBandwidth = bandWidth;
setViewBandwidth(bandWidth * currentRatio);
updateAllVFOs();
}
float WaterFall::getBandwidth() {
double WaterFall::getBandwidth() {
return wholeBandwidth;
}
void WaterFall::setViewBandwidth(float bandWidth) {
void WaterFall::setViewBandwidth(double bandWidth) {
if (bandWidth == viewBandwidth) {
return;
}
if (abs(viewOffset) + (bandWidth / 2.0f) > wholeBandwidth / 2.0f) {
if (abs(viewOffset) + (bandWidth / 2.0) > wholeBandwidth / 2.0) {
if (viewOffset < 0) {
viewOffset = (bandWidth / 2.0f) - (wholeBandwidth / 2.0f);
viewOffset = (bandWidth / 2.0) - (wholeBandwidth / 2.0);
}
else {
viewOffset = (wholeBandwidth / 2.0f) - (bandWidth / 2.0f);
viewOffset = (wholeBandwidth / 2.0) - (bandWidth / 2.0);
}
}
viewBandwidth = bandWidth;
lowerFreq = (centerFreq + viewOffset) - (viewBandwidth / 2.0f);
upperFreq = (centerFreq + viewOffset) + (viewBandwidth / 2.0f);
lowerFreq = (centerFreq + viewOffset) - (viewBandwidth / 2.0);
upperFreq = (centerFreq + viewOffset) + (viewBandwidth / 2.0);
range = findBestRange(bandWidth, maxHSteps);
updateWaterfallFb();
updateAllVFOs();
}
float WaterFall::getViewBandwidth() {
double WaterFall::getViewBandwidth() {
return viewBandwidth;
}
void WaterFall::setViewOffset(float offset) {
void WaterFall::setViewOffset(double offset) {
if (offset == viewOffset) {
return;
}
if (offset - (viewBandwidth / 2.0f) < -(wholeBandwidth / 2.0f)) {
offset = (viewBandwidth / 2.0f) - (wholeBandwidth / 2.0f);
if (offset - (viewBandwidth / 2.0) < -(wholeBandwidth / 2.0)) {
offset = (viewBandwidth / 2.0) - (wholeBandwidth / 2.0);
}
if (offset + (viewBandwidth / 2.0f) > (wholeBandwidth / 2.0f)) {
offset = (wholeBandwidth / 2.0f) - (viewBandwidth / 2.0f);
if (offset + (viewBandwidth / 2.0) > (wholeBandwidth / 2.0)) {
offset = (wholeBandwidth / 2.0) - (viewBandwidth / 2.0);
}
viewOffset = offset;
lowerFreq = (centerFreq + viewOffset) - (viewBandwidth / 2.0f);
upperFreq = (centerFreq + viewOffset) + (viewBandwidth / 2.0f);
lowerFreq = (centerFreq + viewOffset) - (viewBandwidth / 2.0);
upperFreq = (centerFreq + viewOffset) + (viewBandwidth / 2.0);
updateWaterfallFb();
updateAllVFOs();
}
float WaterFall::getViewOffset() {
double WaterFall::getViewOffset() {
return viewOffset;
}
@ -649,21 +664,21 @@ namespace ImGui {
}
}
void WaterfallVFO::setOffset(float offset) {
void WaterfallVFO::setOffset(double offset) {
generalOffset = offset;
if (reference == REF_CENTER) {
centerOffset = offset;
lowerOffset = offset - (bandwidth / 2.0f);
upperOffset = offset + (bandwidth / 2.0f);
lowerOffset = offset - (bandwidth / 2.0);
upperOffset = offset + (bandwidth / 2.0);
}
else if (reference == REF_LOWER) {
lowerOffset = offset;
centerOffset = offset + (bandwidth / 2.0f);
centerOffset = offset + (bandwidth / 2.0);
upperOffset = offset + bandwidth;
}
else if (reference == REF_UPPER) {
upperOffset = offset;
centerOffset = offset - (bandwidth / 2.0f);
centerOffset = offset - (bandwidth / 2.0);
lowerOffset = offset - bandwidth;
}
centerOffsetChanged = true;
@ -672,41 +687,41 @@ namespace ImGui {
redrawRequired = true;
}
void WaterfallVFO::setCenterOffset(float offset) {
void WaterfallVFO::setCenterOffset(double offset) {
if (reference == REF_CENTER) {
generalOffset = offset;
}
else if (reference == REF_LOWER) {
generalOffset = offset - (bandwidth / 2.0f);
generalOffset = offset - (bandwidth / 2.0);
}
else if (reference == REF_UPPER) {
generalOffset = offset + (bandwidth / 2.0f);
generalOffset = offset + (bandwidth / 2.0);
}
centerOffset = offset;
lowerOffset = offset - (bandwidth / 2.0f);
upperOffset = offset + (bandwidth / 2.0f);
lowerOffset = offset - (bandwidth / 2.0);
upperOffset = offset + (bandwidth / 2.0);
centerOffsetChanged = true;
upperOffsetChanged = true;
lowerOffsetChanged = true;
redrawRequired = true;
}
void WaterfallVFO::setBandwidth(float bw) {
void WaterfallVFO::setBandwidth(double bw) {
if (bandwidth == bw || bw < 0) {
return;
}
bandwidth = bw;
if (reference == REF_CENTER) {
lowerOffset = centerOffset - (bandwidth / 2.0f);
upperOffset = centerOffset + (bandwidth / 2.0f);
lowerOffset = centerOffset - (bandwidth / 2.0);
upperOffset = centerOffset + (bandwidth / 2.0);
}
else if (reference == REF_LOWER) {
centerOffset = lowerOffset + (bandwidth / 2.0f);
centerOffset = lowerOffset + (bandwidth / 2.0);
upperOffset = lowerOffset + bandwidth;
centerOffsetChanged = true;
}
else if (reference == REF_UPPER) {
centerOffset = upperOffset - (bandwidth / 2.0f);
centerOffset = upperOffset - (bandwidth / 2.0);
lowerOffset = upperOffset - bandwidth;
centerOffsetChanged = true;
}
@ -722,11 +737,11 @@ namespace ImGui {
}
void WaterfallVFO::updateDrawingVars(float viewBandwidth, float dataWidth, float viewOffset, ImVec2 widgetPos, int fftHeight) {
float width = (bandwidth / viewBandwidth) * (float)dataWidth;
int center = roundf((((centerOffset - viewOffset) / (viewBandwidth / 2.0f)) + 1.0f) * ((float)dataWidth / 2.0f));
int left = roundf((((lowerOffset - viewOffset) / (viewBandwidth / 2.0f)) + 1.0f) * ((float)dataWidth / 2.0f));
int right = roundf((((upperOffset - viewOffset) / (viewBandwidth / 2.0f)) + 1.0f) * ((float)dataWidth / 2.0f));
void WaterfallVFO::updateDrawingVars(double viewBandwidth, float dataWidth, double viewOffset, ImVec2 widgetPos, int fftHeight) {
double width = (bandwidth / viewBandwidth) * (double)dataWidth;
int center = roundf((((centerOffset - viewOffset) / (viewBandwidth / 2.0)) + 1.0) * ((double)dataWidth / 2.0));
int left = roundf((((lowerOffset - viewOffset) / (viewBandwidth / 2.0)) + 1.0) * ((double)dataWidth / 2.0));
int right = roundf((((upperOffset - viewOffset) / (viewBandwidth / 2.0)) + 1.0) * ((double)dataWidth / 2.0));
if (left >= 0 && left < dataWidth && reference == REF_LOWER) {
lineMin = ImVec2(widgetPos.x + 50 + left, widgetPos.y + 9);
@ -780,5 +795,17 @@ namespace ImGui {
int WaterFall::getFFTHeight() {
return FFTAreaHeight;
}
void WaterFall::showBandplan() {
bandplanVisible = true;
}
void WaterFall::hideBandplan() {
bandplanVisible = false;
}
void WaterfallVFO::setSnapInterval(double interval) {
snapInterval = interval;
}
};

View File

@ -1,12 +1,10 @@
#pragma once
#include <imgui.h>
#include <imgui_internal.h>
#include <vector>
#include <mutex>
#include <gui/bandplan.h>
#include <imgui/imgui.h>
#include <imgui/imgui_internal.h>
#include <GL/glew.h>
#include <imutils.h>
#include <bandplan.h>
#include <algorithm>
#define WATERFALL_RESOLUTION 1000000
@ -14,11 +12,12 @@ namespace ImGui {
class WaterfallVFO {
public:
void setOffset(float offset);
void setCenterOffset(float offset);
void setBandwidth(float bw);
void setOffset(double offset);
void setCenterOffset(double offset);
void setBandwidth(double bw);
void setReference(int ref);
void updateDrawingVars(float viewBandwidth, float dataWidth, float viewOffset, ImVec2 widgetPos, int fftHeight);
void setSnapInterval(double interval);
void updateDrawingVars(double viewBandwidth, float dataWidth, double viewOffset, ImVec2 widgetPos, int fftHeight); // NOTE: Datawidth double???
void draw(ImGuiWindow* window, bool selected);
enum {
@ -28,12 +27,12 @@ namespace ImGui {
_REF_COUNT
};
float generalOffset;
float centerOffset;
float lowerOffset;
float upperOffset;
float bandwidth;
float snapInterval;
double generalOffset;
double centerOffset;
double lowerOffset;
double upperOffset;
double bandwidth;
double snapInterval = 5000;
int reference = REF_CENTER;
ImVec2 rectMin;
@ -52,22 +51,24 @@ namespace ImGui {
public:
WaterFall();
void init();
void draw();
void pushFFT(std::vector<float> data, int n);
void updatePallette(float colors[][3], int colorCount);
void setCenterFrequency(float freq);
float getCenterFrequency();
void setCenterFrequency(double freq);
double getCenterFrequency();
void setBandwidth(float bandWidth);
float getBandwidth();
void setBandwidth(double bandWidth);
double getBandwidth();
void setViewBandwidth(float bandWidth);
float getViewBandwidth();
void setViewBandwidth(double bandWidth);
double getViewBandwidth();
void setViewOffset(float offset);
float getViewOffset();
void setViewOffset(double offset);
double getViewOffset();
void setFFTMin(float min);
float getFFTMin();
@ -81,8 +82,8 @@ namespace ImGui {
void setWaterfallMax(float max);
float getWaterfallMax();
void setZoom(float zoomLevel);
void setOffset(float zoomOffset);
void setZoom(double zoomLevel);
void setOffset(double zoomOffset);
void autoRange();
@ -91,6 +92,9 @@ namespace ImGui {
void showWaterfall();
void hideWaterfall();
void showBandplan();
void hideBandplan();
void setFFTHeight(int height);
int getFFTHeight();
@ -156,20 +160,20 @@ namespace ImGui {
int fftHeight; // Height of the fft graph
int waterfallHeight; // Height of the waterfall
float viewBandwidth;
float viewOffset;
double viewBandwidth;
double viewOffset;
float lowerFreq;
float upperFreq;
float range;
double lowerFreq;
double upperFreq;
double range;
float lastDrag;
int vfoRef = REF_CENTER;
// Absolute values
float centerFreq;
float wholeBandwidth;
double centerFreq;
double wholeBandwidth;
// Ranges
float fftMin;
@ -187,5 +191,6 @@ namespace ImGui {
int newFFTAreaHeight;
bool waterfallVisible = true;
bool bandplanVisible = false;
};
};

View File

@ -19,7 +19,7 @@ namespace io {
std::string name;
int index;
int channels;
std::vector<float> sampleRates;
std::vector<double> sampleRates;
std::string txtSampleRates;
};
@ -323,7 +323,7 @@ namespace io {
return 0;
}
float POSSIBLE_SAMP_RATE[6] = {
double POSSIBLE_SAMP_RATE[6] = {
48000.0f,
44100.0f,
24000.0f,
@ -336,7 +336,7 @@ namespace io {
int devIndex;
int devListIndex;
int defaultDev;
float _sampleRate;
double _sampleRate;
int _bufferSize;
dsp::stream<float>* _monoInput;
dsp::stream<dsp::StereoFloat_t>* _stereoInput;

105
core/src/module.cpp Normal file
View File

@ -0,0 +1,105 @@
#include <module.h>
#include <filesystem>
#include <spdlog/spdlog.h>
#include <json.hpp>
#include <fstream>
using nlohmann::json;
namespace mod {
std::map<std::string, Module_t> modules;
std::vector<std::string> moduleNames;
void loadModule(std::string path, std::string name) {
if (!std::filesystem::exists(path)) {
spdlog::error("{0} does not exist", path);
return;
}
if (!std::filesystem::is_regular_file(path)) {
spdlog::error("{0} isn't a loadable module", path);
return;
}
Module_t mod;
#ifdef _WIN32
mod.inst = LoadLibraryA(path.c_str());
if (mod.inst == NULL) {
spdlog::error("Couldn't load {0}.", name);
return;
}
mod._INIT_ = (void(*)())GetProcAddress(mod.inst, "_INIT_");
mod._CREATE_INSTANCE_ = (void*(*)(std::string))GetProcAddress(mod.inst, "_CREATE_INSTANCE_");
mod._DELETE_INSTANCE_ = (void(*)(void*))GetProcAddress(mod.inst, "_DELETE_INSTANCE_");
mod._STOP_ = (void(*)())GetProcAddress(mod.inst, "_STOP_");
#else
mod.inst = dlopen(path.c_str(), RTLD_LAZY);
if (mod.inst == NULL) {
spdlog::error("Couldn't load {0}.", name);
return;
}
mod._INIT_ = (void(*)())dlsym(mod.inst, "_INIT_");
mod._CREATE_INSTANCE_ = (void*(*)(std::string))dlsym(mod.inst, "_CREATE_INSTANCE_");
mod._DELETE_INSTANCE_ = (void(*)(void*))dlsym(mod.inst, "_DELETE_INSTANCE_");
mod._STOP_ = (void(*)())dlsym(mod.inst, "_STOP_");
#endif
if (mod._INIT_ == NULL) {
spdlog::error("Couldn't load {0} because it's missing _INIT_.", name);
return;
}
if (mod._CREATE_INSTANCE_ == NULL) {
spdlog::error("Couldn't load {0} because it's missing _CREATE_INSTANCE_.", name);
return;
}
if (mod._DELETE_INSTANCE_ == NULL) {
spdlog::error("Couldn't load {0} because it's missing _DELETE_INSTANCE_.", name);
return;
}
if (mod._STOP_ == NULL) {
spdlog::error("Couldn't load {0} because it's missing _STOP_.", name);
return;
}
if (!isLoaded(mod.inst)) {
mod._INIT_();
}
mod.ctx = mod._CREATE_INSTANCE_(name);
if (mod.ctx == NULL) {
spdlog::error("{0} Failed to initialize.", name);
}
modules[name] = mod;
moduleNames.push_back(name);
}
void loadFromList(std::string path) {
if (!std::filesystem::exists(path)) {
spdlog::error("Module list file does not exist");
return;
}
if (!std::filesystem::is_regular_file(path)) {
spdlog::error("Module list file isn't a file...");
return;
}
std::ifstream file(path.c_str());
json data;
file >> data;
file.close();
std::map<std::string, std::string> list = data.get<std::map<std::string, std::string>>();
for (auto const& [name, file] : list) {
spdlog::info("Loading {0} ({1})", name, file);
loadModule(file, name);
}
}
bool isLoaded(void* handle) {
for (auto const& [name, module] : modules) {
if (module.inst == handle) {
return true;
}
}
return false;
}
};

54
core/src/module.h Normal file
View File

@ -0,0 +1,54 @@
#pragma once
#include <string>
#include <map>
#include <json.hpp>
#ifdef _WIN32
#ifdef SDRPP_IS_CORE
#define SDRPP_EXPORT extern "C" __declspec(dllexport)
#else
#define SDRPP_EXPORT extern "C" __declspec(dllimport)
#endif
#else
#define SDRPP_EXPORT extern
#endif
#ifdef _WIN32
#include <Windows.h>
#define MOD_EXPORT extern "C" __declspec(dllexport)
#else
#include <dlfcn.h>
#define MOD_EXPORT extern "C"
#endif
namespace mod {
struct Module_t {
#ifdef _WIN32
HINSTANCE inst;
#else
void* inst;
#endif
void (*_INIT_)();
void* (*_CREATE_INSTANCE_)(std::string name);
void (*_DELETE_INSTANCE_)(void* instance);
void (*_STOP_)();
void* ctx;
};
struct ModuleInfo_t {
const char* name;
const char* description;
const char* author;
const char* version;
};
void loadModule(std::string path, std::string name);
void loadFromList(std::string path);
bool isLoaded(void* handle);
extern std::map<std::string, Module_t> modules;
extern std::vector<std::string> moduleNames;
};
#define MOD_INFO MOD_EXPORT const mod::ModuleInfo_t _INFO

93
core/src/scripting.cpp Normal file
View File

@ -0,0 +1,93 @@
#include <scripting.h>
#include <duktape/duk_console.h>
#include <version.h>
#include <fstream>
#include <sstream>
#include <signal_path/signal_path.h>
#include <imgui/imgui.h>
ScriptManager::ScriptManager() {
}
ScriptManager::Script* ScriptManager::createScript(std::string name, std::string path) {
ScriptManager::Script* script = new ScriptManager::Script(this, name, path);
scripts[name] = script;
return script;
}
ScriptManager::Script::Script(ScriptManager* man, std::string name, std::string path) {
this->name = name;
manager = man;
std::ifstream file(path, std::ios::in);
std::stringstream ss;
ss << file.rdbuf();
code = ss.str();
}
void ScriptManager::Script::run() {
ctx = ScriptManager::createContext(manager, name);
running = true;
if (worker.joinable()) {
worker.join();
}
worker = std::thread(scriptWorker, this);
}
duk_context* ScriptManager::createContext(ScriptManager* _this, std::string name) {
duk_context* ctx = duk_create_heap_default();
duk_console_init(ctx, DUK_CONSOLE_PROXY_WRAPPER);
duk_push_string(ctx, name.c_str());
duk_put_global_string(ctx, "SCRIPT_NAME");
duk_idx_t sdrppBase = duk_push_object(ctx);
// API
duk_push_string(ctx, VERSION_STR);
duk_put_prop_string(ctx, sdrppBase, "version");
duk_push_c_function(ctx, ScriptManager::duk_setSource, 1);
duk_put_prop_string(ctx, sdrppBase, "selectSource");
// Modules
duk_idx_t modObjId = duk_push_object(ctx);
for (const auto& [name, handler] : _this->handlers) {
duk_idx_t objId = duk_push_object(ctx);
handler.handler(handler.ctx, ctx, objId);
duk_put_prop_string(ctx, modObjId, name.c_str());
}
duk_put_prop_string(ctx, sdrppBase, "modules");
duk_put_global_string(ctx, "sdrpp");
return ctx;
}
// TODO: Switch to spdlog
void ScriptManager::Script::scriptWorker(Script* script) {
if (duk_peval_string(script->ctx, script->code.c_str()) != 0) {
printf("Error: %s\n", duk_safe_to_string(script->ctx, -1));
//return;
}
duk_destroy_heap(script->ctx);
script->running = false;
}
void ScriptManager::bindScriptRunHandler(std::string name, ScriptManager::ScriptRunHandler_t handler) {
// TODO: check if it exists and add a "unbind" function
handlers[name] = handler;
}
duk_ret_t ScriptManager::duk_setSource(duk_context* dukCtx) {
const char* name = duk_require_string(dukCtx, -1);
sigpath::sourceManager.selectSource(name);
duk_pop_n(dukCtx, 1); // Pop demod name, this and context
return 0;
}

51
core/src/scripting.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include <string>
#include <thread>
#include <vector>
#include <map>
#include <duktape/duktape.h>
class ScriptManager {
public:
ScriptManager();
friend class Script;
class Script {
public:
Script(ScriptManager* man, std::string name, std::string path);
void run();
bool running = false;
private:
static void scriptWorker(Script* _this);
duk_context* ctx;
std::thread worker;
std::string code;
std::string name;
ScriptManager* manager;
};
struct ScriptRunHandler_t {
void (*handler)(void* ctx, duk_context* dukCtx, duk_idx_t objId);
void* ctx;
};
void bindScriptRunHandler(std::string name, ScriptManager::ScriptRunHandler_t handler);
ScriptManager::Script* createScript(std::string name, std::string path);
std::map<std::string, ScriptManager::Script*> scripts;
private:
static duk_context* createContext(ScriptManager* _this, std::string name);
// API
static duk_ret_t duk_setSource(duk_context* ctx);
std::map<std::string, ScriptManager::ScriptRunHandler_t> handlers;
};

View File

@ -1,16 +1,16 @@
#include <audio.h>
#include <signal_path/audio.h>
namespace audio {
std::map<std::string, AudioStream_t*> streams;
float registerMonoStream(dsp::stream<float>* stream, std::string name, std::string vfoName, int (*sampleRateChangeHandler)(void* ctx, float sampleRate), void* ctx) {
double registerMonoStream(dsp::stream<float>* stream, std::string name, std::string vfoName, int (*sampleRateChangeHandler)(void* ctx, double sampleRate), void* ctx) {
AudioStream_t* astr = new AudioStream_t;
astr->type = STREAM_TYPE_MONO;
astr->ctx = ctx;
astr->audio = new io::AudioSink;
astr->audio->init(1);
astr->deviceId = astr->audio->getDeviceId();
float sampleRate = astr->audio->devices[astr->deviceId].sampleRates[0];
double sampleRate = astr->audio->devices[astr->deviceId].sampleRates[0];
int blockSize = sampleRate / 200; // default block size
astr->monoAudioStream = new dsp::stream<float>(blockSize * 2);
astr->audio->setBlockSize(blockSize);
@ -31,13 +31,13 @@ namespace audio {
return sampleRate;
}
float registerStereoStream(dsp::stream<dsp::StereoFloat_t>* stream, std::string name, std::string vfoName, int (*sampleRateChangeHandler)(void* ctx, float sampleRate), void* ctx) {
double registerStereoStream(dsp::stream<dsp::StereoFloat_t>* stream, std::string name, std::string vfoName, int (*sampleRateChangeHandler)(void* ctx, double sampleRate), void* ctx) {
AudioStream_t* astr = new AudioStream_t;
astr->type = STREAM_TYPE_STEREO;
astr->ctx = ctx;
astr->audio = new io::AudioSink;
astr->audio->init(1);
float sampleRate = astr->audio->devices[astr->audio->getDeviceId()].sampleRates[0];
double sampleRate = astr->audio->devices[astr->audio->getDeviceId()].sampleRates[0];
int blockSize = sampleRate / 200; // default block size
astr->stereoAudioStream = new dsp::stream<dsp::StereoFloat_t>(blockSize * 2);
astr->audio->setBlockSize(blockSize);
@ -95,7 +95,7 @@ namespace audio {
delete astr->monoDynSplit;
}
dsp::stream<float>* bindToStreamMono(std::string name, void (*streamRemovedHandler)(void* ctx), void (*sampleRateChangeHandler)(void* ctx, float sampleRate, int blockSize), void* ctx) {
dsp::stream<float>* bindToStreamMono(std::string name, void (*streamRemovedHandler)(void* ctx), void (*sampleRateChangeHandler)(void* ctx, double sampleRate, int blockSize), void* ctx) {
AudioStream_t* astr = streams[name];
BoundStream_t bstr;
bstr.type = STREAM_TYPE_MONO;
@ -125,7 +125,7 @@ namespace audio {
return bstr.monoStream;
}
dsp::stream<dsp::StereoFloat_t>* bindToStreamStereo(std::string name, void (*streamRemovedHandler)(void* ctx), void (*sampleRateChangeHandler)(void* ctx, float sampleRate, int blockSize), void* ctx) {
dsp::stream<dsp::StereoFloat_t>* bindToStreamStereo(std::string name, void (*streamRemovedHandler)(void* ctx), void (*sampleRateChangeHandler)(void* ctx, double sampleRate, int blockSize), void* ctx) {
AudioStream_t* astr = streams[name];
BoundStream_t bstr;
bstr.type = STREAM_TYPE_STEREO;
@ -250,7 +250,7 @@ namespace audio {
return "";
}
void setSampleRate(std::string name, float sampleRate) {
void setSampleRate(std::string name, double sampleRate) {
AudioStream_t* astr = streams[name];
if (astr->running) {
return;
@ -288,7 +288,7 @@ namespace audio {
}
}
void setAudioDevice(std::string name, int deviceId, float sampleRate) {
void setAudioDevice(std::string name, int deviceId, double sampleRate) {
AudioStream_t* astr = streams[name];
if (astr->running) {
return;

View File

@ -3,7 +3,6 @@
#include <dsp/routing.h>
#include <io/audio.h>
#include <map>
#include <watcher.h>
namespace audio {
enum {
@ -12,13 +11,15 @@ namespace audio {
_STREAM_TYPE_COUNT
};
// TODO: Create a manager class and an instance class
struct BoundStream_t {
dsp::stream<float>* monoStream;
dsp::stream<dsp::StereoFloat_t>* stereoStream;
dsp::StereoToMono* s2m;
dsp::MonoToStereo* m2s;
void (*streamRemovedHandler)(void* ctx);
void (*sampleRateChangeHandler)(void* ctx, float sampleRate, int blockSize);
void (*sampleRateChangeHandler)(void* ctx, double sampleRate, int blockSize);
void* ctx;
int type;
};
@ -32,8 +33,8 @@ namespace audio {
dsp::DynamicSplitter<float>* monoDynSplit;
dsp::stream<dsp::StereoFloat_t>* stereoStream;
dsp::DynamicSplitter<dsp::StereoFloat_t>* stereoDynSplit;
int (*sampleRateChangeHandler)(void* ctx, float sampleRate);
float sampleRate;
int (*sampleRateChangeHandler)(void* ctx, double sampleRate);
double sampleRate;
int blockSize;
int type;
bool running = false;
@ -46,19 +47,19 @@ namespace audio {
extern std::map<std::string, AudioStream_t*> streams;
float registerMonoStream(dsp::stream<float>* stream, std::string name, std::string vfoName, int (*sampleRateChangeHandler)(void* ctx, float sampleRate), void* ctx);
float registerStereoStream(dsp::stream<dsp::StereoFloat_t>* stream, std::string name, std::string vfoName, int (*sampleRateChangeHandler)(void* ctx, float sampleRate), void* ctx);
double registerMonoStream(dsp::stream<float>* stream, std::string name, std::string vfoName, int (*sampleRateChangeHandler)(void* ctx, double sampleRate), void* ctx);
double registerStereoStream(dsp::stream<dsp::StereoFloat_t>* stream, std::string name, std::string vfoName, int (*sampleRateChangeHandler)(void* ctx, double sampleRate), void* ctx);
void startStream(std::string name);
void stopStream(std::string name);
void removeStream(std::string name);
dsp::stream<float>* bindToStreamMono(std::string name, void (*streamRemovedHandler)(void* ctx), void (*sampleRateChangeHandler)(void* ctx, float sampleRate, int blockSize), void* ctx);
dsp::stream<dsp::StereoFloat_t>* bindToStreamStereo(std::string name, void (*streamRemovedHandler)(void* ctx), void (*sampleRateChangeHandler)(void* ctx, float sampleRate, int blockSize), void* ctx);
dsp::stream<float>* bindToStreamMono(std::string name, void (*streamRemovedHandler)(void* ctx), void (*sampleRateChangeHandler)(void* ctx, double sampleRate, int blockSize), void* ctx);
dsp::stream<dsp::StereoFloat_t>* bindToStreamStereo(std::string name, void (*streamRemovedHandler)(void* ctx), void (*sampleRateChangeHandler)(void* ctx, double sampleRate, int blockSize), void* ctx);
void setBlockSize(std::string name, int blockSize);
void unbindFromStreamMono(std::string name, dsp::stream<float>* stream);
void unbindFromStreamStereo(std::string name, dsp::stream<dsp::StereoFloat_t>* stream);
std::string getNameFromVFO(std::string vfoName);
void setSampleRate(std::string name, float sampleRate);
void setAudioDevice(std::string name, int deviceId, float sampleRate);
void setSampleRate(std::string name, double sampleRate);
void setAudioDevice(std::string name, int deviceId, double sampleRate);
std::vector<std::string> getStreamNameList();
};

View File

@ -1,4 +1,4 @@
#include <signal_path.h>
#include <signal_path/dsp.h>
SignalPath::SignalPath() {
@ -20,7 +20,7 @@ void SignalPath::init(uint64_t sampleRate, int fftRate, int fftSize, dsp::stream
dynSplit.init(&split.output_b, 32000);
}
void SignalPath::setSampleRate(float sampleRate) {
void SignalPath::setSampleRate(double sampleRate) {
this->sampleRate = sampleRate;
inputBlockSize = sampleRate / 200.0f;
@ -40,7 +40,7 @@ void SignalPath::setSampleRate(float sampleRate) {
fftBlockDec.setSkip(skip);
dynSplit.setBlockSize(inputBlockSize);
mod::broadcastEvent(mod::EVENT_STREAM_PARAM_CHANGED);
// TODO: Tell modules that the block size has changed
for (auto const& [name, vfo] : vfos) {
vfo.vfo->setInputSampleRate(sampleRate, inputBlockSize);
@ -68,7 +68,7 @@ void SignalPath::setDCBiasCorrection(bool enabled) {
dcBiasRemover.bypass = !enabled;
}
dsp::VFO* SignalPath::addVFO(std::string name, float outSampleRate, float bandwidth, float offset) {
dsp::VFO* SignalPath::addVFO(std::string name, double outSampleRate, double bandwidth, double offset) {
if (vfos.find(name) != vfos.end()) {
return NULL;
}
@ -87,6 +87,7 @@ dsp::VFO* SignalPath::addVFO(std::string name, float outSampleRate, float bandwi
void SignalPath::removeVFO(std::string name) {
if (vfos.find(name) == vfos.end()) {
return;
}
dynSplit.stop();
VFO_t vfo = vfos[name];
@ -96,4 +97,10 @@ void SignalPath::removeVFO(std::string name) {
delete vfo.inputStream;
dynSplit.start();
vfos.erase(name);
}
void SignalPath::setInput(dsp::stream<dsp::complex_t>* input) {
dcBiasRemover.stop();
dcBiasRemover.setInput(input);
dcBiasRemover.start();
}

View File

@ -8,7 +8,6 @@
#include <dsp/sink.h>
#include <dsp/correction.h>
#include <dsp/vfo.h>
#include <io/audio.h>
#include <map>
#include <module.h>
@ -17,11 +16,12 @@ public:
SignalPath();
void init(uint64_t sampleRate, int fftRate, int fftSize, dsp::stream<dsp::complex_t>* input, dsp::complex_t* fftBuffer, void fftHandler(dsp::complex_t*));
void start();
void setSampleRate(float sampleRate);
void setSampleRate(double sampleRate);
void setDCBiasCorrection(bool enabled);
void setFFTRate(float rate);
dsp::VFO* addVFO(std::string name, float outSampleRate, float bandwidth, float offset);
void setFFTRate(double rate);
dsp::VFO* addVFO(std::string name, double outSampleRate, double bandwidth, double offset);
void removeVFO(std::string name);
void setInput(dsp::stream<dsp::complex_t>* input);
private:
struct VFO_t {
@ -40,8 +40,8 @@ private:
dsp::DynamicSplitter<dsp::complex_t> dynSplit;
std::map<std::string, VFO_t> vfos;
float sampleRate;
float fftRate;
double sampleRate;
double fftRate;
int fftSize;
int inputBlockSize;
};

View File

@ -0,0 +1,7 @@
#include <signal_path/signal_path.h>
namespace sigpath {
SignalPath signalPath;
VFOManager vfoManager;
SourceManager sourceManager;
};

View File

@ -0,0 +1,11 @@
#pragma once
#include <signal_path/dsp.h>
#include <signal_path/vfo_manager.h>
#include <signal_path/source.h>
#include <module.h>
namespace sigpath {
SDRPP_EXPORT SignalPath signalPath;
SDRPP_EXPORT VFOManager vfoManager;
SDRPP_EXPORT SourceManager sourceManager;
};

View File

@ -0,0 +1,64 @@
#include <signal_path/source.h>
#include <spdlog/spdlog.h>
#include <signal_path/signal_path.h>
SourceManager::SourceManager() {
}
void SourceManager::registerSource(std::string name, SourceHandler* handler) {
if (sources.find(name) != sources.end()) {
spdlog::error("Tried to register new source with existing name: {0}", name);
return;
}
sources[name] = handler;
sourceNames.push_back(name);
}
void SourceManager::selectSource(std::string name) {
if (sources.find(name) == sources.end()) {
spdlog::error("Tried to select non existant source: {0}", name);
return;
}
if (selectedName != "") {
sources[selectedName]->deselectHandler(sources[selectedName]->ctx);
}
selectedHandler = sources[name];
selectedHandler->selectHandler(selectedHandler->ctx);
selectedName = name;
sigpath::signalPath.setInput(selectedHandler->stream);
}
void SourceManager::showSelectedMenu() {
if (selectedHandler == NULL) {
return;
}
selectedHandler->menuHandler(selectedHandler->ctx);
}
void SourceManager::start() {
if (selectedHandler == NULL) {
return;
}
selectedHandler->startHandler(selectedHandler->ctx);
}
void SourceManager::stop() {
if (selectedHandler == NULL) {
return;
}
selectedHandler->stopHandler(selectedHandler->ctx);
}
void SourceManager::tune(double freq) {
if (selectedHandler == NULL) {
return;
}
selectedHandler->tuneHandler(freq + tuneOffset, selectedHandler->ctx);
currentFreq = freq;
}
void SourceManager::setTuningOffset(double offset) {
tuneOffset = offset;
tune(currentFreq);
}

View File

@ -0,0 +1,40 @@
#pragma once
#include <string>
#include <vector>
#include <map>
#include <dsp/stream.h>
#include <dsp/types.h>
class SourceManager {
public:
SourceManager();
struct SourceHandler {
dsp::stream<dsp::complex_t>* stream;
void (*menuHandler)(void* ctx);
void (*selectHandler)(void* ctx);
void (*deselectHandler)(void* ctx);
void (*startHandler)(void* ctx);
void (*stopHandler)(void* ctx);
void (*tuneHandler)(double freq, void* ctx);
void* ctx;
};
void registerSource(std::string name, SourceHandler* handler);
void selectSource(std::string name);
void showSelectedMenu();
void start();
void stop();
void tune(double freq);
void setTuningOffset(double offset);
std::vector<std::string> sourceNames;
private:
std::map<std::string, SourceHandler*> sources;
std::string selectedName;
SourceHandler* selectedHandler = NULL;
double tuneOffset;
double currentFreq;
};

View File

@ -0,0 +1,130 @@
#include <signal_path/vfo_manager.h>
#include <signal_path/signal_path.h>
VFOManager::VFO::VFO(std::string name, int reference, double offset, double bandwidth, double sampleRate, int blockSize) {
this->name = name;
dspVFO = sigpath::signalPath.addVFO(name, sampleRate, bandwidth, offset);
wtfVFO = new ImGui::WaterfallVFO;
wtfVFO->setReference(reference);
wtfVFO->setBandwidth(bandwidth);
wtfVFO->setOffset(offset);
output = dspVFO->output;
gui::waterfall.vfos[name] = wtfVFO;
}
VFOManager::VFO::~VFO() {
gui::waterfall.vfos.erase(name);
sigpath::signalPath.removeVFO(name);
delete wtfVFO;
}
void VFOManager::VFO::setOffset(double offset) {
wtfVFO->setOffset(offset);
dspVFO->setOffset(wtfVFO->centerOffset);
}
void VFOManager::VFO::setCenterOffset(double offset) {
wtfVFO->setCenterOffset(offset);
dspVFO->setOffset(offset);
}
void VFOManager::VFO::setBandwidth(double bandwidth) {
wtfVFO->setBandwidth(bandwidth);
dspVFO->setBandwidth(bandwidth);
}
void VFOManager::VFO::setSampleRate(double sampleRate, double bandwidth) {
dspVFO->setOutputSampleRate(sampleRate, bandwidth);
wtfVFO->setBandwidth(bandwidth);
}
void VFOManager::VFO::setReference(int ref) {
wtfVFO->setReference(ref);
}
int VFOManager::VFO::getOutputBlockSize() {
return dspVFO->getOutputBlockSize();
}
void VFOManager::VFO::setSnapInterval(double interval) {
wtfVFO->setSnapInterval(interval);
}
VFOManager::VFOManager() {
}
VFOManager::VFO* VFOManager::createVFO(std::string name, int reference, double offset, double bandwidth, double sampleRate, int blockSize) {
if (vfos.find(name) != vfos.end() || name == "") {
return NULL;
}
VFOManager::VFO* vfo = new VFO(name, reference, offset, bandwidth, sampleRate, blockSize);
vfos[name] = vfo;
return vfo;
}
void VFOManager::deleteVFO(VFOManager::VFO* vfo) {
std::string name = "";
for (auto const& [_name, _vfo] : vfos) {
if (_vfo == vfo) {
name = _name;
break;
}
}
if (name == "") {
return;
}
vfos.erase(name);
}
void VFOManager::setOffset(std::string name, double offset) {
if (vfos.find(name) == vfos.end()) {
return;
}
vfos[name]->setOffset(offset);
}
void VFOManager::setCenterOffset(std::string name, double offset) {
if (vfos.find(name) == vfos.end()) {
return;
}
vfos[name]->setCenterOffset(offset);
}
void VFOManager::setBandwidth(std::string name, double bandwidth) {
if (vfos.find(name) == vfos.end()) {
return;
}
vfos[name]->setBandwidth(bandwidth);
}
void VFOManager::setSampleRate(std::string name, double sampleRate, double bandwidth) {
if (vfos.find(name) == vfos.end()) {
return;
}
vfos[name]->setSampleRate(sampleRate, bandwidth);
}
void VFOManager::setReference(std::string name, int ref) {
if (vfos.find(name) == vfos.end()) {
return;
}
vfos[name]->setReference(ref);
}
int VFOManager::getOutputBlockSize(std::string name) {
if (vfos.find(name) == vfos.end()) {
return -1;
}
return vfos[name]->getOutputBlockSize();
}
void VFOManager::updateFromWaterfall(ImGui::WaterFall* wtf) {
for (auto const& [name, vfo] : vfos) {
if (vfo->wtfVFO->centerOffsetChanged) {
vfo->wtfVFO->centerOffsetChanged = false;
vfo->dspVFO->setOffset(vfo->wtfVFO->centerOffset);
}
}
}

View File

@ -0,0 +1,48 @@
#pragma once
#include <dsp/vfo.h>
#include <gui/waterfall.h>
#include <gui/gui.h>
class VFOManager {
public:
VFOManager();
class VFO {
public:
VFO(std::string name, int reference, double offset, double bandwidth, double sampleRate, int blockSize);
~VFO();
void setOffset(double offset);
void setCenterOffset(double offset);
void setBandwidth(double bandwidth);
void setSampleRate(double sampleRate, double bandwidth);
void setReference(int ref);
int getOutputBlockSize();
void setSnapInterval(double interval);
dsp::stream<dsp::complex_t>* output;
friend class VFOManager;
private:
std::string name;
dsp::VFO* dspVFO;
ImGui::WaterfallVFO* wtfVFO;
};
VFOManager::VFO* createVFO(std::string name, int reference, double offset, double bandwidth, double sampleRate, int blockSize);
void deleteVFO(VFOManager::VFO* vfo);
void setOffset(std::string name, double offset);
void setCenterOffset(std::string name, double offset);
void setBandwidth(std::string name, double bandwidth);
void setSampleRate(std::string name, double sampleRate, double bandwidth);
void setReference(std::string name, int ref);
int getOutputBlockSize(std::string name);
void updateFromWaterfall(ImGui::WaterFall* wtf);
private:
std::map<std::string, VFO*> vfos;
};

Some files were not shown because too many files have changed in this diff Show More