mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2025-07-09 18:45:22 +02:00
Compare commits
89 Commits
0.2.5_beta
...
0.2.5_beta
Author | SHA1 | Date | |
---|---|---|---|
a2d93915e8 | |||
29e9db184f | |||
2f93c7ae58 | |||
4abfe407da | |||
9b27e81091 | |||
39787743fd | |||
22e47807b8 | |||
898525a6d8 | |||
1ebcfe7d80 | |||
1dbc39b970 | |||
80f5f6c288 | |||
b18acd469f | |||
cefcd18269 | |||
4de3ac176d | |||
afd5699ff1 | |||
b79461e3ce | |||
de6ab8ecdf | |||
d0180d42a8 | |||
2e504b40f6 | |||
9b00304c29 | |||
979928ded8 | |||
7c4e442432 | |||
0dd445f101 | |||
f217804838 | |||
9a630fff06 | |||
db508214d7 | |||
8e764f48ae | |||
2583063f5f | |||
dd4ec22b39 | |||
42dbcec93f | |||
9bbf634f5d | |||
d6b09759de | |||
5bb8a943ad | |||
b370eda0d5 | |||
69bcbf6f27 | |||
04823abb83 | |||
7269a0ea12 | |||
153b58fbbd | |||
149af55e61 | |||
9cac95fd82 | |||
09498f3b18 | |||
bb919d0f32 | |||
6d0abd73a5 | |||
717f2a822b | |||
8946b4b4b6 | |||
db279d2b36 | |||
bb7965b3c4 | |||
a33fe5a4cc | |||
4a03f0870c | |||
bfe15aff19 | |||
42bc2d01f7 | |||
0cb9fc0df8 | |||
450896b122 | |||
cc0b89dbe2 | |||
c887b96a77 | |||
22541ae0f4 | |||
d83da38d79 | |||
b21f8abbd6 | |||
e9aade4d0d | |||
2bf2fff3d6 | |||
463a22fdfb | |||
3175022b31 | |||
504d910226 | |||
c2769e1a72 | |||
7577253dbf | |||
e4c5b2dbd1 | |||
fafd76ff94 | |||
e354d11820 | |||
a3374c7eca | |||
552b886cea | |||
ff9a19381b | |||
77aacc2e5d | |||
6a1fa2c13b | |||
c3bb64bf6e | |||
da68ab4ed0 | |||
1aedf92bcd | |||
abcf484506 | |||
a93681a980 | |||
a08758ea54 | |||
0a0f5b8e8c | |||
84f67a3ac1 | |||
22d18a9e58 | |||
d1a8425d43 | |||
65d94f03e4 | |||
3a49041f27 | |||
eec2f7c4a0 | |||
720df5ce89 | |||
d7cea16d4a | |||
d5c0fdd525 |
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: ryzerth
|
45
.github/workflows/cmake.yml
vendored
Normal file
45
.github/workflows/cmake.yml
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
name: Linux Build
|
||||
|
||||
on: [push]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# The CMake configure and build commands are platform agnostic and should work equally
|
||||
# well on Windows or Mac. You can convert this to a matrix build if you need
|
||||
# cross-platform coverage.
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Update repositories
|
||||
run: sudo apt update
|
||||
|
||||
- name: Install dependencies
|
||||
run: sudo apt install libfftw3-dev libglfw3-dev libglew-dev libvolk2-dev libsoapysdr-dev libairspyhf-dev libairspy-dev libiio-dev libad9361-dev portaudio19-dev libhackrf-dev
|
||||
|
||||
- name: Create Build Environment
|
||||
# Some projects don't allow in-source building, so create a separate build directory
|
||||
# We'll use this as our working directory for all subsequent commands
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
|
||||
- name: Configure CMake
|
||||
# Use a bash shell so we can use the same syntax for environment variable
|
||||
# access regardless of the host operating system
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
# Note the current convention is to use the -S and -B options here to specify source
|
||||
# and build directories, but this is only available with CMake 3.13 and higher.
|
||||
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
# Execute the build. You can specify a specific target with "--target <NAME>"
|
||||
run: cmake --build . --config $BUILD_TYPE
|
@ -5,7 +5,9 @@ option(OPT_BUILD_RTL_TCP_SOURCE "Build RTL-TCP Source Module (no dependencies re
|
||||
option(OPT_BUILD_SPYSERVER_SOURCE "Build SpyServer Source Module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_SOAPY_SOURCE "Build SoapySDR Source Module (Depedencies: soapysdr)" ON)
|
||||
option(OPT_BUILD_AIRSPYHF_SOURCE "Build Airspy HF+ Source Module (Depedencies: libairspyhf)" ON)
|
||||
option(OPT_BUILD_AIRSPY_SOURCE "Build Airspy Source Module (Depedencies: libairspy)" ON)
|
||||
option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Depedencies: libiio, libad9361)" ON)
|
||||
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Depedencies: libhackrf)" ON)
|
||||
option(OPT_BUILD_AUDIO_SINK "Build Audio Sink Module (Depedencies: portaudio)" ON)
|
||||
|
||||
# Core of SDR++
|
||||
@ -32,10 +34,18 @@ if (OPT_BUILD_AIRSPYHF_SOURCE)
|
||||
add_subdirectory("airspyhf_source")
|
||||
endif (OPT_BUILD_AIRSPYHF_SOURCE)
|
||||
|
||||
if (OPT_BUILD_AIRSPY_SOURCE)
|
||||
add_subdirectory("airspy_source")
|
||||
endif (OPT_BUILD_AIRSPY_SOURCE)
|
||||
|
||||
if (OPT_BUILD_PLUTOSDR_SOURCE)
|
||||
add_subdirectory("plutosdr_source")
|
||||
endif (OPT_BUILD_PLUTOSDR_SOURCE)
|
||||
|
||||
if (OPT_BUILD_HACKRF_SOURCE)
|
||||
add_subdirectory("hackrf_source")
|
||||
endif (OPT_BUILD_HACKRF_SOURCE)
|
||||
|
||||
if (OPT_BUILD_AUDIO_SINK)
|
||||
add_subdirectory("audio_sink")
|
||||
endif (OPT_BUILD_AUDIO_SINK)
|
||||
|
31
airspy_source/CMakeLists.txt
Normal file
31
airspy_source/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(airspy_source)
|
||||
|
||||
if (MSVC)
|
||||
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive")
|
||||
endif (MSVC)
|
||||
|
||||
include_directories("src/")
|
||||
|
||||
file(GLOB SRC "src/*.cpp")
|
||||
|
||||
add_library(airspy_source SHARED ${SRC})
|
||||
target_link_libraries(airspy_source PRIVATE sdrpp_core)
|
||||
set_target_properties(airspy_source PROPERTIES PREFIX "")
|
||||
|
||||
if (MSVC)
|
||||
# Lib path
|
||||
target_link_directories(sdrpp_core PUBLIC "C:/Program Files/PothosSDR/bin/")
|
||||
|
||||
target_link_libraries(airspy_source PUBLIC airspy)
|
||||
else (MSVC)
|
||||
find_package(PkgConfig)
|
||||
|
||||
pkg_check_modules(LIBAIRSPYHF REQUIRED libairspy)
|
||||
|
||||
target_include_directories(airspy_source PUBLIC ${LIBAIRSPYHF_INCLUDE_DIRS})
|
||||
target_link_directories(airspy_source PUBLIC ${LIBAIRSPYHF_LIBRARY_DIRS})
|
||||
target_link_libraries(airspy_source PUBLIC ${LIBAIRSPYHF_LIBRARIES})
|
||||
endif (MSVC)
|
597
airspy_source/src/main.cpp
Normal file
597
airspy_source/src/main.cpp
Normal file
@ -0,0 +1,597 @@
|
||||
#include <imgui.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <module.h>
|
||||
#include <gui/gui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <core.h>
|
||||
#include <gui/style.h>
|
||||
#include <config.h>
|
||||
#include <options.h>
|
||||
#include <libairspy/airspy.h>
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
SDRPP_MOD_INFO {
|
||||
/* Name: */ "airspy_source",
|
||||
/* Description: */ "Airspy source module for SDR++",
|
||||
/* Author: */ "Ryzerth",
|
||||
/* Version: */ 0, 1, 0,
|
||||
/* Max instances */ 1
|
||||
};
|
||||
|
||||
ConfigManager config;
|
||||
|
||||
class AirspySourceModule : public ModuleManager::Instance {
|
||||
public:
|
||||
AirspySourceModule(std::string name) {
|
||||
this->name = name;
|
||||
|
||||
sampleRate = 10000000.0;
|
||||
|
||||
handler.ctx = this;
|
||||
handler.selectHandler = menuSelected;
|
||||
handler.deselectHandler = menuDeselected;
|
||||
handler.menuHandler = menuHandler;
|
||||
handler.startHandler = start;
|
||||
handler.stopHandler = stop;
|
||||
handler.tuneHandler = tune;
|
||||
handler.stream = &stream;
|
||||
|
||||
refresh();
|
||||
if (sampleRateList.size() > 0) {
|
||||
sampleRate = sampleRateList[0];
|
||||
}
|
||||
|
||||
// Select device from config
|
||||
config.aquire();
|
||||
std::string devSerial = config.conf["device"];
|
||||
config.release();
|
||||
selectByString(devSerial);
|
||||
core::setInputSampleRate(sampleRate);
|
||||
|
||||
sigpath::sourceManager.registerSource("Airspy", &handler);
|
||||
}
|
||||
|
||||
~AirspySourceModule() {
|
||||
|
||||
}
|
||||
|
||||
void enable() {
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
void disable() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
bool isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
devList.clear();
|
||||
devListTxt = "";
|
||||
|
||||
uint64_t serials[256];
|
||||
int n = airspy_list_devices(serials, 256);
|
||||
|
||||
char buf[1024];
|
||||
for (int i = 0; i < n; i++) {
|
||||
sprintf(buf, "%016" PRIX64, serials[i]);
|
||||
devList.push_back(serials[i]);
|
||||
devListTxt += buf;
|
||||
devListTxt += '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void selectFirst() {
|
||||
if (devList.size() != 0) {
|
||||
selectBySerial(devList[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void selectByString(std::string serial) {
|
||||
char buf[1024];
|
||||
for (int i = 0; i < devList.size(); i++) {
|
||||
sprintf(buf, "%016" PRIX64, devList[i]);
|
||||
std::string str = buf;
|
||||
if (serial == str) {
|
||||
selectBySerial(devList[i]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
selectFirst();
|
||||
}
|
||||
|
||||
void selectBySerial(uint64_t serial) {
|
||||
selectedSerial = serial;
|
||||
airspy_device* dev;
|
||||
int err = airspy_open_sn(&dev, selectedSerial);
|
||||
if (err != 0) {
|
||||
char buf[1024];
|
||||
sprintf(buf, "%016" PRIX64, selectedSerial);
|
||||
spdlog::error("Could not open Airspy HF+ {0}", buf);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t sampleRates[256];
|
||||
airspy_get_samplerates(dev, sampleRates, 0);
|
||||
int n = sampleRates[0];
|
||||
airspy_get_samplerates(dev, sampleRates, n);
|
||||
sampleRateList.clear();
|
||||
sampleRateListTxt = "";
|
||||
for (int i = 0; i < n; i++) {
|
||||
sampleRateList.push_back(sampleRates[i]);
|
||||
sampleRateListTxt += getBandwdithScaled(sampleRates[i]);
|
||||
sampleRateListTxt += '\0';
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
sprintf(buf, "%016" PRIX64, serial);
|
||||
selectedSerStr = std::string(buf);
|
||||
|
||||
// Load config here
|
||||
config.aquire();
|
||||
bool created = false;
|
||||
if (!config.conf["devices"].contains(selectedSerStr)) {
|
||||
created = true;
|
||||
config.conf["devices"][selectedSerStr]["sampleRate"] = 10000000;
|
||||
config.conf["devices"][selectedSerStr]["gainMode"] = 0;
|
||||
config.conf["devices"][selectedSerStr]["sensitiveGain"] = 0;
|
||||
config.conf["devices"][selectedSerStr]["linearGain"] = 0;
|
||||
config.conf["devices"][selectedSerStr]["lnaGain"] = 0;
|
||||
config.conf["devices"][selectedSerStr]["mixerGain"] = 0;
|
||||
config.conf["devices"][selectedSerStr]["vgaGain"] = 0;
|
||||
config.conf["devices"][selectedSerStr]["lnaAgc"] = false;
|
||||
config.conf["devices"][selectedSerStr]["mixerAgc"] = false;
|
||||
config.conf["devices"][selectedSerStr]["biasT"] = false;
|
||||
}
|
||||
|
||||
// Load sample rate
|
||||
srId = 0;
|
||||
sampleRate = sampleRateList[0];
|
||||
if (config.conf["devices"][selectedSerStr].contains("sampleRate")) {
|
||||
int selectedSr = config.conf["devices"][selectedSerStr]["sampleRate"];
|
||||
for (int i = 0; i < sampleRateList.size(); i++) {
|
||||
if (sampleRateList[i] == selectedSr) {
|
||||
srId = i;
|
||||
sampleRate = selectedSr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load gains
|
||||
if (config.conf["devices"][selectedSerStr].contains("gainMode")) {
|
||||
gainMode = config.conf["devices"][selectedSerStr]["gainMode"];
|
||||
}
|
||||
if (config.conf["devices"][selectedSerStr].contains("sensitiveGain")) {
|
||||
sensitiveGain = config.conf["devices"][selectedSerStr]["sensitiveGain"];
|
||||
}
|
||||
if (config.conf["devices"][selectedSerStr].contains("linearGain")) {
|
||||
linearGain = config.conf["devices"][selectedSerStr]["linearGain"];
|
||||
}
|
||||
if (config.conf["devices"][selectedSerStr].contains("lnaGain")) {
|
||||
lnaGain = config.conf["devices"][selectedSerStr]["lnaGain"];
|
||||
}
|
||||
if (config.conf["devices"][selectedSerStr].contains("mixerGain")) {
|
||||
mixerGain = config.conf["devices"][selectedSerStr]["mixerGain"];
|
||||
}
|
||||
if (config.conf["devices"][selectedSerStr].contains("vgaGain")) {
|
||||
vgaGain = config.conf["devices"][selectedSerStr]["vgaGain"];
|
||||
}
|
||||
if (config.conf["devices"][selectedSerStr].contains("lnaAgc")) {
|
||||
lnaAgc = config.conf["devices"][selectedSerStr]["lnaAgc"];
|
||||
}
|
||||
if (config.conf["devices"][selectedSerStr].contains("mixerAgc")) {
|
||||
mixerAgc = config.conf["devices"][selectedSerStr]["mixerAgc"];
|
||||
}
|
||||
|
||||
// Load Bias-T
|
||||
if (config.conf["devices"][selectedSerStr].contains("biasT")) {
|
||||
biasT = config.conf["devices"][selectedSerStr]["biasT"];
|
||||
}
|
||||
|
||||
config.release(created);
|
||||
|
||||
airspy_close(dev);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string getBandwdithScaled(double bw) {
|
||||
char buf[1024];
|
||||
if (bw >= 1000000.0) {
|
||||
sprintf(buf, "%.1lfMHz", bw / 1000000.0);
|
||||
}
|
||||
else if (bw >= 1000.0) {
|
||||
sprintf(buf, "%.1lfKHz", bw / 1000.0);
|
||||
}
|
||||
else {
|
||||
sprintf(buf, "%.1lfHz", bw);
|
||||
}
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
static void menuSelected(void* ctx) {
|
||||
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
spdlog::info("AirspySourceModule '{0}': Menu Select!", _this->name);
|
||||
}
|
||||
|
||||
static void menuDeselected(void* ctx) {
|
||||
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||
spdlog::info("AirspySourceModule '{0}': Menu Deselect!", _this->name);
|
||||
}
|
||||
|
||||
static void start(void* ctx) {
|
||||
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||
if (_this->running) {
|
||||
return;
|
||||
}
|
||||
if (_this->selectedSerial == 0) {
|
||||
spdlog::error("Tried to start AirspyHF+ source with null serial");
|
||||
return;
|
||||
}
|
||||
|
||||
int err = airspy_open_sn(&_this->openDev, _this->selectedSerial);
|
||||
if (err != 0) {
|
||||
char buf[1024];
|
||||
sprintf(buf, "%016" PRIX64, _this->selectedSerial);
|
||||
spdlog::error("Could not open Airspy {0}", buf);
|
||||
return;
|
||||
}
|
||||
|
||||
airspy_set_samplerate(_this->openDev, _this->sampleRateList[_this->srId]);
|
||||
airspy_set_freq(_this->openDev, _this->freq);
|
||||
|
||||
if (_this->gainMode == 0) {
|
||||
airspy_set_lna_agc(_this->openDev, 0);
|
||||
airspy_set_mixer_agc(_this->openDev, 0);
|
||||
airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
|
||||
}
|
||||
else if (_this->gainMode == 1) {
|
||||
airspy_set_lna_agc(_this->openDev, 0);
|
||||
airspy_set_mixer_agc(_this->openDev, 0);
|
||||
airspy_set_linearity_gain(_this->openDev, _this->linearGain);
|
||||
}
|
||||
else if (_this->gainMode == 2) {
|
||||
if (_this->lnaAgc) {
|
||||
airspy_set_lna_agc(_this->openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_lna_agc(_this->openDev, 0);
|
||||
airspy_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||
}
|
||||
if (_this->mixerAgc) {
|
||||
airspy_set_mixer_agc(_this->openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_mixer_agc(_this->openDev, 0);
|
||||
airspy_set_mixer_gain(_this->openDev, _this->mixerGain);
|
||||
}
|
||||
airspy_set_vga_gain(_this->openDev, _this->vgaGain);
|
||||
}
|
||||
|
||||
airspy_set_rf_bias(_this->openDev, _this->biasT);
|
||||
|
||||
airspy_start_rx(_this->openDev, callback, _this);
|
||||
|
||||
_this->running = true;
|
||||
spdlog::info("AirspySourceModule '{0}': Start!", _this->name);
|
||||
}
|
||||
|
||||
static void stop(void* ctx) {
|
||||
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||
if (!_this->running) {
|
||||
return;
|
||||
}
|
||||
_this->running = false;
|
||||
_this->stream.stopWriter();
|
||||
airspy_close(_this->openDev);
|
||||
_this->stream.clearWriteStop();
|
||||
spdlog::info("AirspySourceModule '{0}': Stop!", _this->name);
|
||||
}
|
||||
|
||||
static void tune(double freq, void* ctx) {
|
||||
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||
if (_this->running) {
|
||||
airspy_set_freq(_this->openDev, freq);
|
||||
}
|
||||
_this->freq = freq;
|
||||
spdlog::info("AirspySourceModule '{0}': Tune: {1}!", _this->name, freq);
|
||||
}
|
||||
|
||||
static void menuHandler(void* ctx) {
|
||||
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||
|
||||
if (_this->running) { style::beginDisabled(); }
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth);
|
||||
if (ImGui::Combo(CONCAT("##_airspy_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) {
|
||||
_this->selectBySerial(_this->devList[_this->devId]);
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["device"] = _this->selectedSerStr;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::Combo(CONCAT("##_airspy_sr_sel_", _this->name), &_this->srId, _this->sampleRateListTxt.c_str())) {
|
||||
_this->sampleRate = _this->sampleRateList[_this->srId];
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["sampleRate"] = _this->sampleRate;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX();
|
||||
if (ImGui::Button(CONCAT("Refresh##_airspy_refr_", _this->name), ImVec2(refreshBtnWdith, 0))) {
|
||||
_this->refresh();
|
||||
config.aquire();
|
||||
std::string devSerial = config.conf["device"];
|
||||
config.release();
|
||||
_this->selectByString(devSerial);
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
}
|
||||
|
||||
if (_this->running) { style::endDisabled(); }
|
||||
|
||||
ImGui::BeginGroup();
|
||||
ImGui::Columns(3, CONCAT("AirspyGainModeColumns##_", _this->name), false);
|
||||
if (ImGui::RadioButton(CONCAT("Sensitive##_airspy_gm_", _this->name), _this->gainMode == 0)) {
|
||||
_this->gainMode = 0;
|
||||
if (_this->running) {
|
||||
airspy_set_lna_agc(_this->openDev, 0);
|
||||
airspy_set_mixer_agc(_this->openDev, 0);
|
||||
airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 0;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
ImGui::NextColumn();
|
||||
if (ImGui::RadioButton(CONCAT("Linear##_airspy_gm_", _this->name), _this->gainMode == 1)) {
|
||||
_this->gainMode = 1;
|
||||
if (_this->running) {
|
||||
airspy_set_lna_agc(_this->openDev, 0);
|
||||
airspy_set_mixer_agc(_this->openDev, 0);
|
||||
airspy_set_linearity_gain(_this->openDev, _this->linearGain);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 1;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
ImGui::NextColumn();
|
||||
if (ImGui::RadioButton(CONCAT("Free##_airspy_gm_", _this->name), _this->gainMode == 2)) {
|
||||
_this->gainMode = 2;
|
||||
if (_this->running) {
|
||||
if (_this->lnaAgc) {
|
||||
airspy_set_lna_agc(_this->openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_lna_agc(_this->openDev, 0);
|
||||
airspy_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||
}
|
||||
if (_this->mixerAgc) {
|
||||
airspy_set_mixer_agc(_this->openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_mixer_agc(_this->openDev, 0);
|
||||
airspy_set_mixer_gain(_this->openDev, _this->mixerGain);
|
||||
}
|
||||
airspy_set_vga_gain(_this->openDev, _this->vgaGain);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 2;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
ImGui::Columns(1, CONCAT("EndAirspyGainModeColumns##_", _this->name), false);
|
||||
ImGui::EndGroup();
|
||||
|
||||
// Gain menus
|
||||
|
||||
if (_this->gainMode == 0) {
|
||||
ImGui::Text("Gain");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||
if (ImGui::SliderInt(CONCAT("##_airspy_sens_gain_", _this->name), &_this->sensitiveGain, 0, 21)) {
|
||||
if (_this->running) {
|
||||
airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["sensitiveGain"] = _this->sensitiveGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_this->gainMode == 1) {
|
||||
ImGui::Text("Gain");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||
if (ImGui::SliderInt(CONCAT("##_airspy_lin_gain_", _this->name), &_this->linearGain, 0, 21)) {
|
||||
if (_this->running) {
|
||||
airspy_set_linearity_gain(_this->openDev, _this->linearGain);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["linearGain"] = _this->linearGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_this->gainMode == 2) {
|
||||
// Calculate position of sliders
|
||||
float pos = ImGui::CalcTextSize("Mixer Gain").x + 10;
|
||||
|
||||
if (_this->lnaAgc) { style::beginDisabled(); }
|
||||
ImGui::Text("LNA Gain");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(pos);
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||
if (ImGui::SliderInt(CONCAT("##_airspy_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) {
|
||||
if (_this->running) {
|
||||
airspy_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["lnaGain"] = _this->lnaGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
if (_this->lnaAgc) { style::endDisabled(); }
|
||||
|
||||
if (_this->mixerAgc) { style::beginDisabled(); }
|
||||
ImGui::Text("Mixer Gain");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(pos);
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||
if (ImGui::SliderInt(CONCAT("##_airspy_mix_gain_", _this->name), &_this->mixerGain, 0, 15)) {
|
||||
if (_this->running) {
|
||||
airspy_set_mixer_gain(_this->openDev, _this->mixerGain);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["mixerGain"] = _this->mixerGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
if (_this->mixerAgc) { style::endDisabled(); }
|
||||
|
||||
ImGui::Text("VGA Gain");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(pos);
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||
if (ImGui::SliderInt(CONCAT("##_airspy_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) {
|
||||
if (_this->running) {
|
||||
airspy_set_vga_gain(_this->openDev, _this->vgaGain);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["vgaGain"] = _this->vgaGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
// AGC Control
|
||||
if (ImGui::Checkbox(CONCAT("LNA AGC##_airspy_", _this->name), &_this->lnaAgc)) {
|
||||
if (_this->running) {
|
||||
if (_this->lnaAgc) {
|
||||
airspy_set_lna_agc(_this->openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_lna_agc(_this->openDev, 0);
|
||||
airspy_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||
}
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["lnaAgc"] = _this->lnaAgc;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
if (ImGui::Checkbox(CONCAT("Mixer AGC##_airspy_", _this->name), &_this->mixerAgc)) {
|
||||
if (_this->running) {
|
||||
if (_this->mixerAgc) {
|
||||
airspy_set_mixer_agc(_this->openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_mixer_agc(_this->openDev, 0);
|
||||
airspy_set_mixer_gain(_this->openDev, _this->mixerGain);
|
||||
}
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["mixerAgc"] = _this->mixerAgc;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bias T
|
||||
|
||||
if (ImGui::Checkbox(CONCAT("Bias T##_airspy_", _this->name), &_this->biasT)) {
|
||||
if (_this->running) {
|
||||
airspy_set_rf_bias(_this->openDev, _this->biasT);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["biasT"] = _this->biasT;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
static int callback(airspy_transfer_t* transfer) {
|
||||
AirspySourceModule* _this = (AirspySourceModule*)transfer->ctx;
|
||||
memcpy(_this->stream.writeBuf, transfer->samples, transfer->sample_count * sizeof(dsp::complex_t));
|
||||
if (!_this->stream.swap(transfer->sample_count)) { return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
airspy_device* openDev;
|
||||
bool enabled = true;
|
||||
dsp::stream<dsp::complex_t> stream;
|
||||
double sampleRate;
|
||||
SourceManager::SourceHandler handler;
|
||||
bool running = false;
|
||||
double freq;
|
||||
uint64_t selectedSerial = 0;
|
||||
std::string selectedSerStr = "";
|
||||
int devId = 0;
|
||||
int srId = 0;
|
||||
|
||||
bool biasT = false;
|
||||
|
||||
int lnaGain = 0;
|
||||
int vgaGain = 0;
|
||||
int mixerGain = 0;
|
||||
int linearGain = 0;
|
||||
int sensitiveGain = 0;
|
||||
|
||||
int gainMode = 0;
|
||||
|
||||
bool lnaAgc = false;
|
||||
bool mixerAgc = false;
|
||||
|
||||
std::vector<uint64_t> devList;
|
||||
std::string devListTxt;
|
||||
std::vector<uint32_t> sampleRateList;
|
||||
std::string sampleRateListTxt;
|
||||
};
|
||||
|
||||
MOD_EXPORT void _INIT_() {
|
||||
json def = json({});
|
||||
def["devices"] = json({});
|
||||
def["device"] = "";
|
||||
config.setPath(options::opts.root + "/airspy_config.json");
|
||||
config.load(def);
|
||||
config.enableAutoSave();
|
||||
}
|
||||
|
||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||
return new AirspySourceModule(name);
|
||||
}
|
||||
|
||||
MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
|
||||
delete (AirspySourceModule*)instance;
|
||||
}
|
||||
|
||||
MOD_EXPORT void _END_() {
|
||||
config.disableAutoSave();
|
||||
config.save();
|
||||
}
|
@ -23,9 +23,9 @@ if (MSVC)
|
||||
else (MSVC)
|
||||
find_package(PkgConfig)
|
||||
|
||||
pkg_check_modules(SOAPY REQUIRED airspyhf)
|
||||
pkg_check_modules(LIBAIRSPYHF REQUIRED libairspyhf)
|
||||
|
||||
target_include_directories(airspyhf_source PUBLIC ${AIRSPYHF_INCLUDE_DIRS})
|
||||
target_link_directories(airspyhf_source PUBLIC ${AIRSPYHF_LIBRARY_DIRS})
|
||||
target_link_libraries(airspyhf_source PUBLIC ${AIRSPYHF_LIBRARIES})
|
||||
target_include_directories(airspyhf_source PUBLIC ${LIBAIRSPYHF_INCLUDE_DIRS})
|
||||
target_link_directories(airspyhf_source PUBLIC ${LIBAIRSPYHF_LIBRARY_DIRS})
|
||||
target_link_libraries(airspyhf_source PUBLIC ${LIBAIRSPYHF_LIBRARIES})
|
||||
endif (MSVC)
|
@ -1,11 +1,12 @@
|
||||
#include <imgui.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <new_module.h>
|
||||
#include <module.h>
|
||||
#include <gui/gui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <core.h>
|
||||
#include <gui/style.h>
|
||||
#include <config.h>
|
||||
#include <options.h>
|
||||
#include <libairspyhf/airspyhf.h>
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
@ -18,7 +19,7 @@ SDRPP_MOD_INFO {
|
||||
/* Max instances */ 1
|
||||
};
|
||||
|
||||
//ConfigManager config;
|
||||
ConfigManager config;
|
||||
|
||||
const char* AGG_MODES_STR = "Off\0Low\0High\0";
|
||||
|
||||
@ -40,9 +41,11 @@ public:
|
||||
|
||||
refresh();
|
||||
|
||||
// config.aquire();
|
||||
// std::string serString = config.conf["device"];
|
||||
// config.release();
|
||||
config.aquire();
|
||||
std::string devSerial = config.conf["device"];
|
||||
config.release();
|
||||
selectByString(devSerial);
|
||||
core::setInputSampleRate(sampleRate);
|
||||
|
||||
sigpath::sourceManager.registerSource("Airspy HF+", &handler);
|
||||
}
|
||||
@ -119,25 +122,74 @@ public:
|
||||
airspyhf_get_samplerates(dev, sampleRates, 0);
|
||||
int n = sampleRates[0];
|
||||
airspyhf_get_samplerates(dev, sampleRates, n);
|
||||
char buf[1024];
|
||||
sampleRateList.clear();
|
||||
sampleRateListTxt = "";
|
||||
for (int i = 0; i < n; i++) {
|
||||
sampleRateList.push_back(sampleRates[i]);
|
||||
sprintf(buf, "%d", sampleRates[i]);
|
||||
sampleRateListTxt += buf;
|
||||
sampleRateListTxt += getBandwdithScaled(sampleRates[i]);
|
||||
sampleRateListTxt += '\0';
|
||||
}
|
||||
|
||||
blockSize = airspyhf_get_output_size(dev);
|
||||
spdlog::info("AirspyHF block size {0}", blockSize);
|
||||
char buf[1024];
|
||||
sprintf(buf, "%016" PRIX64, serial);
|
||||
selectedSerStr = std::string(buf);
|
||||
|
||||
// Load config here
|
||||
config.aquire();
|
||||
bool created = false;
|
||||
if (!config.conf["devices"].contains(selectedSerStr)) {
|
||||
created = true;
|
||||
config.conf["devices"][selectedSerStr]["sampleRate"] = 768000;
|
||||
config.conf["devices"][selectedSerStr]["agcMode"] = 0;
|
||||
config.conf["devices"][selectedSerStr]["lna"] = false;
|
||||
config.conf["devices"][selectedSerStr]["attenuation"] = 0;
|
||||
}
|
||||
|
||||
// Load sample rate
|
||||
srId = 0;
|
||||
sampleRate = sampleRateList[0];
|
||||
if (config.conf["devices"][selectedSerStr].contains("sampleRate")) {
|
||||
int selectedSr = config.conf["devices"][selectedSerStr]["sampleRate"];
|
||||
for (int i = 0; i < sampleRateList.size(); i++) {
|
||||
if (sampleRateList[i] == selectedSr) {
|
||||
srId = i;
|
||||
sampleRate = selectedSr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load Gains
|
||||
if (config.conf["devices"][selectedSerStr].contains("agcMode")) {
|
||||
agcMode = config.conf["devices"][selectedSerStr]["agcMode"];
|
||||
}
|
||||
if (config.conf["devices"][selectedSerStr].contains("lna")) {
|
||||
hfLNA = config.conf["devices"][selectedSerStr]["lna"];
|
||||
}
|
||||
if (config.conf["devices"][selectedSerStr].contains("attenuation")) {
|
||||
atten = config.conf["devices"][selectedSerStr]["attenuation"];
|
||||
}
|
||||
|
||||
config.release(created);
|
||||
|
||||
airspyhf_close(dev);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string getBandwdithScaled(double bw) {
|
||||
char buf[1024];
|
||||
if (bw >= 1000000.0) {
|
||||
sprintf(buf, "%.1lfMHz", bw / 1000000.0);
|
||||
}
|
||||
else if (bw >= 1000.0) {
|
||||
sprintf(buf, "%.1lfKHz", bw / 1000.0);
|
||||
}
|
||||
else {
|
||||
sprintf(buf, "%.1lfHz", bw);
|
||||
}
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
static void menuSelected(void* ctx) {
|
||||
AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx;
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
@ -167,8 +219,6 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
spdlog::warn("{0}", _this->sampleRateList[_this->srId]);
|
||||
|
||||
airspyhf_set_samplerate(_this->openDev, _this->sampleRateList[_this->srId]);
|
||||
airspyhf_set_freq(_this->openDev, _this->freq);
|
||||
airspyhf_set_hf_agc(_this->openDev, (_this->agcMode != 0));
|
||||
@ -214,18 +264,33 @@ private:
|
||||
ImGui::SetNextItemWidth(menuWidth);
|
||||
if (ImGui::Combo(CONCAT("##_airspyhf_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) {
|
||||
_this->selectBySerial(_this->devList[_this->devId]);
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["device"] = _this->selectedSerStr;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::Combo(CONCAT("##_airspyhf_sr_sel_", _this->name), &_this->srId, _this->sampleRateListTxt.c_str())) {
|
||||
_this->sampleRate = _this->sampleRateList[_this->srId];
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["sampleRate"] = _this->sampleRate;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX();
|
||||
if (ImGui::Button(CONCAT("Refresh##_airspyhf_refr_", _this->name), ImVec2(refreshBtnWdith, 0))) {
|
||||
_this->refresh();
|
||||
_this->selectFirst();
|
||||
config.aquire();
|
||||
std::string devSerial = config.conf["device"];
|
||||
config.release();
|
||||
_this->selectByString(devSerial);
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
}
|
||||
|
||||
if (_this->running) { style::endDisabled(); }
|
||||
@ -240,6 +305,11 @@ private:
|
||||
airspyhf_set_hf_agc_threshold(_this->openDev, _this->agcMode - 1);
|
||||
}
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["agcMode"] = _this->agcMode;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text("HF LNA");
|
||||
@ -247,6 +317,11 @@ private:
|
||||
if (ImGui::Checkbox(CONCAT("##_airspyhf_lna_", _this->name), &_this->hfLNA)) {
|
||||
if (_this->running) {
|
||||
airspyhf_set_hf_lna(_this->openDev, _this->hfLNA);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["lna"] = _this->hfLNA;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,23 +333,24 @@ private:
|
||||
if (_this->running) {
|
||||
airspyhf_set_hf_att(_this->openDev, _this->atten / 6);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.aquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["attenuation"] = _this->atten;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int callback(airspyhf_transfer_t* transfer) {
|
||||
AirspyHFSourceModule* _this = (AirspyHFSourceModule*)transfer->ctx;
|
||||
if (_this->stream.aquire() < 0) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(_this->stream.data, transfer->samples, transfer->sample_count * sizeof(dsp::complex_t));
|
||||
_this->stream.write(transfer->sample_count);
|
||||
memcpy(_this->stream.writeBuf, transfer->samples, transfer->sample_count * sizeof(dsp::complex_t));
|
||||
if (!_this->stream.swap(transfer->sample_count)) { return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
airspyhf_device_t* openDev;
|
||||
bool enabled = true;
|
||||
int blockSize = 0;
|
||||
dsp::stream<dsp::complex_t> stream;
|
||||
double sampleRate;
|
||||
SourceManager::SourceHandler handler;
|
||||
@ -286,6 +362,7 @@ private:
|
||||
int agcMode = AGC_MODE_OFF;
|
||||
bool hfLNA = false;
|
||||
int atten = 0;
|
||||
std::string selectedSerStr = "";
|
||||
|
||||
std::vector<uint64_t> devList;
|
||||
std::string devListTxt;
|
||||
@ -294,12 +371,12 @@ private:
|
||||
};
|
||||
|
||||
MOD_EXPORT void _INIT_() {
|
||||
// config.setPath(ROOT_DIR "/airspyhf_config.json");
|
||||
// json defConf;
|
||||
// defConf["device"] = "";
|
||||
// defConf["devices"] = json::object();
|
||||
// config.load(defConf);
|
||||
// config.enableAutoSave();
|
||||
json def = json({});
|
||||
def["devices"] = json({});
|
||||
def["device"] = "";
|
||||
config.setPath(options::opts.root + "/airspyhf_config.json");
|
||||
config.load(def);
|
||||
config.enableAutoSave();
|
||||
}
|
||||
|
||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||
@ -311,6 +388,6 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
|
||||
}
|
||||
|
||||
MOD_EXPORT void _END_() {
|
||||
// config.disableAutoSave();
|
||||
// config.save();
|
||||
config.disableAutoSave();
|
||||
config.save();
|
||||
}
|
@ -13,4 +13,20 @@ include_directories("src/")
|
||||
|
||||
add_library(audio_sink SHARED ${SRC})
|
||||
target_link_libraries(audio_sink PRIVATE sdrpp_core)
|
||||
set_target_properties(audio_sink PROPERTIES PREFIX "")
|
||||
set_target_properties(audio_sink PROPERTIES PREFIX "")
|
||||
|
||||
if (MSVC)
|
||||
find_package(portaudio CONFIG REQUIRED)
|
||||
target_link_libraries(sdrpp_core PUBLIC portaudio)
|
||||
else (MSVC)
|
||||
find_package(PkgConfig)
|
||||
|
||||
pkg_check_modules(PORTAUDIO REQUIRED portaudio-2.0)
|
||||
|
||||
target_include_directories(sdrpp_core PUBLIC ${PORTAUDIO_INCLUDE_DIRS})
|
||||
|
||||
target_link_directories(sdrpp_core PUBLIC ${PORTAUDIO_LIBRARY_DIRS})
|
||||
|
||||
target_link_libraries(sdrpp_core PUBLIC ${PORTAUDIO_LIBRARIES})
|
||||
|
||||
endif (MSVC)
|
@ -1,5 +1,5 @@
|
||||
#include <imgui.h>
|
||||
#include <new_module.h>
|
||||
#include <module.h>
|
||||
#include <gui/gui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <signal_path/sink.h>
|
||||
@ -36,7 +36,6 @@ public:
|
||||
stereoRB.init(_stream->sinkOut);
|
||||
|
||||
// Initialize PortAudio
|
||||
Pa_Initialize();
|
||||
devCount = Pa_GetDeviceCount();
|
||||
devId = Pa_GetDefaultOutputDevice();
|
||||
const PaDeviceInfo *deviceInfo;
|
||||
@ -179,6 +178,7 @@ private:
|
||||
}
|
||||
|
||||
void doStop() {
|
||||
s2m.stop();
|
||||
monoRB.stop();
|
||||
stereoRB.stop();
|
||||
monoRB.data.stopReader();
|
||||
@ -238,11 +238,14 @@ public:
|
||||
this->name = name;
|
||||
provider.create = create_sink;
|
||||
provider.ctx = this;
|
||||
|
||||
Pa_Initialize();
|
||||
|
||||
sigpath::sinkManager.registerSinkProvider("Audio", provider);
|
||||
}
|
||||
|
||||
~AudioSinkModule() {
|
||||
|
||||
Pa_Terminate();
|
||||
}
|
||||
|
||||
void enable() {
|
||||
|
@ -30,9 +30,6 @@ if (MSVC)
|
||||
|
||||
# Volk
|
||||
target_link_libraries(sdrpp_core PUBLIC volk)
|
||||
|
||||
# SoapySDR
|
||||
target_link_libraries(sdrpp_core PUBLIC SoapySDR)
|
||||
|
||||
# Glew
|
||||
find_package(GLEW REQUIRED)
|
||||
@ -46,12 +43,6 @@ if (MSVC)
|
||||
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)
|
||||
|
||||
target_link_libraries(sdrpp_core PUBLIC volk)
|
||||
|
||||
else()
|
||||
find_package(PkgConfig)
|
||||
find_package(OpenGL REQUIRED)
|
||||
@ -60,32 +51,28 @@ else()
|
||||
pkg_check_modules(FFTW3 REQUIRED fftw3f)
|
||||
pkg_check_modules(VOLK REQUIRED volk)
|
||||
pkg_check_modules(GLFW3 REQUIRED glfw3)
|
||||
pkg_check_modules(PORTAUDIO REQUIRED portaudio-2.0)
|
||||
|
||||
target_include_directories(sdrpp_core PUBLIC
|
||||
${GLEW_INCLUDE_DIRS}
|
||||
${FFTW3_INCLUDE_DIRS}
|
||||
${GLFW3_INCLUDE_DIRS}
|
||||
${VOLK_INCLUDE_DIRS}
|
||||
${PORTAUDIO_INCLUDE_DIRS}
|
||||
)
|
||||
)
|
||||
|
||||
target_link_directories(sdrpp_core PUBLIC
|
||||
${GLEW_LIBRARY_DIRS}
|
||||
${FFTW3_LIBRARY_DIRS}
|
||||
${GLFW3_LIBRARY_DIRS}
|
||||
${VOLK_LIBRARY_DIRS}
|
||||
${PORTAUDIO_LIBRARY_DIRS}
|
||||
)
|
||||
)
|
||||
|
||||
target_link_libraries(sdrpp_core PUBLIC
|
||||
${OPENGL_LIBRARIES}
|
||||
${GLEW_STATIC_LIBRARIES}
|
||||
${FFTW3_STATIC_LIBRARIES}
|
||||
${GLFW3_STATIC_LIBRARIES}
|
||||
${VOLK_STATIC_LIBRARIES}
|
||||
${PORTAUDIO_STATIC_LIBRARIES}
|
||||
)
|
||||
${GLEW_LIBRARIES}
|
||||
${FFTW3_LIBRARIES}
|
||||
${GLFW3_LIBRARIES}
|
||||
${VOLK_LIBRARIES}
|
||||
)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
target_link_libraries(sdrpp_core PUBLIC stdc++fs)
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <options.h>
|
||||
#include <duktape/duktape.h>
|
||||
#include <duktape/duk_console.h>
|
||||
#include <filesystem>
|
||||
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||
#include <stb_image_resize.h>
|
||||
@ -77,6 +78,20 @@ int sdrpp_main(int argc, char *argv[]) {
|
||||
options::loadDefaults();
|
||||
if (!options::parse(argc, argv)) { return -1; }
|
||||
|
||||
// Check root directory
|
||||
if (!std::filesystem::exists(options::opts.root)) {
|
||||
spdlog::warn("Root directory {0} does not exist, creating it", options::opts.root);
|
||||
if (!std::filesystem::create_directory(options::opts.root)) {
|
||||
spdlog::error("Could not create root directory {0}", options::opts.root);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!std::filesystem::is_directory(options::opts.root)) {
|
||||
spdlog::error("{0} is not a directory", options::opts.root);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ======== DEFAULT CONFIG ========
|
||||
json defConfig;
|
||||
defConfig["bandColors"]["amateur"] = "#FF0000FF";
|
||||
@ -87,6 +102,7 @@ int sdrpp_main(int argc, char *argv[]) {
|
||||
defConfig["bandPlan"] = "General";
|
||||
defConfig["bandPlanEnabled"] = true;
|
||||
defConfig["centerTuning"] = false;
|
||||
defConfig["colorMap"] = "Classic";
|
||||
defConfig["fftHeight"] = 300;
|
||||
defConfig["frequency"] = 100000000.0;
|
||||
defConfig["max"] = 0.0;
|
||||
@ -103,12 +119,17 @@ int sdrpp_main(int argc, char *argv[]) {
|
||||
};
|
||||
defConfig["menuWidth"] = 300;
|
||||
defConfig["min"] = -70.0;
|
||||
defConfig["moduleInstances"]["Audio Sink"] = "audio_sink";
|
||||
defConfig["moduleInstances"]["PlutoSDR Source"] = "plutosdr_source";
|
||||
defConfig["moduleInstances"]["RTL-TCP Source"] = "rtl_tcp_source";
|
||||
|
||||
defConfig["moduleInstances"]["Radio"] = "radio";
|
||||
defConfig["moduleInstances"]["Recorder"] = "recorder";
|
||||
defConfig["moduleInstances"]["SoapySDR Source"] = "soapy_source";
|
||||
defConfig["moduleInstances"]["PlutoSDR Source"] = "plutosdr_source";
|
||||
defConfig["moduleInstances"]["RTL-TCP Source"] = "rtl_tcp_source";
|
||||
defConfig["moduleInstances"]["AirspyHF+ Source"] = "airspyhf_source";
|
||||
defConfig["moduleInstances"]["Airspy Source"] = "airspy_source";
|
||||
defConfig["moduleInstances"]["HackRF Source"] = "hackrf_source";
|
||||
defConfig["moduleInstances"]["Audio Sink"] = "audio_sink";
|
||||
|
||||
defConfig["modules"] = json::array();
|
||||
defConfig["offset"] = 0.0;
|
||||
defConfig["showWaterfall"] = true;
|
||||
@ -116,6 +137,20 @@ int sdrpp_main(int argc, char *argv[]) {
|
||||
defConfig["streams"] = json::object();
|
||||
defConfig["windowSize"]["h"] = 720;
|
||||
defConfig["windowSize"]["w"] = 1280;
|
||||
|
||||
defConfig["bandColors"]["broadcast"] = "#0000FFFF";
|
||||
defConfig["bandColors"]["amateur"] = "#FF0000FF";
|
||||
defConfig["bandColors"]["aviation"] = "#00FF00FF";
|
||||
defConfig["bandColors"]["marine"] = "#00FFFFFF";
|
||||
defConfig["bandColors"]["military"] = "#FFFF00FF";
|
||||
|
||||
#ifdef _WIN32
|
||||
defConfig["modulesDirectory"] = "./modules";
|
||||
defConfig["resourcesDirectory"] = "./res";
|
||||
#else
|
||||
defConfig["modulesDirectory"] = "/usr/lib/sdrpp/plugins";
|
||||
defConfig["resourcesDirectory"] = "/usr/share/sdrpp";
|
||||
#endif
|
||||
|
||||
// Load config
|
||||
spdlog::info("Loading config");
|
||||
@ -123,21 +158,42 @@ int sdrpp_main(int argc, char *argv[]) {
|
||||
core::configManager.load(defConfig);
|
||||
core::configManager.enableAutoSave();
|
||||
|
||||
// Fix config
|
||||
core::configManager.aquire();
|
||||
for (auto const& item : defConfig.items()) {
|
||||
if (!core::configManager.conf.contains(item.key())) {
|
||||
spdlog::warn("Missing key in config {0}, repairing", item.key());
|
||||
core::configManager.conf[item.key()] = defConfig[item.key()];
|
||||
}
|
||||
}
|
||||
core::configManager.release(true);
|
||||
|
||||
// Setup window
|
||||
glfwSetErrorCallback(glfw_error_callback);
|
||||
if (!glfwInit()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
// GL 3.2 + GLSL 150
|
||||
const char* glsl_version = "#version 150";
|
||||
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
|
||||
#else
|
||||
// GL 3.0 + GLSL 120
|
||||
const char* glsl_version = "#version 120";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
#endif
|
||||
|
||||
core::configManager.aquire();
|
||||
int winWidth = core::configManager.conf["windowSize"]["w"];
|
||||
int winHeight = core::configManager.conf["windowSize"]["h"];
|
||||
maximized = core::configManager.conf["maximized"];
|
||||
std::string resDir = core::configManager.conf["resourcesDirectory"];
|
||||
json bandColors = core::configManager.conf["bandColors"];
|
||||
core::configManager.release();
|
||||
|
||||
// Create window with graphics context
|
||||
@ -157,7 +213,7 @@ int sdrpp_main(int argc, char *argv[]) {
|
||||
|
||||
// Load app icon
|
||||
GLFWimage icons[10];
|
||||
icons[0].pixels = stbi_load(((std::string)(options::opts.root + "/res/icons/sdrpp.png")).c_str(), &icons[0].width, &icons[0].height, 0, 4);
|
||||
icons[0].pixels = stbi_load(((std::string)(resDir + "/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;
|
||||
@ -196,23 +252,23 @@ int sdrpp_main(int argc, char *argv[]) {
|
||||
|
||||
// Setup Platform/Renderer bindings
|
||||
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
||||
ImGui_ImplOpenGL3_Init("#version 150");
|
||||
ImGui_ImplOpenGL3_Init(glsl_version);
|
||||
|
||||
style::setDarkStyle();
|
||||
if (!style::setDarkStyle(resDir)) { return -1; }
|
||||
|
||||
LoadingScreen::setWindow(window);
|
||||
|
||||
LoadingScreen::show("Loading icons");
|
||||
spdlog::info("Loading icons");
|
||||
icons::load();
|
||||
if (!icons::load(resDir)) { return -1; }
|
||||
|
||||
LoadingScreen::show("Loading band plans");
|
||||
spdlog::info("Loading band plans");
|
||||
bandplan::loadFromDir(options::opts.root + "/bandplans");
|
||||
bandplan::loadFromDir(resDir + "/bandplans");
|
||||
|
||||
LoadingScreen::show("Loading band plan colors");
|
||||
spdlog::info("Loading band plans color table");
|
||||
bandplan::loadColorTable(options::opts.root + "/band_colors.json");
|
||||
bandplan::loadColorTable(bandColors);
|
||||
|
||||
windowInit();
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
#include <config.h>
|
||||
#include <new_module.h>
|
||||
#include <module.h>
|
||||
#include <scripting.h>
|
||||
#include <new_module.h>
|
||||
#include <module.h>
|
||||
|
||||
namespace core {
|
||||
SDRPP_EXPORT ConfigManager configManager;
|
||||
|
@ -6,9 +6,12 @@ namespace sdrpp_credits {
|
||||
"aosync",
|
||||
"Alexsey Shestacov",
|
||||
"Benjamin Kyd",
|
||||
"Tobias Mädel",
|
||||
"Cropinghigh",
|
||||
"Howard0su",
|
||||
"Martin Hauke",
|
||||
"Raov",
|
||||
"Howard0su"
|
||||
"Szymon Zakrent",
|
||||
"Tobias Mädel"
|
||||
};
|
||||
|
||||
const char* libraries[] = {
|
||||
|
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include <new_module.h>
|
||||
#include <module.h>
|
||||
|
||||
namespace sdrpp_credits {
|
||||
SDRPP_EXPORT const char* contributors[];
|
||||
|
@ -29,14 +29,13 @@ namespace dsp {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
for (int i = 0; i < count; i++) {
|
||||
out.data[i].l = _in->data[i];
|
||||
out.data[i].r = _in->data[i];
|
||||
out.writeBuf[i].l = _in->readBuf[i];
|
||||
out.writeBuf[i].r = _in->readBuf[i];
|
||||
}
|
||||
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -75,13 +74,12 @@ namespace dsp {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
for (int i = 0; i < count; i++) {
|
||||
out.data[i] = (_in->data[i].l + _in->data[i].r) / 2.0f;
|
||||
out.writeBuf[i] = (_in->readBuf[i].l + _in->readBuf[i].r) / 2.0f;
|
||||
}
|
||||
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#define FL_M_PI 3.1415926535f
|
||||
|
||||
namespace dsp {
|
||||
@ -77,10 +79,10 @@ namespace dsp {
|
||||
}
|
||||
|
||||
virtual void doStop() {
|
||||
for (auto const& in : inputs) {
|
||||
for (auto& in : inputs) {
|
||||
in->stopReader();
|
||||
}
|
||||
for (auto const& out : outputs) {
|
||||
for (auto& out : outputs) {
|
||||
out->stopWriter();
|
||||
}
|
||||
|
||||
@ -88,11 +90,11 @@ namespace dsp {
|
||||
if (workerThread.joinable()) {
|
||||
workerThread.join();
|
||||
}
|
||||
|
||||
for (auto const& in : inputs) {
|
||||
|
||||
for (auto& in : inputs) {
|
||||
in->clearReadStop();
|
||||
}
|
||||
for (auto const& out : outputs) {
|
||||
for (auto& out : outputs) {
|
||||
out->clearWriteStop();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <dsp/block.h>
|
||||
#include <string.h>
|
||||
|
||||
#define RING_BUF_SZ 1000000
|
||||
|
||||
|
@ -31,11 +31,10 @@ namespace dsp {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
memcpy(out.data, _in->data, count * sizeof(complex_t));
|
||||
memcpy(out.writeBuf, _in->readBuf, count * sizeof(complex_t));
|
||||
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -75,11 +74,10 @@ namespace dsp {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
volk_32fc_deinterleave_real_32f(out.data, (lv_32fc_t*)_in->data, count);
|
||||
volk_32fc_deinterleave_real_32f(out.writeBuf, (lv_32fc_t*)_in->readBuf, count);
|
||||
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -119,11 +117,10 @@ namespace dsp {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
volk_32fc_deinterleave_imag_32f(out.data, (lv_32fc_t*)_in->data, count);
|
||||
volk_32fc_deinterleave_imag_32f(out.writeBuf, (lv_32fc_t*)_in->readBuf, count);
|
||||
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
if(!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -135,4 +132,54 @@ namespace dsp {
|
||||
stream<complex_t>* _in;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class RealToComplex : public generic_block<RealToComplex> {
|
||||
public:
|
||||
RealToComplex() {}
|
||||
|
||||
RealToComplex(stream<float>* in) { init(in); }
|
||||
|
||||
~RealToComplex() {
|
||||
delete[] nullBuffer;
|
||||
generic_block<RealToComplex>::stop();
|
||||
}
|
||||
|
||||
void init(stream<float>* in) {
|
||||
_in = in;
|
||||
nullBuffer = new float[STREAM_BUFFER_SIZE];
|
||||
memset(nullBuffer, 0, STREAM_BUFFER_SIZE * sizeof(float));
|
||||
generic_block<RealToComplex>::registerInput(_in);
|
||||
generic_block<RealToComplex>::registerOutput(&out);
|
||||
}
|
||||
|
||||
void setInput(stream<float>* in) {
|
||||
std::lock_guard<std::mutex> lck(generic_block<RealToComplex>::ctrlMtx);
|
||||
generic_block<RealToComplex>::tempStop();
|
||||
generic_block<RealToComplex>::unregisterInput(_in);
|
||||
_in = in;
|
||||
generic_block<RealToComplex>::registerInput(_in);
|
||||
generic_block<RealToComplex>::tempStart();
|
||||
}
|
||||
|
||||
int run() {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
volk_32f_x2_interleave_32fc((lv_32fc_t*)out.writeBuf, _in->readBuf, nullBuffer, count);
|
||||
|
||||
_in->flush();
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
stream<complex_t> out;
|
||||
|
||||
private:
|
||||
float avg;
|
||||
int count;
|
||||
float* nullBuffer;
|
||||
stream<float>* _in;
|
||||
|
||||
};
|
||||
}
|
@ -83,19 +83,17 @@ namespace dsp {
|
||||
// This is somehow faster than volk...
|
||||
|
||||
float diff, currentPhase;
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
for (int i = 0; i < count; i++) {
|
||||
currentPhase = fast_arctan2(_in->data[i].i, _in->data[i].q);
|
||||
currentPhase = fast_arctan2(_in->readBuf[i].i, _in->readBuf[i].q);
|
||||
diff = currentPhase - phase;
|
||||
if (diff > 3.1415926535f) { diff -= 2 * 3.1415926535f; }
|
||||
else if (diff <= -3.1415926535f) { diff += 2 * 3.1415926535f; }
|
||||
out.data[i] = diff / phasorSpeed;
|
||||
out.writeBuf[i] = diff / phasorSpeed;
|
||||
phase = currentPhase;
|
||||
}
|
||||
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -135,19 +133,18 @@ namespace dsp {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
volk_32fc_magnitude_32f(out.data, (lv_32fc_t*)_in->data, count);
|
||||
volk_32fc_magnitude_32f(out.writeBuf, (lv_32fc_t*)_in->readBuf, count);
|
||||
|
||||
_in->flush();
|
||||
|
||||
volk_32f_accumulator_s32f(&avg, out.data, count);
|
||||
volk_32f_accumulator_s32f(&avg, out.writeBuf, count);
|
||||
avg /= (float)count;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
out.data[i] -= avg;
|
||||
out.writeBuf[i] -= avg;
|
||||
}
|
||||
|
||||
out.write(count);
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -259,12 +256,11 @@ namespace dsp {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
volk_32fc_s32fc_x2_rotator_32fc(buffer, (lv_32fc_t*)_in->data, phaseDelta, &phase, count);
|
||||
volk_32fc_deinterleave_real_32f(out.data, buffer, count);
|
||||
volk_32fc_s32fc_x2_rotator_32fc(buffer, (lv_32fc_t*)_in->readBuf, phaseDelta, &phase, count);
|
||||
volk_32fc_deinterleave_real_32f(out.writeBuf, buffer, count);
|
||||
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -52,24 +52,21 @@ namespace dsp {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
memcpy(bufStart, _in->data, count * sizeof(T));
|
||||
memcpy(bufStart, _in->readBuf, count * sizeof(T));
|
||||
_in->flush();
|
||||
|
||||
// Write to output
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
|
||||
if constexpr (std::is_same_v<T, float>) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
volk_32f_x2_dot_prod_32f((float*)&out.data[i], (float*)&buffer[i+1], taps, tapCount);
|
||||
volk_32f_x2_dot_prod_32f((float*)&out.writeBuf[i], (float*)&buffer[i+1], taps, tapCount);
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_same_v<T, complex_t>) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&out.data[i], (lv_32fc_t*)&buffer[i+1], taps, tapCount);
|
||||
volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&out.writeBuf[i], (lv_32fc_t*)&buffer[i+1], taps, tapCount);
|
||||
}
|
||||
}
|
||||
|
||||
out.write(count);
|
||||
if (!out.swap(count)) { return -1; }
|
||||
|
||||
memmove(buffer, &buffer[count], tapCount * sizeof(T));
|
||||
|
||||
@ -135,25 +132,23 @@ namespace dsp {
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
if (bypass) {
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
memcpy(out.data, _in->data, count * sizeof(float));
|
||||
memcpy(out.writeBuf, _in->readBuf, count * sizeof(float));
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
if (isnan(lastOut)) {
|
||||
lastOut = 0.0f;
|
||||
}
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
out.data[0] = (alpha * _in->data[0]) + ((1-alpha) * lastOut);
|
||||
out.writeBuf[0] = (alpha * _in->readBuf[0]) + ((1-alpha) * lastOut);
|
||||
for (int i = 1; i < count; i++) {
|
||||
out.data[i] = (alpha * _in->data[i]) + ((1 - alpha) * out.data[i - 1]);
|
||||
out.writeBuf[i] = (alpha * _in->readBuf[i]) + ((1 - alpha) * out.writeBuf[i - 1]);
|
||||
}
|
||||
lastOut = out.data[count - 1];
|
||||
lastOut = out.writeBuf[count - 1];
|
||||
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -31,17 +31,16 @@ namespace dsp {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
if constexpr (std::is_same_v<T, complex_t> || std::is_same_v<T, stereo_t>) {
|
||||
volk_32fc_x2_add_32fc(out.data, _a->data, _b->data, a_count);
|
||||
volk_32fc_x2_add_32fc(out.writeBuf, _a->readBuf, _b->readBuf, a_count);
|
||||
}
|
||||
else {
|
||||
volk_32f_x2_add_32f(out.data, _a->data, _b->data, a_count);
|
||||
volk_32f_x2_add_32f(out.writeBuf, _a->readBuf, _b->readBuf, a_count);
|
||||
}
|
||||
|
||||
_a->flush();
|
||||
_b->flush();
|
||||
out.write(a_count);
|
||||
if (!out.swap(a_count)) { return -1; }
|
||||
return a_count;
|
||||
}
|
||||
|
||||
@ -82,17 +81,16 @@ namespace dsp {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
if constexpr (std::is_same_v<T, complex_t>) {
|
||||
volk_32fc_x2_multiply_32fc(out.data, _a->data, _b->data, a_count);
|
||||
volk_32fc_x2_multiply_32fc(out.writeBuf, _a->readBuf, _b->readBuf, a_count);
|
||||
}
|
||||
else {
|
||||
volk_32f_x2_multiply_32f(out.data, _a->data, _b->data, a_count);
|
||||
volk_32f_x2_multiply_32f(out.writeBuf, _a->readBuf, _b->readBuf, a_count);
|
||||
}
|
||||
|
||||
_a->flush();
|
||||
_b->flush();
|
||||
out.write(a_count);
|
||||
if (!out.swap(a_count)) { return -1; }
|
||||
return a_count;
|
||||
}
|
||||
|
||||
|
99
core/src/dsp/measure.h
Normal file
99
core/src/dsp/measure.h
Normal file
@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
#include <dsp/block.h>
|
||||
#include <fftw3.h>
|
||||
#include <volk/volk.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <dsp/types.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace dsp {
|
||||
class VolumeMeasure : public generic_block<VolumeMeasure> {
|
||||
public:
|
||||
VolumeMeasure() {}
|
||||
|
||||
VolumeMeasure(stream<stereo_t>* in) { init(in); }
|
||||
|
||||
~VolumeMeasure() {
|
||||
generic_block<VolumeMeasure>::stop();
|
||||
delete[] leftBuf;
|
||||
delete[] rightBuf;
|
||||
}
|
||||
|
||||
void init(stream<stereo_t>* in) {
|
||||
_in = in;
|
||||
leftBuf = new float[STREAM_BUFFER_SIZE];
|
||||
rightBuf = new float[STREAM_BUFFER_SIZE];
|
||||
generic_block<VolumeMeasure>::registerInput(_in);
|
||||
generic_block<VolumeMeasure>::registerOutput(&out);
|
||||
}
|
||||
|
||||
void setInput(stream<stereo_t>* in) {
|
||||
std::lock_guard<std::mutex> lck(generic_block<VolumeMeasure>::ctrlMtx);
|
||||
generic_block<VolumeMeasure>::tempStop();
|
||||
generic_block<VolumeMeasure>::unregisterInput(_in);
|
||||
_in = in;
|
||||
generic_block<VolumeMeasure>::registerInput(_in);
|
||||
generic_block<VolumeMeasure>::tempStart();
|
||||
}
|
||||
|
||||
int run() {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
memcpy(out.writeBuf, _in->readBuf, count * sizeof(stereo_t));
|
||||
volk_32fc_deinterleave_32f_x2(leftBuf, rightBuf, (lv_32fc_t*)_in->readBuf, count);
|
||||
|
||||
_in->flush();
|
||||
if (!out.swap(count)) { return -1; }
|
||||
|
||||
// Get peak from last value
|
||||
float time = (float)count / sampleRate;
|
||||
peak.l -= peakFall * time;
|
||||
peak.r -= peakFall * time;
|
||||
stereo_t _peak;
|
||||
_peak.l = powf(10, peak.l / 10.0f);
|
||||
_peak.r = powf(10, peak.r / 10.0f);
|
||||
|
||||
stereo_t _average;
|
||||
|
||||
// Calculate average
|
||||
volk_32f_s32f_power_32f(leftBuf, leftBuf, 2, count);
|
||||
volk_32f_s32f_power_32f(rightBuf, rightBuf, 2, count);
|
||||
volk_32f_sqrt_32f(leftBuf, leftBuf, count);
|
||||
volk_32f_sqrt_32f(rightBuf, rightBuf, count);
|
||||
volk_32f_accumulator_s32f(&_average.l, leftBuf, count);
|
||||
volk_32f_accumulator_s32f(&_average.r, rightBuf, count);
|
||||
_average.l /= (float)count;
|
||||
_average.r /= (float)count;
|
||||
|
||||
// Calculate peak
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (leftBuf[i] > _peak.l) { _peak.l = leftBuf[i]; }
|
||||
if (rightBuf[i] > _peak.r) { _peak.r = rightBuf[i]; }
|
||||
}
|
||||
|
||||
// Assign
|
||||
peak.l = 10.0f * log10f(_peak.l);
|
||||
peak.r = 10.0f * log10f(_peak.r);
|
||||
average.l = (average.l * (1.0f - avgFilt)) + (10.0f * log10f(_average.l) * avgFilt);
|
||||
average.r = (average.r * (1.0f - avgFilt)) + (10.0f * log10f(_average.r) * avgFilt);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
stream<stereo_t> out;
|
||||
|
||||
stereo_t peak = {0, 0};
|
||||
stereo_t average = {0, 0};
|
||||
|
||||
private:
|
||||
int count;
|
||||
float peakFall = 10.0f; // dB/S
|
||||
float avgFilt = 0.2f; // IIR filter coef
|
||||
float sampleRate = 48000;
|
||||
stream<stereo_t>* _in;
|
||||
|
||||
float* leftBuf;
|
||||
float* rightBuf;
|
||||
};
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include <dsp/block.h>
|
||||
#include <fftw3.h>
|
||||
#include <volk/volk.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <string.h>
|
||||
@ -60,18 +59,16 @@ namespace dsp {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
|
||||
// TODO: Do float xlation
|
||||
if constexpr (std::is_same_v<T, float>) {
|
||||
spdlog::error("XLATOR NOT IMPLEMENTED FOR FLOAT");
|
||||
}
|
||||
if constexpr (std::is_same_v<T, complex_t>) {
|
||||
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)out.data, (lv_32fc_t*)_in->data, phaseDelta, &phase, count);
|
||||
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)out.writeBuf, (lv_32fc_t*)_in->readBuf, phaseDelta, &phase, count);
|
||||
}
|
||||
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -91,13 +88,15 @@ namespace dsp {
|
||||
public:
|
||||
AGC() {}
|
||||
|
||||
AGC(stream<float>* in, float ratio) { init(in, ratio); }
|
||||
AGC(stream<float>* in, float fallRate, float sampleRate) { init(in, fallRate, sampleRate); }
|
||||
|
||||
~AGC() { generic_block<AGC>::stop(); }
|
||||
|
||||
void init(stream<float>* in, float ratio) {
|
||||
void init(stream<float>* in, float fallRate, float sampleRate) {
|
||||
_in = in;
|
||||
_ratio = ratio;
|
||||
_sampleRate = sampleRate;
|
||||
_fallRate = fallRate;
|
||||
_CorrectedFallRate = _fallRate / _sampleRate;
|
||||
generic_block<AGC>::registerInput(_in);
|
||||
generic_block<AGC>::registerOutput(&out);
|
||||
}
|
||||
@ -111,21 +110,32 @@ namespace dsp {
|
||||
generic_block<AGC>::tempStart();
|
||||
}
|
||||
|
||||
void setSampleRate(float sampleRate) {
|
||||
std::lock_guard<std::mutex> lck(generic_block<AGC>::ctrlMtx);
|
||||
_sampleRate = sampleRate;
|
||||
_CorrectedFallRate = _fallRate / _sampleRate;
|
||||
}
|
||||
|
||||
void setFallRate(float fallRate) {
|
||||
std::lock_guard<std::mutex> lck(generic_block<AGC>::ctrlMtx);
|
||||
_fallRate = fallRate;
|
||||
_CorrectedFallRate = _fallRate / _sampleRate;
|
||||
}
|
||||
|
||||
int run() {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
level = pow(10, ((10.0f * log10f(level)) - (_CorrectedFallRate * count)) / 10.0f);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
level = (fabsf(_in->data[i]) * _ratio) + (level * (1.0f - _ratio));
|
||||
out.data[i] = _in->data[i] / level;
|
||||
if (_in->readBuf[i] > level) { level = _in->readBuf[i]; }
|
||||
}
|
||||
|
||||
|
||||
volk_32f_s32f_multiply_32f(out.writeBuf, _in->readBuf, 1.0f / level, count);
|
||||
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -133,8 +143,10 @@ namespace dsp {
|
||||
|
||||
private:
|
||||
int count;
|
||||
float level = 1.0f;
|
||||
float _ratio;
|
||||
float level = 0.0f;
|
||||
float _fallRate;
|
||||
float _CorrectedFallRate;
|
||||
float _sampleRate;
|
||||
stream<float>* _in;
|
||||
|
||||
};
|
||||
@ -185,27 +197,25 @@ namespace dsp {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
|
||||
if (_muted) {
|
||||
if constexpr (std::is_same_v<T, stereo_t>) {
|
||||
memset(out.data, 0, sizeof(stereo_t) * count);
|
||||
memset(out.writeBuf, 0, sizeof(stereo_t) * count);
|
||||
}
|
||||
else {
|
||||
memset(out.data, 0, sizeof(float) * count);
|
||||
memset(out.writeBuf, 0, sizeof(float) * count);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if constexpr (std::is_same_v<T, stereo_t>) {
|
||||
volk_32f_s32f_multiply_32f((float*)out.data, (float*)_in->data, level, count * 2);
|
||||
volk_32f_s32f_multiply_32f((float*)out.writeBuf, (float*)_in->readBuf, level, count * 2);
|
||||
}
|
||||
else {
|
||||
volk_32f_s32f_multiply_32f((float*)out.data, (float*)_in->data, level, count);
|
||||
volk_32f_s32f_multiply_32f((float*)out.writeBuf, (float*)_in->readBuf, level, count);
|
||||
}
|
||||
}
|
||||
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -260,21 +270,20 @@ namespace dsp {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
float sum = 0.0f;
|
||||
volk_32fc_magnitude_32f(normBuffer, (lv_32fc_t*)_in->data, count);
|
||||
volk_32fc_magnitude_32f(normBuffer, (lv_32fc_t*)_in->readBuf, count);
|
||||
volk_32f_accumulator_s32f(&sum, normBuffer, count);
|
||||
sum /= (float)count;
|
||||
|
||||
if (10.0f * log10f(sum) >= _level) {
|
||||
memcpy(out.data, _in->data, count * sizeof(complex_t));
|
||||
memcpy(out.writeBuf, _in->readBuf, count * sizeof(complex_t));
|
||||
}
|
||||
else {
|
||||
memset(out.data, 0, count * sizeof(complex_t));
|
||||
memset(out.writeBuf, 0, count * sizeof(complex_t));
|
||||
}
|
||||
|
||||
_in->flush();
|
||||
out.write(count);
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ namespace dsp {
|
||||
generic_block<PolyphaseResampler<T>>::stop();
|
||||
volk_free(buffer);
|
||||
volk_free(taps);
|
||||
freeTapPhases();
|
||||
}
|
||||
|
||||
void init(stream<T>* in, dsp::filter_window::generic_window* window, float inSampleRate, float outSampleRate) {
|
||||
@ -32,9 +33,10 @@ namespace dsp {
|
||||
taps = (float*)volk_malloc(tapCount * sizeof(float), volk_get_alignment());
|
||||
_window->createTaps(taps, tapCount, _interp);
|
||||
|
||||
buildTapPhases();
|
||||
|
||||
buffer = (T*)volk_malloc(STREAM_BUFFER_SIZE * sizeof(T) * 2, volk_get_alignment());
|
||||
memset(buffer, 0, STREAM_BUFFER_SIZE * sizeof(T) * 2);
|
||||
bufStart = &buffer[tapCount];
|
||||
generic_block<PolyphaseResampler<T>>::registerInput(_in);
|
||||
generic_block<PolyphaseResampler<T>>::registerOutput(&out);
|
||||
}
|
||||
@ -55,6 +57,7 @@ namespace dsp {
|
||||
int _gcd = std::gcd((int)_inSampleRate, (int)_outSampleRate);
|
||||
_interp = _outSampleRate / _gcd;
|
||||
_decim = _inSampleRate / _gcd;
|
||||
buildTapPhases();
|
||||
generic_block<PolyphaseResampler<T>>::tempStart();
|
||||
}
|
||||
|
||||
@ -65,6 +68,7 @@ namespace dsp {
|
||||
int _gcd = std::gcd((int)_inSampleRate, (int)_outSampleRate);
|
||||
_interp = _outSampleRate / _gcd;
|
||||
_decim = _inSampleRate / _gcd;
|
||||
buildTapPhases();
|
||||
generic_block<PolyphaseResampler<T>>::tempStart();
|
||||
}
|
||||
|
||||
@ -84,7 +88,7 @@ namespace dsp {
|
||||
tapCount = window->getTapCount();
|
||||
taps = (float*)volk_malloc(tapCount * sizeof(float), volk_get_alignment());
|
||||
window->createTaps(taps, tapCount, _interp);
|
||||
bufStart = &buffer[tapCount];
|
||||
buildTapPhases();
|
||||
generic_block<PolyphaseResampler<T>>::tempStart();
|
||||
}
|
||||
|
||||
@ -100,37 +104,29 @@ namespace dsp {
|
||||
|
||||
int outCount = calcOutSize(count);
|
||||
|
||||
memcpy(&buffer[tapCount], _in->data, count * sizeof(T));
|
||||
memcpy(&buffer[tapsPerPhase], _in->readBuf, count * sizeof(T));
|
||||
_in->flush();
|
||||
|
||||
// Write to output
|
||||
if (out.aquire() < 0) {
|
||||
return -1;
|
||||
}
|
||||
int outIndex = 0;
|
||||
int _interp_m_1 = _interp - 1;
|
||||
if constexpr (std::is_same_v<T, float>) {
|
||||
for (int i = 0; outIndex < outCount; i += _decim) {
|
||||
out.data[outIndex] = 0;
|
||||
for (int j = i % _interp; j < tapCount; j += _interp) {
|
||||
out.data[outIndex] += buffer[((i - j) / _interp) + tapCount] * taps[j];
|
||||
}
|
||||
int phase = i % _interp;
|
||||
volk_32f_x2_dot_prod_32f(&out.writeBuf[outIndex], &buffer[i / _interp], tapPhases[phase], tapsPerPhase);
|
||||
outIndex++;
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_same_v<T, complex_t>) {
|
||||
for (int i = 0; outIndex < outCount; i += _decim) {
|
||||
out.data[outIndex].i = 0;
|
||||
out.data[outIndex].q = 0;
|
||||
for (int j = i % _interp; j < tapCount; j += _interp) {
|
||||
out.data[outIndex].i += buffer[((i - j) / _interp) + tapCount].i * taps[j];
|
||||
out.data[outIndex].q += buffer[((i - j) / _interp) + tapCount].q * taps[j];
|
||||
}
|
||||
int phase = i % _interp;
|
||||
volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&out.writeBuf[outIndex], (lv_32fc_t*)&buffer[(i / _interp)], tapPhases[phase], tapsPerPhase);
|
||||
outIndex++;
|
||||
}
|
||||
}
|
||||
out.write(outCount);
|
||||
if (!out.swap(outCount)) { return -1; }
|
||||
|
||||
memmove(buffer, &buffer[count], tapCount * sizeof(T));
|
||||
memmove(buffer, &buffer[count], tapsPerPhase * sizeof(T));
|
||||
|
||||
return count;
|
||||
}
|
||||
@ -138,6 +134,44 @@ namespace dsp {
|
||||
stream<T> out;
|
||||
|
||||
private:
|
||||
void buildTapPhases(){
|
||||
if(!taps){
|
||||
return;
|
||||
}
|
||||
|
||||
if(!tapPhases.empty()){
|
||||
freeTapPhases();
|
||||
}
|
||||
|
||||
int phases = _interp;
|
||||
tapsPerPhase = (tapCount+phases-1)/phases; //Integer division ceiling
|
||||
|
||||
bufStart = &buffer[tapsPerPhase];
|
||||
|
||||
for(int i = 0; i < phases; i++){
|
||||
tapPhases.push_back((float*)volk_malloc(tapsPerPhase * sizeof(float), volk_get_alignment()));
|
||||
}
|
||||
|
||||
int currentTap = 0;
|
||||
for(int tap = 0; tap < tapsPerPhase; tap++) {
|
||||
for (int phase = 0; phase < phases; phase++) {
|
||||
if(currentTap < tapCount) {
|
||||
tapPhases[(_interp - 1) - phase][tap] = taps[currentTap++];
|
||||
}
|
||||
else{
|
||||
tapPhases[(_interp - 1) - phase][tap] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void freeTapPhases(){
|
||||
for(auto & tapPhase : tapPhases){
|
||||
volk_free(tapPhase);
|
||||
}
|
||||
tapPhases.clear();
|
||||
}
|
||||
|
||||
int count;
|
||||
stream<T>* _in;
|
||||
|
||||
@ -150,5 +184,84 @@ namespace dsp {
|
||||
float _inSampleRate, _outSampleRate;
|
||||
float* taps;
|
||||
|
||||
int tapsPerPhase;
|
||||
std::vector<float*> tapPhases;
|
||||
|
||||
};
|
||||
|
||||
class PowerDecimator : public generic_block<PowerDecimator> {
|
||||
public:
|
||||
PowerDecimator() {}
|
||||
|
||||
PowerDecimator(stream<complex_t>* in, unsigned int power) { init(in, power); }
|
||||
|
||||
~PowerDecimator() {
|
||||
generic_block<PowerDecimator>::stop();
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* in, unsigned int power) {
|
||||
_in = in;
|
||||
_power = power;
|
||||
generic_block<PowerDecimator>::registerInput(_in);
|
||||
generic_block<PowerDecimator>::registerOutput(&out);
|
||||
}
|
||||
|
||||
void setInput(stream<complex_t>* in) {
|
||||
std::lock_guard<std::mutex> lck(generic_block<PowerDecimator>::ctrlMtx);
|
||||
generic_block<PowerDecimator>::tempStop();
|
||||
generic_block<PowerDecimator>::unregisterInput(_in);
|
||||
_in = in;
|
||||
generic_block<PowerDecimator>::registerInput(_in);
|
||||
generic_block<PowerDecimator>::tempStart();
|
||||
}
|
||||
|
||||
void setPower(unsigned int power) {
|
||||
std::lock_guard<std::mutex> lck(generic_block<PowerDecimator>::ctrlMtx);
|
||||
generic_block<PowerDecimator>::tempStop();
|
||||
generic_block<PowerDecimator>::unregisterInput(_in);
|
||||
_power = power;
|
||||
generic_block<PowerDecimator>::registerInput(_in);
|
||||
generic_block<PowerDecimator>::tempStart();
|
||||
}
|
||||
|
||||
int run() {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
if (_power == 0) {
|
||||
memcpy(out.writeBuf, _in->readBuf, count * sizeof(complex_t));
|
||||
}
|
||||
else if (_power == 1) {
|
||||
for (int j = 0; j < count; j += 2) {
|
||||
out.writeBuf[j / 2].i = (_in->readBuf[j].i + _in->readBuf[j + 1].i) * 0.5f;
|
||||
out.writeBuf[j / 2].q = (_in->readBuf[j].q + _in->readBuf[j + 1].q) * 0.5f;
|
||||
}
|
||||
count /= 2;
|
||||
}
|
||||
|
||||
_in->flush();
|
||||
|
||||
if (_power > 1) {
|
||||
for (int i = 1; i < _power; i++) {
|
||||
for (int j = 0; j < count; j += 2) {
|
||||
out.writeBuf[j / 2].i = (_in->readBuf[j].i + _in->readBuf[j + 1].i) * 0.5f;
|
||||
out.writeBuf[j / 2].q = (_in->readBuf[j].q + _in->readBuf[j + 1].q) * 0.5f;
|
||||
}
|
||||
count /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
stream<complex_t> out;
|
||||
|
||||
|
||||
private:
|
||||
int count;
|
||||
unsigned int _power = 0;
|
||||
stream<complex_t>* _in;
|
||||
|
||||
};
|
||||
}
|
@ -51,9 +51,8 @@ namespace dsp {
|
||||
int count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
for (const auto& stream : out) {
|
||||
if (stream->aquire() < 0) { return -1; }
|
||||
memcpy(stream->data, _in->data, count * sizeof(T));
|
||||
stream->write(count);
|
||||
memcpy(stream->writeBuf, _in->readBuf, count * sizeof(T));
|
||||
if (!stream->swap(count)) { return -1; }
|
||||
}
|
||||
_in->flush();
|
||||
return count;
|
||||
@ -115,7 +114,7 @@ namespace dsp {
|
||||
int run() {
|
||||
int count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
ringBuf.write(_in->data, count);
|
||||
ringBuf.write(_in->readBuf, count);
|
||||
_in->flush();
|
||||
return count;
|
||||
}
|
||||
@ -172,9 +171,8 @@ namespace dsp {
|
||||
}
|
||||
}
|
||||
if (ringBuf.readAndSkip(start, readCount, skip) < 0) { break; };
|
||||
if (out.aquire() < 0) { break; }
|
||||
memcpy(out.data, buf, _keep * sizeof(complex_t));
|
||||
out.write(_keep);
|
||||
memcpy(out.writeBuf, buf, _keep * sizeof(complex_t));
|
||||
if (!out.swap(_keep)) { break; }
|
||||
}
|
||||
delete[] buf;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ namespace dsp {
|
||||
int run() {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
_handler(_in->data, count, _ctx);
|
||||
_handler(_in->readBuf, count, _ctx);
|
||||
_in->flush();
|
||||
return count;
|
||||
}
|
||||
@ -79,7 +79,7 @@ namespace dsp {
|
||||
int run() {
|
||||
count = _in->read();
|
||||
if (count < 0) { return -1; }
|
||||
if (data.write(_in->data, count) < 0) { return -1; }
|
||||
if (data.write(_in->readBuf, count) < 0) { return -1; }
|
||||
_in->flush();
|
||||
return count;
|
||||
}
|
||||
|
@ -55,9 +55,8 @@ namespace dsp {
|
||||
}
|
||||
|
||||
int run() {
|
||||
if (out.aquire() < 0) { return -1; }
|
||||
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)out.data, zeroPhase, phaseDelta, &phase, _blockSize);
|
||||
out.write(_blockSize);
|
||||
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)out.writeBuf, zeroPhase, phaseDelta, &phase, _blockSize);
|
||||
if(!out.swap(_blockSize)) { return -1; }
|
||||
return _blockSize;
|
||||
}
|
||||
|
||||
|
@ -9,100 +9,118 @@
|
||||
namespace dsp {
|
||||
class untyped_steam {
|
||||
public:
|
||||
virtual int aquire() { return -1; }
|
||||
virtual void write(int size) {}
|
||||
virtual bool swap(int size) { return false; }
|
||||
virtual int read() { return -1; }
|
||||
virtual void flush() {}
|
||||
virtual void stopReader() {}
|
||||
virtual void clearReadStop() {}
|
||||
virtual void stopWriter() {}
|
||||
virtual void clearWriteStop() {}
|
||||
virtual void stopReader() {}
|
||||
virtual void clearReadStop() {}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class stream : public untyped_steam {
|
||||
public:
|
||||
stream() {
|
||||
data = (T*)volk_malloc(STREAM_BUFFER_SIZE * sizeof(T), volk_get_alignment());
|
||||
writeBuf = (T*)volk_malloc(STREAM_BUFFER_SIZE * sizeof(T), volk_get_alignment());
|
||||
readBuf = (T*)volk_malloc(STREAM_BUFFER_SIZE * sizeof(T), volk_get_alignment());
|
||||
}
|
||||
|
||||
int aquire() {
|
||||
waitReady();
|
||||
if (writerStop) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
~stream() {
|
||||
volk_free(writeBuf);
|
||||
volk_free(readBuf);
|
||||
}
|
||||
|
||||
void write(int size) {
|
||||
bool swap(int size) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(sigMtx);
|
||||
contentSize = size;
|
||||
// Wait to either swap or stop
|
||||
std::unique_lock<std::mutex> lck(swapMtx);
|
||||
swapCV.wait(lck, [this]{ return (canSwap || writerStop); });
|
||||
|
||||
// If writer was stopped, abandon operation
|
||||
if (writerStop) { return false; }
|
||||
|
||||
// Swap buffers
|
||||
dataSize = size;
|
||||
T* temp = writeBuf;
|
||||
writeBuf = readBuf;
|
||||
readBuf = temp;
|
||||
canSwap = false;
|
||||
}
|
||||
|
||||
// Notify reader that some data is ready
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(rdyMtx);
|
||||
dataReady = true;
|
||||
}
|
||||
cv.notify_one();
|
||||
rdyCV.notify_all();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int read() {
|
||||
waitData();
|
||||
if (readerStop) {
|
||||
return -1;
|
||||
}
|
||||
return contentSize;
|
||||
// Wait for data to be ready or to be stopped
|
||||
std::unique_lock<std::mutex> lck(rdyMtx);
|
||||
rdyCV.wait(lck, [this]{ return (dataReady || readerStop); });
|
||||
|
||||
return (readerStop ? -1 : dataSize);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
// Clear data ready
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(sigMtx);
|
||||
std::lock_guard<std::mutex> lck(rdyMtx);
|
||||
dataReady = false;
|
||||
}
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void stopReader() {
|
||||
// Notify writer that buffers can be swapped
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(sigMtx);
|
||||
readerStop = true;
|
||||
std::lock_guard<std::mutex> lck(swapMtx);
|
||||
canSwap = true;
|
||||
}
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void clearReadStop() {
|
||||
readerStop = false;
|
||||
swapCV.notify_all();
|
||||
}
|
||||
|
||||
void stopWriter() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(sigMtx);
|
||||
std::lock_guard<std::mutex> lck(swapMtx);
|
||||
writerStop = true;
|
||||
}
|
||||
cv.notify_one();
|
||||
swapCV.notify_all();
|
||||
}
|
||||
|
||||
void clearWriteStop() {
|
||||
writerStop = false;
|
||||
}
|
||||
|
||||
T* data;
|
||||
void stopReader() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(rdyMtx);
|
||||
readerStop = true;
|
||||
}
|
||||
rdyCV.notify_all();
|
||||
}
|
||||
|
||||
void clearReadStop() {
|
||||
readerStop = false;
|
||||
}
|
||||
|
||||
T* writeBuf;
|
||||
T* readBuf;
|
||||
|
||||
private:
|
||||
void waitReady() {
|
||||
std::unique_lock<std::mutex> lck(sigMtx);
|
||||
cv.wait(lck, [this]{ return (!dataReady || writerStop); });
|
||||
}
|
||||
std::mutex swapMtx;
|
||||
std::condition_variable swapCV;
|
||||
bool canSwap = true;
|
||||
|
||||
void waitData() {
|
||||
std::unique_lock<std::mutex> lck(sigMtx);
|
||||
cv.wait(lck, [this]{ return (dataReady || readerStop); });
|
||||
}
|
||||
|
||||
std::mutex sigMtx;
|
||||
std::condition_variable cv;
|
||||
std::mutex rdyMtx;
|
||||
std::condition_variable rdyCV;
|
||||
bool dataReady = false;
|
||||
|
||||
bool readerStop = false;
|
||||
bool writerStop = false;
|
||||
|
||||
int contentSize = 0;
|
||||
int dataSize = 0;
|
||||
};
|
||||
}
|
49
core/src/gui/colormaps.cpp
Normal file
49
core/src/gui/colormaps.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include <gui/colormaps.h>
|
||||
#include <filesystem>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <fstream>
|
||||
#include <json.hpp>
|
||||
|
||||
using nlohmann::json;
|
||||
|
||||
namespace colormaps {
|
||||
std::map<std::string, Map> maps;
|
||||
|
||||
void loadMap(std::string path) {
|
||||
if (!std::filesystem::is_regular_file(path)) {
|
||||
spdlog::error("Could not load {0}, file doesn't exist", path);
|
||||
return;
|
||||
}
|
||||
|
||||
std::ifstream file(path.c_str());
|
||||
json data;
|
||||
file >> data;
|
||||
file.close();
|
||||
|
||||
Map map;
|
||||
std::vector<std::string> mapTxt;
|
||||
|
||||
try {
|
||||
map.name = data["name"];
|
||||
map.author = data["author"];
|
||||
mapTxt = data["map"].get<std::vector<std::string>>();
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
spdlog::error("Could not load {0}", path);
|
||||
return;
|
||||
}
|
||||
|
||||
map.entryCount = mapTxt.size();
|
||||
map.map = new float[mapTxt.size() * 3];
|
||||
int i = 0;
|
||||
for(auto const& col : mapTxt) {
|
||||
uint8_t r, g, b, a;
|
||||
map.map[i * 3] = std::stoi(col.substr(1, 2), NULL, 16);
|
||||
map.map[(i * 3) + 1] = std::stoi(col.substr(3, 2), NULL, 16);
|
||||
map.map[(i * 3) + 2] = std::stoi(col.substr(5, 2), NULL, 16);
|
||||
i++;
|
||||
}
|
||||
|
||||
maps[map.name] = map;
|
||||
}
|
||||
}
|
18
core/src/gui/colormaps.h
Normal file
18
core/src/gui/colormaps.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <module.h>
|
||||
#include <map>
|
||||
|
||||
namespace colormaps {
|
||||
struct Map {
|
||||
std::string name;
|
||||
std::string author;
|
||||
float* map;
|
||||
int entryCount;
|
||||
};
|
||||
|
||||
void loadMap(std::string path);
|
||||
|
||||
SDRPP_EXPORT std::map<std::string, Map> maps;
|
||||
}
|
@ -33,19 +33,19 @@ namespace credits {
|
||||
|
||||
ImGui::Text("Contributors");
|
||||
for (int i = 0; i < sdrpp_credits::contributorCount; i++) {
|
||||
ImGui::BulletText(sdrpp_credits::contributors[i]);
|
||||
ImGui::BulletText("%s", sdrpp_credits::contributors[i]);
|
||||
}
|
||||
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Libraries");
|
||||
for (int i = 0; i < sdrpp_credits::libraryCount; i++) {
|
||||
ImGui::BulletText(sdrpp_credits::libraries[i]);
|
||||
ImGui::BulletText("%s", sdrpp_credits::libraries[i]);
|
||||
}
|
||||
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Patrons");
|
||||
for (int i = 0; i < sdrpp_credits::patronCount; i++) {
|
||||
ImGui::BulletText(sdrpp_credits::patrons[i]);
|
||||
ImGui::BulletText("%s", sdrpp_credits::patrons[i]);
|
||||
}
|
||||
|
||||
ImGui::Columns(1, "CreditColumnsEnd", true);
|
||||
|
@ -44,19 +44,19 @@ namespace LoadingScreen {
|
||||
|
||||
ImGui::Text("Contributors");
|
||||
for (int i = 0; i < sdrpp_credits::contributorCount; i++) {
|
||||
ImGui::BulletText(sdrpp_credits::contributors[i]);
|
||||
ImGui::BulletText("%s", sdrpp_credits::contributors[i]);
|
||||
}
|
||||
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Libraries");
|
||||
for (int i = 0; i < sdrpp_credits::libraryCount; i++) {
|
||||
ImGui::BulletText(sdrpp_credits::libraries[i]);
|
||||
ImGui::BulletText("%s", sdrpp_credits::libraries[i]);
|
||||
}
|
||||
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Patrons");
|
||||
for (int i = 0; i < sdrpp_credits::patronCount; i++) {
|
||||
ImGui::BulletText(sdrpp_credits::patrons[i]);
|
||||
ImGui::BulletText("%s", sdrpp_credits::patrons[i]);
|
||||
}
|
||||
|
||||
ImGui::Columns(1, "CreditColumnsEnd", true);
|
||||
@ -64,7 +64,7 @@ namespace LoadingScreen {
|
||||
ImGui::Spacing();
|
||||
ImGui::Spacing();
|
||||
ImGui::Spacing();
|
||||
ImGui::Text(msg.c_str());
|
||||
ImGui::Text("%s", msg.c_str());
|
||||
|
||||
ImGui::EndPopup();
|
||||
ImGui::PopStyleVar(1);
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include <gui/widgets/frequency_select.h>
|
||||
#include <gui/widgets/menu.h>
|
||||
#include <gui/dialogs/loading_screen.h>
|
||||
#include <new_module.h>
|
||||
#include <module.h>
|
||||
|
||||
namespace gui {
|
||||
SDRPP_EXPORT ImGui::WaterFall waterfall;
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <imgui/stb_image.h>
|
||||
#include <filesystem>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace icons {
|
||||
ImTextureID LOGO;
|
||||
@ -31,14 +33,21 @@ namespace icons {
|
||||
return texId;
|
||||
}
|
||||
|
||||
void load() {
|
||||
LOGO = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/sdrpp.png");
|
||||
PLAY = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/play.png");
|
||||
STOP = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/stop.png");
|
||||
MENU = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/menu.png");
|
||||
MUTED = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/muted.png");
|
||||
UNMUTED = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/unmuted.png");
|
||||
NORMAL_TUNING = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/normal_tuning.png");
|
||||
CENTER_TUNING = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/center_tuning.png");
|
||||
bool load(std::string resDir) {
|
||||
if (!std::filesystem::is_directory(resDir)) {
|
||||
spdlog::error("Inavlid resource directory: {0}", resDir);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGO = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/sdrpp.png");
|
||||
PLAY = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/play.png");
|
||||
STOP = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/stop.png");
|
||||
MENU = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/menu.png");
|
||||
MUTED = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/muted.png");
|
||||
UNMUTED = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/unmuted.png");
|
||||
NORMAL_TUNING = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/normal_tuning.png");
|
||||
CENTER_TUNING = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/center_tuning.png");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -14,5 +14,5 @@ namespace icons {
|
||||
extern ImTextureID CENTER_TUNING;
|
||||
|
||||
GLuint loadTexture(std::string path);
|
||||
void load();
|
||||
bool load(std::string resDir);
|
||||
}
|
@ -31,6 +31,7 @@
|
||||
#include <signal_path/source.h>
|
||||
#include <gui/dialogs/loading_screen.h>
|
||||
#include <options.h>
|
||||
#include <gui/colormaps.h>
|
||||
|
||||
// const int FFTSizes[] = {
|
||||
// 65536,
|
||||
@ -60,6 +61,7 @@ fftwf_plan p;
|
||||
float* tempFFT;
|
||||
float* FFTdata;
|
||||
char buf[1024];
|
||||
bool experimentalZoom = false;
|
||||
|
||||
|
||||
|
||||
@ -120,6 +122,8 @@ void windowInit() {
|
||||
|
||||
core::configManager.aquire();
|
||||
gui::menu.order = core::configManager.conf["menuOrder"].get<std::vector<std::string>>();
|
||||
std::string modulesDir = core::configManager.conf["modulesDirectory"];
|
||||
std::string resourcesDir = core::configManager.conf["resourcesDirectory"];
|
||||
core::configManager.release();
|
||||
|
||||
gui::menu.registerEntry("Source", sourecmenu::draw, NULL);
|
||||
@ -144,8 +148,8 @@ void windowInit() {
|
||||
spdlog::info("Loading modules");
|
||||
|
||||
// Load modules from /module directory
|
||||
if (std::filesystem::is_directory(options::opts.root + "/modules")) {
|
||||
for (const auto & file : std::filesystem::directory_iterator(options::opts.root + "/modules")) {
|
||||
if (std::filesystem::is_directory(modulesDir)) {
|
||||
for (const auto & file : std::filesystem::directory_iterator(modulesDir)) {
|
||||
std::string path = file.path().generic_string();
|
||||
if (file.path().extension().generic_string() != SDRPP_MOD_EXTENTSION) {
|
||||
continue;
|
||||
@ -157,7 +161,7 @@ void windowInit() {
|
||||
}
|
||||
}
|
||||
else {
|
||||
spdlog::warn("Module directory {0} does not exist, not loading modules from directory");
|
||||
spdlog::warn("Module directory {0} does not exist, not loading modules from directory", modulesDir);
|
||||
}
|
||||
|
||||
// Read module config
|
||||
@ -180,6 +184,27 @@ void windowInit() {
|
||||
core::moduleManager.createInstance(name, module);
|
||||
}
|
||||
|
||||
// Load color maps
|
||||
LoadingScreen::show("Loading color maps");
|
||||
spdlog::info("Loading color maps");
|
||||
if (std::filesystem::is_directory(resourcesDir + "/colormaps")) {
|
||||
for (const auto & file : std::filesystem::directory_iterator(resourcesDir + "/colormaps")) {
|
||||
std::string path = file.path().generic_string();
|
||||
LoadingScreen::show("Loading " + path);
|
||||
spdlog::info("Loading {0}", path);
|
||||
if (file.path().extension().generic_string() != ".json") {
|
||||
continue;
|
||||
}
|
||||
if (!file.is_regular_file()) { continue; }
|
||||
colormaps::loadMap(path);
|
||||
}
|
||||
}
|
||||
else {
|
||||
spdlog::warn("Color map directory {0} does not exist, not loading modules from directory", modulesDir);
|
||||
}
|
||||
|
||||
gui::waterfall.updatePalletteFromArray(colormaps::maps["Turbo"].map, colormaps::maps["Turbo"].entryCount);
|
||||
|
||||
sourecmenu::init();
|
||||
sinkmenu::init();
|
||||
scriptingmenu::init();
|
||||
@ -275,7 +300,7 @@ void setVFO(double freq) {
|
||||
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);
|
||||
sigpath::vfoManager.setOffset(gui::waterfall.selectedVFO, newVFOOffset);
|
||||
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
|
||||
sigpath::sourceManager.tune(freq - newVFOOffset);
|
||||
return;
|
||||
@ -285,7 +310,7 @@ void setVFO(double freq) {
|
||||
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);
|
||||
sigpath::vfoManager.setOffset(gui::waterfall.selectedVFO, newVFOOffset);
|
||||
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
|
||||
sigpath::sourceManager.tune(freq - newVFOOffset);
|
||||
return;
|
||||
@ -438,7 +463,7 @@ void drawWindow() {
|
||||
|
||||
gui::freqSelect.draw();
|
||||
|
||||
//ImGui::SameLine();
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 9);
|
||||
if (centerTuning) {
|
||||
@ -527,6 +552,7 @@ void drawWindow() {
|
||||
//sigpath::signalPath.setDCBiasCorrection(dcbias.val);
|
||||
}
|
||||
ImGui::Checkbox("Show demo window", &demoWindow);
|
||||
ImGui::Checkbox("Experimental zoom", &experimentalZoom);
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
@ -558,7 +584,7 @@ void drawWindow() {
|
||||
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Zoom").x / 2.0));
|
||||
ImGui::Text("Zoom");
|
||||
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10);
|
||||
if (ImGui::VSliderFloat("##_7_", ImVec2(20.0, 150.0), &bw, gui::waterfall.getBandwidth(), 1000.0, "")) {
|
||||
if (ImGui::VSliderFloat("##_7_", ImVec2(20.0, 150.0), &bw, gui::waterfall.getBandwidth(), 1000.0, "", (experimentalZoom ? 2.0 : 1.0))) {
|
||||
gui::waterfall.setViewBandwidth(bw);
|
||||
if (vfo != NULL) {
|
||||
gui::waterfall.setViewOffset(vfo->centerOffset); // center vfo on screen
|
||||
@ -609,4 +635,8 @@ void drawWindow() {
|
||||
|
||||
void setViewBandwidthSlider(float bandwidth) {
|
||||
bw = bandwidth;
|
||||
}
|
||||
|
||||
bool sdrIsRunning() {
|
||||
return playing;
|
||||
}
|
@ -5,4 +5,5 @@
|
||||
|
||||
void windowInit();
|
||||
void drawWindow();
|
||||
void setViewBandwidthSlider(float bandwidth);
|
||||
void setViewBandwidthSlider(float bandwidth);
|
||||
bool sdrIsRunning();
|
@ -2,21 +2,58 @@
|
||||
#include <imgui.h>
|
||||
#include <gui/gui.h>
|
||||
#include <core.h>
|
||||
#include <gui/colormaps.h>
|
||||
#include <gui/gui.h>
|
||||
|
||||
namespace displaymenu {
|
||||
bool showWaterfall;
|
||||
int colorMapId = 0;
|
||||
std::vector<std::string> colorMapNames;
|
||||
std::string colorMapNamesTxt = "";
|
||||
std::string colorMapAuthor = "";
|
||||
|
||||
void init() {
|
||||
showWaterfall = core::configManager.conf["showWaterfall"];
|
||||
showWaterfall ? gui::waterfall.showWaterfall() : gui::waterfall.hideWaterfall();
|
||||
std::string colormapName = core::configManager.conf["colorMap"];
|
||||
if (colormaps::maps.find(colormapName) != colormaps::maps.end()) {
|
||||
colormaps::Map map = colormaps::maps[colormapName];
|
||||
gui::waterfall.updatePalletteFromArray(map.map, map.entryCount);
|
||||
}
|
||||
|
||||
for (auto const& [name, map] : colormaps::maps) {
|
||||
colorMapNames.push_back(name);
|
||||
colorMapNamesTxt += name;
|
||||
colorMapNamesTxt += '\0';
|
||||
if (name == colormapName) {
|
||||
colorMapId = (colorMapNames.size() - 1);
|
||||
colorMapAuthor = map.author;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw(void* ctx) {
|
||||
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||
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);
|
||||
}
|
||||
|
||||
if (colorMapNames.size() > 0) {
|
||||
ImGui::Text("Color Map");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||
if (ImGui::Combo("##_sdrpp_color_map_sel", &colorMapId, colorMapNamesTxt.c_str())) {
|
||||
colormaps::Map map = colormaps::maps[colorMapNames[colorMapId]];
|
||||
gui::waterfall.updatePalletteFromArray(map.map, map.entryCount);
|
||||
core::configManager.aquire();
|
||||
core::configManager.conf["colorMap"] = colorMapNames[colorMapId];
|
||||
core::configManager.release(true);
|
||||
colorMapAuthor = map.author;
|
||||
}
|
||||
ImGui::Text("Color map Author: %s", colorMapAuthor.c_str());
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,8 @@
|
||||
#include <imgui.h>
|
||||
#include <gui/gui.h>
|
||||
#include <core.h>
|
||||
#include <gui/main_window.h>
|
||||
#include <gui/style.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
|
||||
namespace sourecmenu {
|
||||
@ -33,6 +35,8 @@ namespace sourecmenu {
|
||||
items += '\0';
|
||||
}
|
||||
float itemWidth = ImGui::GetContentRegionAvailWidth();
|
||||
|
||||
if (sdrIsRunning()) { style::beginDisabled(); }
|
||||
|
||||
ImGui::SetNextItemWidth(itemWidth);
|
||||
if (ImGui::Combo("##source", &sourceId, items.c_str())) {
|
||||
@ -42,7 +46,10 @@ namespace sourecmenu {
|
||||
core::configManager.release(true);
|
||||
}
|
||||
|
||||
if (sdrIsRunning()) { style::endDisabled(); }
|
||||
|
||||
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);
|
||||
|
@ -3,13 +3,20 @@
|
||||
#include <imgui_internal.h>
|
||||
#include <config.h>
|
||||
#include <options.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <filesystem>
|
||||
|
||||
namespace style {
|
||||
ImFont* baseFont;
|
||||
ImFont* bigFont;
|
||||
ImFont* hugeFont;
|
||||
|
||||
void setDefaultStyle() {
|
||||
bool setDefaultStyle(std::string resDir) {
|
||||
if (!std::filesystem::is_directory(resDir)) {
|
||||
spdlog::error("Inavlid resource directory: {0}", resDir);
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGui::GetStyle().WindowRounding = 0.0f;
|
||||
ImGui::GetStyle().ChildRounding = 0.0f;
|
||||
ImGui::GetStyle().FrameRounding = 0.0f;
|
||||
@ -17,19 +24,26 @@ namespace style {
|
||||
ImGui::GetStyle().PopupRounding = 0.0f;
|
||||
ImGui::GetStyle().ScrollbarRounding = 0.0f;
|
||||
|
||||
baseFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 16.0f);
|
||||
bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 42.0f);
|
||||
hugeFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 128.0f);
|
||||
baseFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 16.0f);
|
||||
bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 45.0f);
|
||||
hugeFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 128.0f);
|
||||
|
||||
ImGui::StyleColorsDark();
|
||||
//ImGui::StyleColorsLight();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void testtt() {
|
||||
ImGui::StyleColorsLight();
|
||||
}
|
||||
|
||||
void setDarkStyle() {
|
||||
bool setDarkStyle(std::string resDir) {
|
||||
if (!std::filesystem::is_directory(resDir)) {
|
||||
spdlog::error("Inavlid resource directory: {0}", resDir);
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGui::GetStyle().WindowRounding = 0.0f;
|
||||
ImGui::GetStyle().ChildRounding = 0.0f;
|
||||
ImGui::GetStyle().FrameRounding = 0.0f;
|
||||
@ -37,9 +51,9 @@ namespace style {
|
||||
ImGui::GetStyle().PopupRounding = 0.0f;
|
||||
ImGui::GetStyle().ScrollbarRounding = 0.0f;
|
||||
|
||||
baseFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 16.0f);
|
||||
bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 42.0f);
|
||||
hugeFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 128.0f);
|
||||
baseFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 16.0f);
|
||||
bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 45.0f);
|
||||
hugeFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 128.0f);
|
||||
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
@ -89,6 +103,8 @@ namespace style {
|
||||
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
||||
colors[ImGuiCol_NavHighlight] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
|
||||
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void beginDisabled() {
|
||||
|
@ -1,13 +1,14 @@
|
||||
#pragma once
|
||||
#include <imgui.h>
|
||||
#include <string>
|
||||
|
||||
namespace style {
|
||||
extern ImFont* baseFont;
|
||||
extern ImFont* bigFont;
|
||||
extern ImFont* hugeFont;
|
||||
|
||||
void setDefaultStyle();
|
||||
void setDarkStyle();
|
||||
bool setDefaultStyle(std::string resDir);
|
||||
bool setDarkStyle(std::string resDir);
|
||||
void beginDisabled();
|
||||
void endDisabled();
|
||||
void testtt();
|
||||
|
@ -108,20 +108,7 @@ namespace bandplan {
|
||||
}
|
||||
}
|
||||
|
||||
void loadColorTable(std::string path) {
|
||||
if (!std::filesystem::exists(path)) {
|
||||
spdlog::error("Band Plan Color Table file does not exist");
|
||||
return;
|
||||
}
|
||||
if (!std::filesystem::is_regular_file(path)) {
|
||||
spdlog::error("Band Plan Color Table file isn't a file...");
|
||||
return;
|
||||
}
|
||||
std::ifstream file(path.c_str());
|
||||
json data;
|
||||
file >> data;
|
||||
file.close();
|
||||
|
||||
colorTable = data.get<std::map<std::string, BandPlanColor_t>>();
|
||||
void loadColorTable(json table) {
|
||||
colorTable = table.get<std::map<std::string, BandPlanColor_t>>();
|
||||
}
|
||||
};
|
@ -38,7 +38,7 @@ namespace bandplan {
|
||||
|
||||
void loadBandPlan(std::string path);
|
||||
void loadFromDir(std::string path);
|
||||
void loadColorTable(std::string path);
|
||||
void loadColorTable(json table);
|
||||
|
||||
extern std::map<std::string, BandPlan_t> bandplans;
|
||||
extern std::vector<std::string> bandplanNames;
|
||||
|
@ -2,6 +2,11 @@
|
||||
#include <config.h>
|
||||
#include <gui/style.h>
|
||||
|
||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif
|
||||
#include <imgui/imgui_internal.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;
|
||||
}
|
||||
@ -18,17 +23,20 @@ void FrequencySelect::init() {
|
||||
}
|
||||
|
||||
void FrequencySelect::onPosChange() {
|
||||
int digitHeight = ImGui::CalcTextSize("0").y;
|
||||
ImVec2 digitSz = ImGui::CalcTextSize("0");
|
||||
ImVec2 commaSz = ImGui::CalcTextSize(".");
|
||||
int digitHeight = digitSz.y;
|
||||
int digitWidth = digitSz.x;
|
||||
int commaOffset = 0;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
digitTopMins[i] = ImVec2(widgetPos.x + (i * 22) + commaOffset, widgetPos.y);
|
||||
digitBottomMins[i] = ImVec2(widgetPos.x + (i * 22) + commaOffset, widgetPos.y + (digitHeight / 2));
|
||||
digitTopMins[i] = ImVec2(widgetPos.x + (i * digitWidth) + commaOffset, widgetPos.y);
|
||||
digitBottomMins[i] = ImVec2(widgetPos.x + (i * digitWidth) + commaOffset, widgetPos.y + (digitHeight / 2));
|
||||
|
||||
digitTopMaxs[i] = ImVec2(widgetPos.x + (i * 22) + commaOffset + 22, widgetPos.y + (digitHeight / 2));
|
||||
digitBottomMaxs[i] = ImVec2(widgetPos.x + (i * 22) + commaOffset + 22, widgetPos.y + digitHeight);
|
||||
digitTopMaxs[i] = ImVec2(widgetPos.x + (i * digitWidth) + commaOffset + digitWidth, widgetPos.y + (digitHeight / 2));
|
||||
digitBottomMaxs[i] = ImVec2(widgetPos.x + (i * digitWidth) + commaOffset + digitWidth, widgetPos.y + digitHeight);
|
||||
|
||||
if ((i + 1) % 3 == 0 && i < 11) {
|
||||
commaOffset += 12;
|
||||
commaOffset += commaSz.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -90,18 +98,25 @@ void FrequencySelect::draw() {
|
||||
ImU32 disabledColor = ImGui::GetColorU32(ImGuiCol_Text, 0.3f);
|
||||
ImU32 textColor = ImGui::GetColorU32(ImGuiCol_Text);
|
||||
|
||||
ImVec2 digitSz = ImGui::CalcTextSize("0");
|
||||
ImVec2 commaSz = ImGui::CalcTextSize(".");
|
||||
int digitHeight = digitSz.y;
|
||||
int digitWidth = digitSz.x;
|
||||
int commaOffset = 0;
|
||||
bool zeros = true;
|
||||
|
||||
ImGui::ItemSize(ImRect(digitTopMins[0], ImVec2(digitBottomMaxs[11].x + 15, digitBottomMaxs[11].y)));
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
if (digits[i] != 0) {
|
||||
zeros = false;
|
||||
}
|
||||
sprintf(buf, "%d", digits[i]);
|
||||
window->DrawList->AddText(ImVec2(widgetPos.x + (i * 22) + commaOffset, widgetPos.y),
|
||||
window->DrawList->AddText(ImVec2(widgetPos.x + (i * digitWidth) + commaOffset, widgetPos.y),
|
||||
zeros ? disabledColor : textColor, buf);
|
||||
if ((i + 1) % 3 == 0 && i < 11) {
|
||||
commaOffset += 12;
|
||||
window->DrawList->AddText(ImVec2(widgetPos.x + (i * 22) + commaOffset + 10, widgetPos.y),
|
||||
commaOffset += commaSz.x;
|
||||
window->DrawList->AddText(ImVec2(widgetPos.x + (i * digitWidth) + commaOffset + 11, widgetPos.y),
|
||||
zeros ? disabledColor : textColor, ".");
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <new_module.h>
|
||||
#include <module.h>
|
||||
|
||||
class Menu {
|
||||
public:
|
||||
|
23
core/src/gui/widgets/stepped_slider.cpp
Normal file
23
core/src/gui/widgets/stepped_slider.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#include <gui/widgets/stepped_slider.h>
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
|
||||
namespace ImGui {
|
||||
bool SliderFloatWithSteps(const char* label, float* v, float v_min, float v_max, float v_step, const char* display_format) {
|
||||
if (!display_format) {
|
||||
display_format = "%.3f";
|
||||
}
|
||||
|
||||
char text_buf[64] = {};
|
||||
ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), display_format, *v);
|
||||
|
||||
// Map from [v_min,v_max] to [0,N]
|
||||
const int countValues = int((v_max-v_min)/v_step);
|
||||
int v_i = int((*v - v_min)/v_step);
|
||||
const bool value_changed = ImGui::SliderInt(label, &v_i, 0, countValues, text_buf);
|
||||
|
||||
// Remap from [0,N] to [v_min,v_max]
|
||||
*v = v_min + float(v_i) * v_step;
|
||||
return value_changed;
|
||||
}
|
||||
}
|
5
core/src/gui/widgets/stepped_slider.h
Normal file
5
core/src/gui/widgets/stepped_slider.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace ImGui {
|
||||
bool SliderFloatWithSteps(const char* label, float* v, float v_min, float v_max, float v_step, const char* display_format = "%.3f");
|
||||
}
|
53
core/src/gui/widgets/volume_meter.cpp
Normal file
53
core/src/gui/widgets/volume_meter.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
#include <gui/widgets/volume_meter.h>
|
||||
#include <algorithm>
|
||||
|
||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif
|
||||
#include <imgui/imgui_internal.h>
|
||||
|
||||
namespace ImGui {
|
||||
void VolumeMeter(float avg, float peak, float val_min, float val_max, const ImVec2& size_arg) {
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
ImGuiStyle& style = GImGui->Style;
|
||||
|
||||
avg = std::clamp<float>(avg, val_min, val_max);
|
||||
peak = std::clamp<float>(peak, val_min, val_max);
|
||||
|
||||
float pad = style.FramePadding.y;
|
||||
|
||||
ImVec2 min = window->DC.CursorPos;
|
||||
ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (GImGui->FontSize / 2) + style.FramePadding.y);
|
||||
ImRect bb(min, min + size);
|
||||
|
||||
float lineHeight = size.y;
|
||||
|
||||
ItemSize(size, style.FramePadding.y);
|
||||
if (!ItemAdd(bb, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
float zeroDb = roundf(((-val_min) / (val_max - val_min)) * size.x);
|
||||
|
||||
window->DrawList->AddRectFilled(min, min + ImVec2(zeroDb, lineHeight), IM_COL32( 0, 255, 0, 127 ));
|
||||
window->DrawList->AddRectFilled(min + ImVec2(zeroDb, 0), min + ImVec2(size.x, lineHeight), IM_COL32( 255, 0, 0, 127 ));
|
||||
|
||||
float end = roundf(((avg - val_min) / (val_max - val_min)) * size.x);
|
||||
float endP = roundf(((peak - val_min) / (val_max - val_min)) * size.x);
|
||||
|
||||
if (avg <= 0) {
|
||||
window->DrawList->AddRectFilled(min, min + ImVec2(end, lineHeight), IM_COL32( 0, 255, 0, 255 ));
|
||||
}
|
||||
else {
|
||||
window->DrawList->AddRectFilled(min, min + ImVec2(zeroDb, lineHeight), IM_COL32( 0, 255, 0, 255 ));
|
||||
window->DrawList->AddRectFilled(min + ImVec2(zeroDb, 0), min + ImVec2(end, lineHeight), IM_COL32( 255, 0, 0, 255 ));
|
||||
}
|
||||
|
||||
if (peak <= 0) {
|
||||
window->DrawList->AddLine(min + ImVec2(endP, -1), min + ImVec2(endP, lineHeight - 1), IM_COL32( 127, 255, 127, 255 ));
|
||||
}
|
||||
else {
|
||||
window->DrawList->AddLine(min + ImVec2(endP, -1), min + ImVec2(endP, lineHeight - 1), IM_COL32( 255, 127, 127, 255 ));
|
||||
}
|
||||
}
|
||||
}
|
6
core/src/gui/widgets/volume_meter.h
Normal file
6
core/src/gui/widgets/volume_meter.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
namespace ImGui {
|
||||
void VolumeMeter(float avg, float peak, float val_min, float val_max, const ImVec2& size_arg = ImVec2(0, 0));
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
float COLOR_MAP[][3] = {
|
||||
float DEFAULT_COLOR_MAP[][3] = {
|
||||
{0x00, 0x00, 0x20},
|
||||
{0x00, 0x00, 0x30},
|
||||
{0x00, 0x00, 0x50},
|
||||
@ -106,7 +106,7 @@ namespace ImGui {
|
||||
viewBandwidth = 1.0;
|
||||
wholeBandwidth = 1.0;
|
||||
|
||||
updatePallette(COLOR_MAP, 13);
|
||||
updatePallette(DEFAULT_COLOR_MAP, 13);
|
||||
}
|
||||
|
||||
void WaterFall::init() {
|
||||
@ -347,6 +347,7 @@ namespace ImGui {
|
||||
cPos = widgetPos.x + 50 + ((center - lowerFreq) * horizScale);
|
||||
width = bPos - aPos;
|
||||
txtSz = ImGui::CalcTextSize(bandplan->bands[i].name.c_str());
|
||||
float height = txtSz.y * 2.5f;
|
||||
if (bandplan::colorTable.find(bandplan->bands[i].type.c_str()) != bandplan::colorTable.end()) {
|
||||
color = bandplan::colorTable[bandplan->bands[i].type].colorValue;
|
||||
colorTrans = bandplan::colorTable[bandplan->bands[i].type].transColorValue;
|
||||
@ -362,19 +363,19 @@ namespace ImGui {
|
||||
bPos = widgetPos.x + 51;
|
||||
}
|
||||
if (width >= 1.0) {
|
||||
window->DrawList->AddRectFilled(ImVec2(roundf(aPos), widgetPos.y + fftHeight - 25),
|
||||
window->DrawList->AddRectFilled(ImVec2(roundf(aPos), widgetPos.y + fftHeight + 10 - height),
|
||||
ImVec2(roundf(bPos), widgetPos.y + fftHeight + 10), colorTrans);
|
||||
if (startVis) {
|
||||
window->DrawList->AddLine(ImVec2(roundf(aPos), widgetPos.y + fftHeight - 26),
|
||||
window->DrawList->AddLine(ImVec2(roundf(aPos), widgetPos.y + fftHeight + 10 - height - 1),
|
||||
ImVec2(roundf(aPos), widgetPos.y + fftHeight + 9), color);
|
||||
}
|
||||
if (endVis) {
|
||||
window->DrawList->AddLine(ImVec2(roundf(bPos), widgetPos.y + fftHeight - 26),
|
||||
window->DrawList->AddLine(ImVec2(roundf(bPos), widgetPos.y + fftHeight + 10 - height - 1),
|
||||
ImVec2(roundf(bPos), widgetPos.y + fftHeight + 9), color);
|
||||
}
|
||||
}
|
||||
if (txtSz.x <= width) {
|
||||
window->DrawList->AddText(ImVec2(cPos - (txtSz.x / 2.0), widgetPos.y + fftHeight - 17),
|
||||
window->DrawList->AddText(ImVec2(cPos - (txtSz.x / 2.0), widgetPos.y + fftHeight + 10 - (height / 2.0f) - (txtSz.y / 2.0f)),
|
||||
IM_COL32(255, 255, 255, 255), bandplan->bands[i].name.c_str());
|
||||
}
|
||||
}
|
||||
@ -537,11 +538,14 @@ namespace ImGui {
|
||||
float* WaterFall::getFFTBuffer() {
|
||||
if (rawFFTs == NULL) { return NULL; }
|
||||
buf_mtx.lock();
|
||||
currentFFTLine--;
|
||||
fftLines++;
|
||||
currentFFTLine = ((currentFFTLine + waterfallHeight) % waterfallHeight);
|
||||
fftLines = std::min<float>(fftLines, waterfallHeight);
|
||||
return &rawFFTs[currentFFTLine * rawFFTSize];
|
||||
if (waterfallVisible) {
|
||||
currentFFTLine--;
|
||||
fftLines++;
|
||||
currentFFTLine = ((currentFFTLine + waterfallHeight) % waterfallHeight);
|
||||
fftLines = std::min<float>(fftLines, waterfallHeight);
|
||||
return &rawFFTs[currentFFTLine * rawFFTSize];
|
||||
}
|
||||
return rawFFTs;
|
||||
}
|
||||
|
||||
void WaterFall::pushFFT() {
|
||||
@ -550,9 +554,10 @@ namespace ImGui {
|
||||
int drawDataSize = (viewBandwidth / wholeBandwidth) * rawFFTSize;
|
||||
int drawDataStart = (((double)rawFFTSize / 2.0) * (offsetRatio + 1)) - (drawDataSize / 2);
|
||||
|
||||
doZoom(drawDataStart, drawDataSize, dataWidth, &rawFFTs[currentFFTLine * rawFFTSize], latestFFT);
|
||||
|
||||
|
||||
if (waterfallVisible) {
|
||||
doZoom(drawDataStart, drawDataSize, dataWidth, &rawFFTs[currentFFTLine * rawFFTSize], latestFFT);
|
||||
memmove(&waterfallFb[dataWidth], waterfallFb, dataWidth * (waterfallHeight - 1) * sizeof(uint32_t));
|
||||
float pixel;
|
||||
float dataRange = waterfallMax - waterfallMin;
|
||||
@ -563,11 +568,16 @@ namespace ImGui {
|
||||
}
|
||||
waterfallUpdate = true;
|
||||
}
|
||||
else {
|
||||
doZoom(drawDataStart, drawDataSize, dataWidth, rawFFTs, latestFFT);
|
||||
fftLines = 1;
|
||||
}
|
||||
|
||||
buf_mtx.unlock();
|
||||
}
|
||||
|
||||
void WaterFall::updatePallette(float colors[][3], int colorCount) {
|
||||
std::lock_guard<std::mutex> lck(buf_mtx);
|
||||
for (int i = 0; i < WATERFALL_RESOLUTION; i++) {
|
||||
int lowerId = floorf(((float)i / (float)WATERFALL_RESOLUTION) * colorCount);
|
||||
int upperId = ceilf(((float)i / (float)WATERFALL_RESOLUTION) * colorCount);
|
||||
@ -579,6 +589,23 @@ namespace ImGui {
|
||||
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;
|
||||
}
|
||||
updateWaterfallFb();
|
||||
}
|
||||
|
||||
void WaterFall::updatePalletteFromArray(float* colors, int colorCount) {
|
||||
std::lock_guard<std::mutex> lck(buf_mtx);
|
||||
for (int i = 0; i < WATERFALL_RESOLUTION; i++) {
|
||||
int lowerId = floorf(((float)i / (float)WATERFALL_RESOLUTION) * colorCount);
|
||||
int upperId = ceilf(((float)i / (float)WATERFALL_RESOLUTION) * colorCount);
|
||||
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 * 3) + 0] * (1.0 - ratio)) + (colors[(upperId * 3) + 0] * (ratio));
|
||||
float g = (colors[(lowerId * 3) + 1] * (1.0 - ratio)) + (colors[(upperId * 3) + 1] * (ratio));
|
||||
float b = (colors[(lowerId * 3) + 2] * (1.0 - ratio)) + (colors[(upperId * 3) + 2] * (ratio));
|
||||
waterfallPallet[i] = ((uint32_t)255 << 24) | ((uint32_t)b << 16) | ((uint32_t)g << 8) | (uint32_t)r;
|
||||
}
|
||||
updateWaterfallFb();
|
||||
}
|
||||
|
||||
void WaterFall::autoRange() {
|
||||
@ -627,6 +654,7 @@ namespace ImGui {
|
||||
}
|
||||
|
||||
void WaterFall::setViewBandwidth(double bandWidth) {
|
||||
std::lock_guard<std::mutex> lck(buf_mtx);
|
||||
if (bandWidth == viewBandwidth) {
|
||||
return;
|
||||
}
|
||||
@ -651,6 +679,7 @@ namespace ImGui {
|
||||
}
|
||||
|
||||
void WaterFall::setViewOffset(double offset) {
|
||||
std::lock_guard<std::mutex> lck(buf_mtx);
|
||||
if (offset == viewOffset) {
|
||||
return;
|
||||
}
|
||||
@ -690,6 +719,7 @@ namespace ImGui {
|
||||
}
|
||||
|
||||
void WaterFall::setWaterfallMin(float min) {
|
||||
std::lock_guard<std::mutex> lck(buf_mtx);
|
||||
if (min == waterfallMin) {
|
||||
return;
|
||||
}
|
||||
@ -702,6 +732,7 @@ namespace ImGui {
|
||||
}
|
||||
|
||||
void WaterFall::setWaterfallMax(float max) {
|
||||
std::lock_guard<std::mutex> lck(buf_mtx);
|
||||
if (max == waterfallMax) {
|
||||
return;
|
||||
}
|
||||
@ -720,16 +751,17 @@ namespace ImGui {
|
||||
}
|
||||
|
||||
void WaterFall::setRawFFTSize(int size, bool lock) {
|
||||
if (lock) { buf_mtx.lock(); }
|
||||
std::lock_guard<std::mutex> lck(buf_mtx);
|
||||
rawFFTSize = size;
|
||||
if (rawFFTs != NULL) {
|
||||
rawFFTs = (float*)realloc(rawFFTs, rawFFTSize * waterfallHeight * sizeof(float));
|
||||
int wfSize = std::max<int>(1, waterfallHeight);
|
||||
rawFFTs = (float*)realloc(rawFFTs, rawFFTSize * wfSize * sizeof(float));
|
||||
}
|
||||
else {
|
||||
rawFFTs = (float*)malloc(rawFFTSize * waterfallHeight * sizeof(float));
|
||||
int wfSize = std::max<int>(1, waterfallHeight);
|
||||
rawFFTs = (float*)malloc(rawFFTSize * wfSize * sizeof(float));
|
||||
}
|
||||
memset(rawFFTs, 0, rawFFTSize * waterfallHeight * sizeof(float));
|
||||
if (lock) { buf_mtx.unlock(); }
|
||||
}
|
||||
|
||||
void WaterfallVFO::setOffset(double offset) {
|
||||
@ -845,15 +877,17 @@ namespace ImGui {
|
||||
};
|
||||
|
||||
void WaterFall::showWaterfall() {
|
||||
waterfallVisible = true;
|
||||
buf_mtx.lock();
|
||||
waterfallVisible = true;
|
||||
onResize();
|
||||
memset(rawFFTs, 0, waterfallHeight * rawFFTSize * sizeof(float));
|
||||
updateWaterfallFb();
|
||||
buf_mtx.unlock();
|
||||
}
|
||||
|
||||
void WaterFall::hideWaterfall() {
|
||||
waterfallVisible = false;
|
||||
buf_mtx.lock();
|
||||
waterfallVisible = false;
|
||||
onResize();
|
||||
buf_mtx.unlock();
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ namespace ImGui {
|
||||
void pushFFT();
|
||||
|
||||
void updatePallette(float colors[][3], int colorCount);
|
||||
void updatePalletteFromArray(float* colors, int colorCount);
|
||||
|
||||
void setCenterFrequency(double freq);
|
||||
double getCenterFrequency();
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include <new_module.h>
|
||||
#include <module.h>
|
||||
#include <filesystem>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <options.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace options {
|
||||
CMDLineOptions opts;
|
||||
@ -8,7 +9,8 @@ namespace options {
|
||||
#ifdef _WIN32
|
||||
opts.root = ".";
|
||||
#else
|
||||
opts.root = "~/.sdrpp/";
|
||||
std::string homedir = getenv("HOME");
|
||||
opts.root = homedir + "/.config/sdrpp";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <new_module.h>
|
||||
#include <module.h>
|
||||
|
||||
namespace options {
|
||||
struct CMDLineOptions {
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include <signal_path/vfo_manager.h>
|
||||
#include <signal_path/source.h>
|
||||
#include <signal_path/sink.h>
|
||||
#include <new_module.h>
|
||||
#include <module.h>
|
||||
|
||||
namespace sigpath {
|
||||
SDRPP_EXPORT SignalPath signalPath;
|
||||
|
@ -16,6 +16,7 @@ public:
|
||||
|
||||
class Sink {
|
||||
public:
|
||||
virtual ~Sink() {}
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual void menuHandler() = 0;
|
||||
|
@ -1,3 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#define VERSION_STR "0.2.5_alpha"
|
||||
#define VERSION_STR "0.2.5_beta"
|
@ -80,7 +80,7 @@ private:
|
||||
static void worker(void* ctx) {
|
||||
FileSourceModule* _this = (FileSourceModule*)ctx;
|
||||
double sampleRate = _this->reader->getSampleRate();
|
||||
int blockSize = sampleRate / 200.0;
|
||||
int blockSize = sampleRate / 200.0f;
|
||||
int16_t* inBuf = new int16_t[blockSize * 2];
|
||||
|
||||
while (true) {
|
||||
|
31
hackrf_source/CMakeLists.txt
Normal file
31
hackrf_source/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(hackrf_source)
|
||||
|
||||
if (MSVC)
|
||||
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive")
|
||||
endif (MSVC)
|
||||
|
||||
include_directories("src/")
|
||||
|
||||
file(GLOB SRC "src/*.cpp")
|
||||
|
||||
add_library(hackrf_source SHARED ${SRC})
|
||||
target_link_libraries(hackrf_source PRIVATE sdrpp_core)
|
||||
set_target_properties(hackrf_source PROPERTIES PREFIX "")
|
||||
|
||||
if (MSVC)
|
||||
# Lib path
|
||||
target_link_directories(sdrpp_core PUBLIC "C:/Program Files/PothosSDR/bin/")
|
||||
|
||||
target_link_libraries(hackrf_source PUBLIC hackrf)
|
||||
else (MSVC)
|
||||
find_package(PkgConfig)
|
||||
|
||||
pkg_check_modules(LIBHACKRF REQUIRED libhackrf)
|
||||
|
||||
target_include_directories(hackrf_source PUBLIC ${LIBHACKRF_INCLUDE_DIRS})
|
||||
target_link_directories(hackrf_source PUBLIC ${LIBHACKRF_LIBRARY_DIRS})
|
||||
target_link_libraries(hackrf_source PUBLIC ${LIBHACKRF_LIBRARIES})
|
||||
endif (MSVC)
|
277
hackrf_source/src/main.cpp
Normal file
277
hackrf_source/src/main.cpp
Normal file
@ -0,0 +1,277 @@
|
||||
#include <imgui.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <module.h>
|
||||
#include <gui/gui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <core.h>
|
||||
#include <gui/style.h>
|
||||
#include <config.h>
|
||||
#include <libhackrf/hackrf.h>
|
||||
|
||||
#pragma optimize( "", off )
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
SDRPP_MOD_INFO {
|
||||
/* Name: */ "hackrf_source",
|
||||
/* Description: */ "HackRF source module for SDR++",
|
||||
/* Author: */ "Ryzerth",
|
||||
/* Version: */ 0, 1, 0,
|
||||
/* Max instances */ 1
|
||||
};
|
||||
|
||||
//ConfigManager config;
|
||||
|
||||
const char* AGG_MODES_STR = "Off\0Low\0High\0";
|
||||
|
||||
const char* sampleRatesTxt = "20MHz\00016MHz\00010MHz\0008MHz\0005MHz\0004MHz\0002MHz\000";
|
||||
|
||||
const int sampleRates[] = {
|
||||
20000000,
|
||||
16000000,
|
||||
10000000,
|
||||
8000000,
|
||||
5000000,
|
||||
4000000,
|
||||
2000000,
|
||||
};
|
||||
|
||||
class HackRFSourceModule : public ModuleManager::Instance {
|
||||
public:
|
||||
HackRFSourceModule(std::string name) {
|
||||
this->name = name;
|
||||
|
||||
hackrf_init();
|
||||
|
||||
sampleRate = 2000000;
|
||||
|
||||
handler.ctx = this;
|
||||
handler.selectHandler = menuSelected;
|
||||
handler.deselectHandler = menuDeselected;
|
||||
handler.menuHandler = menuHandler;
|
||||
handler.startHandler = start;
|
||||
handler.stopHandler = stop;
|
||||
handler.tuneHandler = tune;
|
||||
handler.stream = &stream;
|
||||
|
||||
refresh();
|
||||
|
||||
selectFirst();
|
||||
|
||||
// config.aquire();
|
||||
// std::string serString = config.conf["device"];
|
||||
// config.release();
|
||||
|
||||
sigpath::sourceManager.registerSource("HackRF", &handler);
|
||||
}
|
||||
|
||||
~HackRFSourceModule() {
|
||||
hackrf_exit();
|
||||
}
|
||||
|
||||
|
||||
void enable() {
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
void disable() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
bool isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
devList.clear();
|
||||
devListTxt = "";
|
||||
|
||||
uint64_t serials[256];
|
||||
hackrf_device_list_t* _devList = hackrf_device_list();
|
||||
|
||||
for (int i = 0; i < _devList->devicecount; i++) {
|
||||
devList.push_back(_devList->serial_numbers[i]);
|
||||
devListTxt += (char*)(_devList->serial_numbers[i] + 16);
|
||||
devListTxt += '\0';
|
||||
}
|
||||
|
||||
hackrf_device_list_free(_devList);
|
||||
}
|
||||
|
||||
void selectFirst() {
|
||||
if (devList.size() != 0) {
|
||||
selectedSerial = devList[0];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static void menuSelected(void* ctx) {
|
||||
HackRFSourceModule* _this = (HackRFSourceModule*)ctx;
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
spdlog::info("HackRFSourceModule '{0}': Menu Select!", _this->name);
|
||||
}
|
||||
|
||||
static void menuDeselected(void* ctx) {
|
||||
HackRFSourceModule* _this = (HackRFSourceModule*)ctx;
|
||||
spdlog::info("HackRFSourceModule '{0}': Menu Deselect!", _this->name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void start(void* ctx) {
|
||||
HackRFSourceModule* _this = (HackRFSourceModule*)ctx;
|
||||
if (_this->running) {
|
||||
return;
|
||||
}
|
||||
if (_this->selectedSerial == "") {
|
||||
spdlog::error("Tried to start HackRF source with empty serial");
|
||||
return;
|
||||
}
|
||||
|
||||
int err = hackrf_open_by_serial(_this->selectedSerial.c_str(), &_this->openDev);
|
||||
if (err != 0) {
|
||||
spdlog::error("Could not open HackRF {0}", _this->selectedSerial);
|
||||
return;
|
||||
}
|
||||
|
||||
hackrf_set_sample_rate(_this->openDev, _this->sampleRate);
|
||||
hackrf_set_baseband_filter_bandwidth(_this->openDev, hackrf_compute_baseband_filter_bw(_this->sampleRate));
|
||||
hackrf_set_freq(_this->openDev, _this->freq);
|
||||
|
||||
hackrf_set_amp_enable(_this->openDev, _this->amp);
|
||||
hackrf_set_lna_gain(_this->openDev, _this->lna);
|
||||
hackrf_set_vga_gain(_this->openDev, _this->lna);
|
||||
|
||||
hackrf_start_rx(_this->openDev, callback, _this);
|
||||
|
||||
_this->running = true;
|
||||
spdlog::info("HackRFSourceModule '{0}': Start!", _this->name);
|
||||
}
|
||||
|
||||
static void stop(void* ctx) {
|
||||
HackRFSourceModule* _this = (HackRFSourceModule*)ctx;
|
||||
if (!_this->running) {
|
||||
return;
|
||||
}
|
||||
_this->running = false;
|
||||
_this->stream.stopWriter();
|
||||
// TODO: Stream stop
|
||||
hackrf_close(_this->openDev);
|
||||
_this->stream.clearWriteStop();
|
||||
spdlog::info("HackRFSourceModule '{0}': Stop!", _this->name);
|
||||
}
|
||||
|
||||
static void tune(double freq, void* ctx) {
|
||||
HackRFSourceModule* _this = (HackRFSourceModule*)ctx;
|
||||
if (_this->running) {
|
||||
hackrf_set_freq(_this->openDev, freq);
|
||||
}
|
||||
_this->freq = freq;
|
||||
spdlog::info("HackRFSourceModule '{0}': Tune: {1}!", _this->name, freq);
|
||||
}
|
||||
|
||||
static void menuHandler(void* ctx) {
|
||||
HackRFSourceModule* _this = (HackRFSourceModule*)ctx;
|
||||
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||
|
||||
if (_this->running) { style::beginDisabled(); }
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth);
|
||||
if (ImGui::Combo(CONCAT("##_hackrf_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) {
|
||||
_this->selectedSerial = _this->devList[_this->devId];
|
||||
}
|
||||
|
||||
if (ImGui::Combo(CONCAT("##_hackrf_sr_sel_", _this->name), &_this->srId, sampleRatesTxt)) {
|
||||
_this->sampleRate = sampleRates[_this->srId];
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX();
|
||||
if (ImGui::Button(CONCAT("Refresh##_hackrf_refr_", _this->name), ImVec2(refreshBtnWdith, 0))) {
|
||||
_this->refresh();
|
||||
}
|
||||
|
||||
if (_this->running) { style::endDisabled(); }
|
||||
|
||||
ImGui::Text("Amp Enabled");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Checkbox(CONCAT("##_hackrf_amp_", _this->name), &_this->amp)) {
|
||||
if (_this->running) {
|
||||
hackrf_set_amp_enable(_this->openDev, _this->amp);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text("LNA Gain");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SliderInt(CONCAT("##_hackrf_lna_", _this->name), &_this->lna, 0, 40)) {
|
||||
_this->lna = (_this->lna / 8) * 8;
|
||||
if (_this->running) {
|
||||
hackrf_set_lna_gain(_this->openDev, _this->lna);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text("LNA Gain");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SliderInt(CONCAT("##_hackrf_vga_", _this->name), &_this->vga, 0, 62)) {
|
||||
_this->vga = (_this->vga / 2) * 2;
|
||||
if (_this->running) {
|
||||
hackrf_set_vga_gain(_this->openDev, _this->lna);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int callback(hackrf_transfer* transfer) {
|
||||
HackRFSourceModule* _this = (HackRFSourceModule*)transfer->rx_ctx;
|
||||
int count = transfer->valid_length / 2;
|
||||
int8_t* buffer = (int8_t*)transfer->buffer;
|
||||
for (int i = 0; i < count; i++) {
|
||||
_this->stream.writeBuf[i].i = (float)buffer[i * 2] / 128.0f;
|
||||
_this->stream.writeBuf[i].q = (float)buffer[(i * 2) + 1] / 128.0f;
|
||||
}
|
||||
if (!_this->stream.swap(count)) { return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
hackrf_device* openDev;
|
||||
bool enabled = true;
|
||||
dsp::stream<dsp::complex_t> stream;
|
||||
int sampleRate;
|
||||
SourceManager::SourceHandler handler;
|
||||
bool running = false;
|
||||
double freq;
|
||||
std::string selectedSerial = "";
|
||||
int devId = 0;
|
||||
int srId = 0;
|
||||
bool amp = false;
|
||||
int lna = 0;
|
||||
int vga = 0;
|
||||
|
||||
std::vector<std::string> devList;
|
||||
std::string devListTxt;
|
||||
};
|
||||
|
||||
MOD_EXPORT void _INIT_() {
|
||||
// config.setPath(ROOT_DIR "/airspyhf_config.json");
|
||||
// json defConf;
|
||||
// defConf["device"] = "";
|
||||
// defConf["devices"] = json::object();
|
||||
// config.load(defConf);
|
||||
// config.enableAutoSave();
|
||||
}
|
||||
|
||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||
return new HackRFSourceModule(name);
|
||||
}
|
||||
|
||||
MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
|
||||
delete (HackRFSourceModule*)instance;
|
||||
}
|
||||
|
||||
MOD_EXPORT void _END_() {
|
||||
// config.disableAutoSave();
|
||||
// config.save();
|
||||
}
|
||||
|
||||
#pragma optimize( "", on )
|
49
make_debian_package.sh
Normal file
49
make_debian_package.sh
Normal file
@ -0,0 +1,49 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Create directory structure
|
||||
echo Create directory structure
|
||||
mkdir sdrpp_debian_amd64
|
||||
mkdir sdrpp_debian_amd64/DEBIAN
|
||||
mkdir sdrpp_debian_amd64/usr
|
||||
mkdir sdrpp_debian_amd64/usr/bin
|
||||
mkdir sdrpp_debian_amd64/usr/share
|
||||
mkdir sdrpp_debian_amd64/usr/share/sdrpp
|
||||
mkdir sdrpp_debian_amd64/usr/lib
|
||||
mkdir sdrpp_debian_amd64/usr/lib/sdrpp
|
||||
mkdir sdrpp_debian_amd64/usr/lib/sdrpp/plugins
|
||||
|
||||
# Create package info
|
||||
echo Create package info
|
||||
echo Package: sdrpp >> sdrpp_debian_amd64/DEBIAN/control
|
||||
echo Version: 0.2.5 >> sdrpp_debian_amd64/DEBIAN/control
|
||||
echo Maintainer: Ryzerth >> sdrpp_debian_amd64/DEBIAN/control
|
||||
echo Architecture: all >> sdrpp_debian_amd64/DEBIAN/control
|
||||
echo Description: Bloat-free SDR receiver software >> sdrpp_debian_amd64/DEBIAN/control
|
||||
|
||||
# Copy core files
|
||||
echo Copy core files
|
||||
cp $1/sdrpp sdrpp_debian_amd64/usr/bin/
|
||||
cp $1/libsdrpp_core.so sdrpp_debian_amd64/usr/lib/
|
||||
|
||||
# Copy reasources
|
||||
echo Copy reasources
|
||||
cp -r root/res/* sdrpp_debian_amd64/usr/share/sdrpp/
|
||||
|
||||
# Copy module
|
||||
echo Copy modules
|
||||
cp $1/radio/radio.so sdrpp_debian_amd64/usr/lib/sdrpp/plugins/
|
||||
cp $1/recorder/recorder.so sdrpp_debian_amd64/usr/lib/sdrpp/plugins/
|
||||
cp $1/airspyhf_source/airspyhf_source.so sdrpp_debian_amd64/usr/lib/sdrpp/plugins/
|
||||
cp $1/airspy_source/airspy_source.so sdrpp_debian_amd64/usr/lib/sdrpp/plugins/
|
||||
cp $1/plutosdr_source/plutosdr_source.so sdrpp_debian_amd64/usr/lib/sdrpp/plugins/
|
||||
cp $1/rtl_tcp_source/rtl_tcp_source.so sdrpp_debian_amd64/usr/lib/sdrpp/plugins/
|
||||
cp $1/soapy_source/soapy_source.so sdrpp_debian_amd64/usr/lib/sdrpp/plugins/
|
||||
cp $1/audio_sink/audio_sink.so sdrpp_debian_amd64/usr/lib/sdrpp/plugins/
|
||||
|
||||
# Create package
|
||||
echo Create packagesudo
|
||||
dpkg-deb --build sdrpp_debian_amd64
|
||||
|
||||
# Cleanup
|
||||
echo Cleanup
|
||||
rm -rf sdrpp_debian_amd64
|
34
make_windows_package.ps1
Normal file
34
make_windows_package.ps1
Normal file
@ -0,0 +1,34 @@
|
||||
mkdir sdrpp_windows_x64
|
||||
|
||||
# Copy root
|
||||
cp -Recurse root/* sdrpp_windows_x64/
|
||||
|
||||
# Copy core
|
||||
cp build/Release/* sdrpp_windows_x64/
|
||||
cp 'C:/Program Files/PothosSDR/bin/volk.dll' sdrpp_windows_x64/
|
||||
|
||||
# Copy modules
|
||||
cp build/radio/Release/radio.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp build/recorder/Release/recorder.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp build/airspyhf_source/Release/airspyhf_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/airspyhf.dll' sdrpp_windows_x64/
|
||||
|
||||
cp build/airspy_source/Release/airspy_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/airspy.dll' sdrpp_windows_x64/
|
||||
|
||||
cp build/plutosdr_source/Release/plutosdr_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/libiio.dll' sdrpp_windows_x64/
|
||||
cp 'C:/Program Files/PothosSDR/bin/libad9361.dll' sdrpp_windows_x64/
|
||||
|
||||
cp build/rtl_tcp_source/Release/rtl_tcp_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp build/soapy_source/Release/soapy_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp build/audio_sink/Release/audio_sink.dll sdrpp_windows_x64/modules/
|
||||
cp build/audio_sink/Release/portaudio.dll sdrpp_windows_x64/
|
||||
|
||||
Compress-Archive -Path sdrpp_windows_x64/ -DestinationPath sdrpp_windows_x64.zip
|
||||
|
||||
rm -Force -Recurse sdrpp_windows_x64
|
@ -25,8 +25,8 @@ if (MSVC)
|
||||
else (MSVC)
|
||||
find_package(PkgConfig)
|
||||
|
||||
pkg_check_modules(SOAPY REQUIRED libiio)
|
||||
pkg_check_modules(SOAPY REQUIRED libad9361)
|
||||
pkg_check_modules(LIBIIO REQUIRED libiio)
|
||||
pkg_check_modules(LIBAD9361 REQUIRED libad9361)
|
||||
|
||||
target_include_directories(plutosdr_source PUBLIC ${LIBIIO_INCLUDE_DIRS})
|
||||
target_link_directories(plutosdr_source PUBLIC ${LIBIIO_LIBRARY_DIRS})
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include <imgui.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <new_module.h>
|
||||
#include <module.h>
|
||||
#include <gui/gui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <core.h>
|
||||
@ -205,7 +205,7 @@ private:
|
||||
|
||||
static void worker(void* ctx) {
|
||||
PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx;
|
||||
int blockSize = _this->sampleRate / 200.0;
|
||||
int blockSize = _this->sampleRate / 200.0f;
|
||||
|
||||
struct iio_channel *rx0_i, *rx0_q;
|
||||
struct iio_buffer *rxbuf;
|
||||
@ -229,12 +229,11 @@ private:
|
||||
|
||||
int16_t* buf = (int16_t*)iio_buffer_first(rxbuf, rx0_i);
|
||||
|
||||
if (_this->stream.aquire() < 0) { break; }
|
||||
for (int i = 0; i < blockSize; i++) {
|
||||
_this->stream.data[i].q = (float)buf[i * 2] / 32768.0f;
|
||||
_this->stream.data[i].i = (float)buf[(i * 2) + 1] / 32768.0f;
|
||||
_this->stream.writeBuf[i].q = (float)buf[i * 2] / 32768.0f;
|
||||
_this->stream.writeBuf[i].i = (float)buf[(i * 2) + 1] / 32768.0f;
|
||||
}
|
||||
_this->stream.write(blockSize);
|
||||
if (!_this->stream.swap(blockSize)) { break; };
|
||||
}
|
||||
|
||||
iio_buffer_destroy(rxbuf);
|
||||
|
@ -42,7 +42,7 @@ public:
|
||||
|
||||
demod.init(&squelch.out);
|
||||
|
||||
agc.init(&demod.out, 1.0f / 125.0f);
|
||||
agc.init(&demod.out, 20.0f, bbSampRate);
|
||||
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
win.init(audioBW, audioBW, bbSampRate);
|
||||
@ -151,6 +151,11 @@ private:
|
||||
void setBandwidth(float bandWidth) {
|
||||
bw = bandWidth;
|
||||
_vfo->setBandwidth(bw);
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
win.setCutoff(audioBW);
|
||||
win.setTransWidth(audioBW);
|
||||
resamp.updateWindow(&win);
|
||||
}
|
||||
|
||||
void setSnapInterval(float snapInt) {
|
||||
|
@ -53,7 +53,7 @@ public:
|
||||
|
||||
c2r.init(&xlator.out);
|
||||
|
||||
agc.init(&c2r.out, 1.0f / 125.0f);
|
||||
agc.init(&c2r.out, 20.0f, audioSampRate);
|
||||
|
||||
m2s.init(&agc.out);
|
||||
}
|
||||
@ -103,11 +103,9 @@ public:
|
||||
xlator.stop();
|
||||
}
|
||||
audioSampRate = sampleRate;
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
agc.setSampleRate(audioSampRate);
|
||||
resamp.setOutSampleRate(audioSampRate);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
win.setCutoff(audioBW);
|
||||
win.setTransWidth(audioBW);
|
||||
resamp.updateWindow(&win);
|
||||
xlator.setSampleRate(audioSampRate);
|
||||
if (running) {
|
||||
|
@ -42,7 +42,7 @@ public:
|
||||
|
||||
demod.init(&squelch.out, bbSampRate, bandWidth, dsp::SSBDemod::MODE_DSB);
|
||||
|
||||
agc.init(&demod.out, 1.0f / 125.0f);
|
||||
agc.init(&demod.out, 20.0f, bbSampRate);
|
||||
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
win.init(audioBW, audioBW, bbSampRate);
|
||||
|
@ -42,7 +42,7 @@ public:
|
||||
|
||||
demod.init(&squelch.out, bbSampRate, bandWidth, dsp::SSBDemod::MODE_LSB);
|
||||
|
||||
agc.init(&demod.out, 1.0f / 125.0f);
|
||||
agc.init(&demod.out, 20.0f, bbSampRate);
|
||||
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw);
|
||||
win.init(audioBW, audioBW, bbSampRate);
|
||||
@ -151,6 +151,12 @@ private:
|
||||
void setBandwidth(float bandWidth) {
|
||||
bw = bandWidth;
|
||||
_vfo->setBandwidth(bw);
|
||||
demod.setBandWidth(bw);
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
win.setCutoff(audioBW);
|
||||
win.setTransWidth(audioBW);
|
||||
resamp.updateWindow(&win);
|
||||
}
|
||||
|
||||
void setSnapInterval(float snapInt) {
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <gui/style.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <radio_demod.h>
|
||||
#include <new_module.h>
|
||||
#include <module.h>
|
||||
#include <wfm_demod.h>
|
||||
#include <fm_demod.h>
|
||||
#include <am_demod.h>
|
||||
|
@ -42,7 +42,7 @@ public:
|
||||
|
||||
demod.init(&squelch.out, bbSampRate, bandWidth, dsp::SSBDemod::MODE_USB);
|
||||
|
||||
agc.init(&demod.out, 1.0f / 125.0f);
|
||||
agc.init(&demod.out, 20.0f, bbSampRate);
|
||||
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw);
|
||||
win.init(audioBW, audioBW, bbSampRate);
|
||||
@ -151,6 +151,12 @@ private:
|
||||
void setBandwidth(float bandWidth) {
|
||||
bw = bandWidth;
|
||||
_vfo->setBandwidth(bw);
|
||||
demod.setBandWidth(bw);
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
win.setCutoff(audioBW);
|
||||
win.setTransWidth(audioBW);
|
||||
resamp.updateWindow(&win);
|
||||
}
|
||||
|
||||
void setSnapInterval(float snapInt) {
|
||||
|
56
readme.md
56
readme.md
@ -3,6 +3,8 @@
|
||||

|
||||
SDR++ is a cross-platform and open source SDR software with the aim of being bloat free and simple to use.
|
||||
|
||||

|
||||
|
||||
* [Patreon](https://patreon.com/ryzerth)
|
||||
* [Discord Server](https://discord.gg/aFgWjyD)
|
||||
|
||||
@ -29,7 +31,15 @@ Download the latest release from [the Releases page](https://github.com/Alexandr
|
||||
To create a desktop short, rightclick the exe and select `Send to -> Desktop (create shortcut)`, then, rename the shortcut on the desktop to whatever you want.
|
||||
|
||||
## Linux
|
||||
TODO
|
||||
Download the latest release from [the Releases page](https://github.com/AlexandreRouma/SDRPlusPlus/releases) and extract to the directory of your choice.
|
||||
|
||||
Then, run:
|
||||
```sh
|
||||
sudo apt install libfftw3-dev libglfw3-dev libglew-dev libvolk2-dev libsoapysdr-dev libairspyhf-dev libiio-dev libad9361-dev portaudio19-dev libhackrf-dev
|
||||
sudo dpkg -i sdrpp_debian_amd64.deb
|
||||
```
|
||||
|
||||
If `libvolk2-dev` is not available, use `libvolk1-dev`.
|
||||
|
||||
## MacOS
|
||||
TODO
|
||||
@ -79,6 +89,15 @@ You will first need to edit the `root_dev/config` file to point to the modules t
|
||||
...
|
||||
```
|
||||
|
||||
You also need to change the location of the resource and module directories, for development, I recommend:
|
||||
```json
|
||||
...
|
||||
"modulesDirectory": "../root_dev/modules",
|
||||
...
|
||||
"resourcesDirectory": "../root_dev/res",
|
||||
...
|
||||
```
|
||||
|
||||
Remember that these paths will be relative to the run directory.
|
||||
|
||||
Off cours, remember to add entries for all modules that were built and that you wish to use.
|
||||
@ -115,7 +134,6 @@ The modules built will be some of the following (Repeat the instructions above f
|
||||
# Building on Linux / BSD
|
||||
## Install dependencies
|
||||
* cmake
|
||||
* vcpkg
|
||||
* fftw3
|
||||
* glfw
|
||||
* glew
|
||||
@ -127,6 +145,8 @@ Next install dependencies based on the modules you wish to build:
|
||||
* plutosdr_source: libiio, libad9361
|
||||
* audio_sink: portaudio
|
||||
|
||||
Note: make sure you're using GCC 8 or later as older versions do not have `std::filesystem` built-in.
|
||||
|
||||
## Building
|
||||
replace `<N>` with the number of threads you wish to use to build
|
||||
```sh
|
||||
@ -144,7 +164,12 @@ sh ./create_root.sh
|
||||
## Running for development
|
||||
If you wish to install SDR++, skip to the next step
|
||||
|
||||
You will first need to edit the `root_dev/config` file to point to the modules that were built. Here us a sample if what it would look like:
|
||||
First run SDR++ from the build directory to generate a default config file
|
||||
```
|
||||
./sdrpp -r ../root_dev/
|
||||
```
|
||||
|
||||
Then, you need to edit the `root_dev/config` file to point to the modules that were built. Here us a sample if what it would look like:
|
||||
|
||||
```json
|
||||
...
|
||||
@ -158,18 +183,27 @@ You will first need to edit the `root_dev/config` file to point to the modules t
|
||||
...
|
||||
```
|
||||
|
||||
You also need to change the location of the resource and module directories, for development, I recommend:
|
||||
```json
|
||||
...
|
||||
"modulesDirectory": "./root_dev/modules",
|
||||
...
|
||||
"resourcesDirectory": "./root_dev/res",
|
||||
...
|
||||
```
|
||||
|
||||
Remember that these paths will be relative to the run directory.
|
||||
|
||||
Off cours, remember to add entries for all modules that were built and that you wish to use.
|
||||
|
||||
Next, from the top directory, you can simply run:
|
||||
```
|
||||
./build/Release/sdrpp.exe -r root_dev
|
||||
./build/sdrpp -r root_dev
|
||||
```
|
||||
|
||||
Or, if you wish to run from the build directory:
|
||||
Or, if you wish to run from the build directory, you need to correct directories in config.json, and:
|
||||
```
|
||||
./Release/sdrpp.exe -r ../root_dev
|
||||
./sdrpp -r ../root_dev
|
||||
```
|
||||
|
||||
## Installing SDR++
|
||||
@ -190,13 +224,17 @@ I will soon publish a contributing.md listing the code style to use.
|
||||
* [aosync](https://github.com/aosync)
|
||||
* [Alexsey Shestacov](https://github.com/wingrime)
|
||||
* [Benjamin Kyd](https://github.com/benkyd)
|
||||
* [Tobias Mädel](https://github.com/Manawyrm)
|
||||
* [Raov](https://twitter.com/raov_birbtog)
|
||||
* [cropinghigh](https://github.com/cropinghigh)
|
||||
* [Howard0su](https://github.com/howard0su)
|
||||
* [Martin Hauke](https://github.com/mnhauke)
|
||||
* [Raov](https://twitter.com/raov_birbtog)
|
||||
* [Szymon Zakrent](https://github.com/zakrent)
|
||||
* [Tobias Mädel](https://github.com/Manawyrm)
|
||||
|
||||
|
||||
## Libaries used
|
||||
* [SoapySDR (PothosWare)](https://github.com/pothosware/SoapySDR)
|
||||
* [Dear ImGui (ocornut)](https://github.com/ocornut/imgui)
|
||||
* [spdlog (gabime)](https://github.com/gabime/spdlog)
|
||||
* [json (nlohmann)](https://github.com/nlohmann/json)
|
||||
* [portaudio (PortAudio community)](http://www.portaudio.com/)
|
||||
* [portaudio (PortAudio community)](http://www.portaudio.com/)
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include <imgui.h>
|
||||
#include <new_module.h>
|
||||
#include <module.h>
|
||||
#include <watcher.h>
|
||||
#include <wav.h>
|
||||
#include <dsp/types.h>
|
||||
@ -13,7 +13,6 @@
|
||||
#include <gui/style.h>
|
||||
#include <regex>
|
||||
#include <options.h>
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
SDRPP_MOD_INFO {
|
||||
@ -254,8 +253,8 @@ private:
|
||||
int count = _this->audioStream->read();
|
||||
if (count < 0) { break; }
|
||||
for (int i = 0; i < count; i++) {
|
||||
sampleBuf[(i * 2) + 0] = _this->audioStream->data[i].l * 0x7FFF;
|
||||
sampleBuf[(i * 2) + 1] = _this->audioStream->data[i].r * 0x7FFF;
|
||||
sampleBuf[(i * 2) + 0] = _this->audioStream->readBuf[i].l * 512;
|
||||
sampleBuf[(i * 2) + 1] = _this->audioStream->readBuf[i].r * 512;
|
||||
}
|
||||
_this->audioStream->flush();
|
||||
_this->samplesWritten += count;
|
||||
@ -270,8 +269,8 @@ private:
|
||||
int count = _this->iqStream->read();
|
||||
if (count < 0) { break; }
|
||||
for (int i = 0; i < count; i++) {
|
||||
sampleBuf[(i * 2) + 0] = _this->iqStream->data[i].q * 0x7FFF;
|
||||
sampleBuf[(i * 2) + 1] = _this->iqStream->data[i].i * 0x7FFF;
|
||||
sampleBuf[(i * 2) + 0] = _this->iqStream->readBuf[i].q * 0x7FFF;
|
||||
sampleBuf[(i * 2) + 1] = _this->iqStream->readBuf[i].i * 0x7FFF;
|
||||
}
|
||||
_this->iqStream->flush();
|
||||
_this->samplesWritten += count;
|
||||
@ -310,6 +309,14 @@ MOD_EXPORT void _INIT_() {
|
||||
config.setPath(options::opts.root + "/recorder_config.json");
|
||||
config.load(def);
|
||||
config.enableAutoSave();
|
||||
|
||||
// Create default recording directory
|
||||
if (!std::filesystem::exists(options::opts.root + "/recordings")) {
|
||||
spdlog::warn("Recordings directory does not exist, creating it");
|
||||
if (!std::filesystem::create_directory(options::opts.root + "/recordings")) {
|
||||
spdlog::error("Could not create recordings directory");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"broadcast": "#0000FFFF",
|
||||
"amateur": "#FF0000FF",
|
||||
"aviation": "#00FF00FF",
|
||||
"marine": "#00FFFFFF",
|
||||
"military": "#FFFF00FF"
|
||||
}
|
87
root/res/bandplans/germany-mobile-lte-bands.json
Normal file
87
root/res/bandplans/germany-mobile-lte-bands.json
Normal file
@ -0,0 +1,87 @@
|
||||
{
|
||||
"name": "German LTE bands",
|
||||
"country_name": "Germany",
|
||||
"country_code": "DE",
|
||||
"author_name": "Martin Hauke",
|
||||
"author_url": "none",
|
||||
"bands": [
|
||||
{
|
||||
"name": "LTE band 1 (IMT) FDD uplink",
|
||||
"type": "LTE.FDD.uplink",
|
||||
"start": 1920000000,
|
||||
"end": 1980000000
|
||||
},
|
||||
{
|
||||
"name": "LTE band 1 (IMT) FDD downlink",
|
||||
"type": "LTE.FDD.downlink",
|
||||
"start": 2110000000,
|
||||
"end": 2170000000
|
||||
},
|
||||
{
|
||||
"name": "LTE band 3 (DCS) FDD uplink",
|
||||
"type": "LTE.FDD.uplink",
|
||||
"start": 1710000000,
|
||||
"end": 1785000000
|
||||
},
|
||||
{
|
||||
"name": "LTE band 3 (DCS) FDD downlink",
|
||||
"type": "LTE.FDD.downlink",
|
||||
"start": 1805000000,
|
||||
"end": 1880000000
|
||||
},
|
||||
{
|
||||
"name": "LTE band 7 (IMT-E) FDD uplink",
|
||||
"type": "LTE.FDD.uplink",
|
||||
"start": 2500000000,
|
||||
"end": 2570000000
|
||||
},
|
||||
{
|
||||
"name": "LTE band 7 (IMT-E) FDD downlink",
|
||||
"type": "LTE.FDD.downlink",
|
||||
"start": 2620000000,
|
||||
"end": 2690000000
|
||||
},
|
||||
{
|
||||
"name": "LTE band 8 (Extended GSM) FDD uplink",
|
||||
"type": "LTE.FDD.uplink",
|
||||
"start": 880000000,
|
||||
"end": 915000000
|
||||
},
|
||||
{
|
||||
"name": "LTE band 8 (Extended GSM) FDD downlink",
|
||||
"type": "LTE.FDD.downlink",
|
||||
"start": 9250000000,
|
||||
"end": 9600000000
|
||||
},
|
||||
{
|
||||
"name": "LTE band 20 (Digital Dividend) FDD uplink",
|
||||
"type": "LTE.FDD.uplink",
|
||||
"start": 832000000,
|
||||
"end": 862000000
|
||||
},
|
||||
{
|
||||
"name": "LTE band 20 (Digital Dividend) FDD downlink",
|
||||
"type": "LTE.FDD.downlink",
|
||||
"start": 7910000000,
|
||||
"end": 8210000000
|
||||
},
|
||||
{
|
||||
"name": "LTE band 28 (APT) FDD uplink",
|
||||
"type": "LTE.FDD.uplink",
|
||||
"start": 703000000,
|
||||
"end": 748000000
|
||||
},
|
||||
{
|
||||
"name": "LTE band 28 (APT) FDD downlink",
|
||||
"type": "LTE.FDD.downlink",
|
||||
"start": 7580000000,
|
||||
"end": 8030000000
|
||||
},
|
||||
{
|
||||
"name": "LTE band 32 (L-Band (EU)) SDL downlink",
|
||||
"type": "LTE.SDL",
|
||||
"start": 14520000000,
|
||||
"end": 14960000000
|
||||
}
|
||||
]
|
||||
}
|
321
root/res/bandplans/germany-mobile-networks.json
Normal file
321
root/res/bandplans/germany-mobile-networks.json
Normal file
@ -0,0 +1,321 @@
|
||||
{
|
||||
"name": "German Mobile Networks",
|
||||
"country_name": "Germany",
|
||||
"country_code": "DE",
|
||||
"author_name": "Martin Hauke",
|
||||
"author_url": "none",
|
||||
"bands": [
|
||||
{
|
||||
"name": "DVB-T2",
|
||||
"type": "broadcast",
|
||||
"start": 470000000,
|
||||
"end": 694000000
|
||||
},
|
||||
{
|
||||
"name": "703 Telefonica FDD uplink",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 703000000,
|
||||
"end": 713000000
|
||||
},
|
||||
{
|
||||
"name": "713 Telekom FDD uplink",
|
||||
"type": "mobile.mno.telekom",
|
||||
"start": 713000000,
|
||||
"end": 723000000
|
||||
},
|
||||
{
|
||||
"name": "723 Vodafone FDD uplink",
|
||||
"type": "mobile.mno.vodafone",
|
||||
"start": 723000000,
|
||||
"end": 733000000
|
||||
},
|
||||
{
|
||||
"name": "758 Telefonica FDD downlink",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 758000000,
|
||||
"end": 768000000
|
||||
},
|
||||
{
|
||||
"name": "768 Telekom FDD downlink",
|
||||
"type": "mobile.mno.telekom",
|
||||
"start": 768000000,
|
||||
"end": 778000000
|
||||
},
|
||||
{
|
||||
"name": "778 Vodafone FDD downlink",
|
||||
"type": "mobile.mno.vodafone",
|
||||
"start": 778000000,
|
||||
"end": 788000000
|
||||
},
|
||||
{
|
||||
"name": "791 Telefonica FDD downlink",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 791000000,
|
||||
"end": 801000000
|
||||
},
|
||||
{
|
||||
"name": "801 Vodafone FDD downlink",
|
||||
"type": "mobile.mno.vodafone",
|
||||
"start": 801000000,
|
||||
"end": 811000000
|
||||
},
|
||||
{
|
||||
"name": "811 Telekom FDD downlink",
|
||||
"type": "mobile.mno.telekom",
|
||||
"start": 811000000,
|
||||
"end": 821000000
|
||||
},
|
||||
{
|
||||
"name": "832 Telefonica FDD uplink",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 832000000,
|
||||
"end": 842000000
|
||||
},
|
||||
{
|
||||
"name": "842 Vodafone FDD uplink",
|
||||
"type": "mobile.mno.vodafone",
|
||||
"start": 842000000,
|
||||
"end": 852000000
|
||||
},
|
||||
{
|
||||
"name": "852 Telekom FDD uplink",
|
||||
"type": "mobile.mno.telekom",
|
||||
"start": 852000000,
|
||||
"end": 862000000
|
||||
},
|
||||
{
|
||||
"name": "GSM-R FDD uplink",
|
||||
"type": "mobile.gsm-r",
|
||||
"start": 873100000,
|
||||
"end": 880000000
|
||||
},
|
||||
{
|
||||
"name": "880 Telefonica FDD uplink",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 880000000,
|
||||
"end": 890000000
|
||||
},
|
||||
{
|
||||
"name": "890 Vodafone FDD uplink",
|
||||
"type": "mobile.mno.vodafone",
|
||||
"start": 890000000,
|
||||
"end": 900000000
|
||||
},
|
||||
{
|
||||
"name": "900 Telekom FDD uplink",
|
||||
"type": "mobile.mno.telekom",
|
||||
"start": 900000000,
|
||||
"end": 915000000
|
||||
},
|
||||
{
|
||||
"name": "GSM-R FDD downlink",
|
||||
"type": "mobile.gsm-r",
|
||||
"start": 918100000,
|
||||
"end": 925000000
|
||||
},
|
||||
{
|
||||
"name": "925 Telefonica FDD downlink",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 925000000,
|
||||
"end": 935000000
|
||||
},
|
||||
{
|
||||
"name": "935 Vodafone FDD downlink",
|
||||
"type": "mobile.mno.vodafone",
|
||||
"start": 935000000,
|
||||
"end": 945000000
|
||||
},
|
||||
{
|
||||
"name": "945 Telekom FDD downlink",
|
||||
"type": "mobile.mno.telekom",
|
||||
"start": 945000000,
|
||||
"end": 960000000
|
||||
},
|
||||
{
|
||||
"name": "1452 Telekom SDL downlink",
|
||||
"type": "mobile.mno.telekom",
|
||||
"start": 1452000000,
|
||||
"end": 1472000000
|
||||
},
|
||||
{
|
||||
"name": "1472 Vodafone SDL downlink",
|
||||
"type": "mobile.mno.vodafone",
|
||||
"start": 1472000000,
|
||||
"end": 1492000000
|
||||
},
|
||||
{
|
||||
"name": "1710 Telekom FDD uplink",
|
||||
"type": "mobile.mno.telekom",
|
||||
"start": 1710000000,
|
||||
"end": 1740000000
|
||||
},
|
||||
{
|
||||
"name": "1740 Telefonica FDD uplink",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 1740000000,
|
||||
"end": 1760000000
|
||||
},
|
||||
{
|
||||
"name": "1760 Vodafone FDD uplink",
|
||||
"type": "mobile.mno.vodafone",
|
||||
"start": 1760000000,
|
||||
"end": 1785000000
|
||||
},
|
||||
{
|
||||
"name": "1805 Telekom FDD downlink",
|
||||
"type": "mobile.mno.telekom",
|
||||
"start": 1805000000,
|
||||
"end": 1835000000
|
||||
},
|
||||
{
|
||||
"name": "1835 Telefonica FDD downlink",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 1835000000,
|
||||
"end": 1855000000
|
||||
},
|
||||
{
|
||||
"name": "1855 Vodafone FDD downlink",
|
||||
"type": "mobile.mno.vodafone",
|
||||
"start": 1855000000,
|
||||
"end": 1880000000
|
||||
},
|
||||
{
|
||||
"name": "DECT",
|
||||
"type": "broadcast",
|
||||
"start": 1880000000,
|
||||
"end": 1900000000
|
||||
},
|
||||
{
|
||||
"name": "1900.1 Telefonica",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 1900100000,
|
||||
"end": 1905100000
|
||||
},
|
||||
{
|
||||
"name": "1920 Vodafone FDD uplink",
|
||||
"type": "mobile.mno.vodafone",
|
||||
"start": 1920000000,
|
||||
"end": 1940000000
|
||||
},
|
||||
{
|
||||
"name": "1940 Telefonica FDD uplink",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 1940000000,
|
||||
"end": 1960000000
|
||||
},
|
||||
{
|
||||
"name": "1960 Telekom FDD uplink",
|
||||
"type": "mobile.mno.telekom",
|
||||
"start": 1960000000,
|
||||
"end": 1980000000
|
||||
},
|
||||
{
|
||||
"name": "2010.5 Telefonica",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 2010500000,
|
||||
"end": 2024700000
|
||||
},
|
||||
{
|
||||
"name": "2110 Vodafone FDD downlink",
|
||||
"type": "mobile.mno.vodafone",
|
||||
"start": 2110000000,
|
||||
"end": 2130000000
|
||||
},
|
||||
{
|
||||
"name": "2130 Telefonica FDD downlink",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 2130000000,
|
||||
"end": 2150000000
|
||||
},
|
||||
{
|
||||
"name": "2150 Telekom FDD downlink",
|
||||
"type": "mobile.mno.telekom",
|
||||
"start": 2150000000,
|
||||
"end": 2170000000
|
||||
},
|
||||
{
|
||||
"name": "2500 Vodafone FDD uplink",
|
||||
"type": "mobile.mno.vodafone",
|
||||
"start": 2500000000,
|
||||
"end": 2520000000
|
||||
},
|
||||
{
|
||||
"name": "2520 Telekom FDD uplink",
|
||||
"type": "mobile.mno.telekom",
|
||||
"start": 2520000000,
|
||||
"end": 2540000000
|
||||
},
|
||||
{
|
||||
"name": "2540 Telefonica FDD uplink",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 2540000000,
|
||||
"end": 2570000000
|
||||
},
|
||||
{
|
||||
"name": "2570 Telefonica TDD",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 2570000000,
|
||||
"end": 2580000000
|
||||
},
|
||||
{
|
||||
"name": "2580 Vodafone TDD",
|
||||
"type": "mobile.mno.vodafone",
|
||||
"start": 2580000000,
|
||||
"end": 2605000000
|
||||
},
|
||||
{
|
||||
"name": "2605 Telekom TDD",
|
||||
"type": "mobile.mno.telekom",
|
||||
"start": 2605000000,
|
||||
"end": 2610000000
|
||||
},
|
||||
{
|
||||
"name": "2610 Telefonica TDD",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 2610000000,
|
||||
"end": 2620000000
|
||||
},
|
||||
{
|
||||
"name": "2620 Vodafone FDD downlink",
|
||||
"type": "mobile.mno.vodafone",
|
||||
"start": 2620000000,
|
||||
"end": 2640000000
|
||||
},
|
||||
{
|
||||
"name": "2640 Telekom FDD downlink",
|
||||
"type": "mobile.mno.telekom",
|
||||
"start": 2640000000,
|
||||
"end": 2660000000
|
||||
},
|
||||
{
|
||||
"name": "2660 Telefonica FDD downlink",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 2660000000,
|
||||
"end": 2690000000
|
||||
},
|
||||
{
|
||||
"name": "3400 Vodafone",
|
||||
"type": "mobile.mno.vodafone",
|
||||
"start": 3400000000,
|
||||
"end": 3490000000
|
||||
},
|
||||
{
|
||||
"name": "3490 Drillisch",
|
||||
"type": "mobile.mno.drillisch",
|
||||
"start": 3490000000,
|
||||
"end": 3540000000
|
||||
},
|
||||
{
|
||||
"name": "3540 Telefonica",
|
||||
"type": "mobile.mno.telefonica",
|
||||
"start": 3540000000,
|
||||
"end": 3610000000
|
||||
},
|
||||
{
|
||||
"name": "3610 Telekom",
|
||||
"type": "mobile.mno.telekom",
|
||||
"start": 3610000000,
|
||||
"end": 3700000000
|
||||
}
|
||||
]
|
||||
}
|
21
root/res/colormaps/classic.json
Normal file
21
root/res/colormaps/classic.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "Classic",
|
||||
"author": "Youssef Touil",
|
||||
"map": [
|
||||
"#000020",
|
||||
"#000030",
|
||||
"#000050",
|
||||
"#000091",
|
||||
"#1E90FF",
|
||||
"#FFFFFF",
|
||||
"#FFFF00",
|
||||
"#FE6D16",
|
||||
"#FE6D16",
|
||||
"#FF0000",
|
||||
"#FF0000",
|
||||
"#C60000",
|
||||
"#9F0000",
|
||||
"#750000",
|
||||
"#4A0000"
|
||||
]
|
||||
}
|
8
root/res/colormaps/greyscale.json
Normal file
8
root/res/colormaps/greyscale.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "Grey Scale",
|
||||
"author": "Ryzerth",
|
||||
"map": [
|
||||
"#000000",
|
||||
"#FFFFFF"
|
||||
]
|
||||
}
|
262
root/res/colormaps/inferno.json
Normal file
262
root/res/colormaps/inferno.json
Normal file
@ -0,0 +1,262 @@
|
||||
{
|
||||
"name": "Inferno",
|
||||
"author": "B.I.D.S.",
|
||||
"map": [
|
||||
"#000004",
|
||||
"#010005",
|
||||
"#010106",
|
||||
"#010108",
|
||||
"#02010A",
|
||||
"#02020C",
|
||||
"#02020E",
|
||||
"#030210",
|
||||
"#040312",
|
||||
"#040314",
|
||||
"#050417",
|
||||
"#060419",
|
||||
"#07051B",
|
||||
"#08051D",
|
||||
"#09061F",
|
||||
"#0A0722",
|
||||
"#0B0724",
|
||||
"#0C0826",
|
||||
"#0D0829",
|
||||
"#0E092B",
|
||||
"#10092D",
|
||||
"#110A30",
|
||||
"#120A32",
|
||||
"#140B34",
|
||||
"#150B37",
|
||||
"#160B39",
|
||||
"#180C3C",
|
||||
"#190C3E",
|
||||
"#1B0C41",
|
||||
"#1C0C43",
|
||||
"#1E0C45",
|
||||
"#1F0C48",
|
||||
"#210C4A",
|
||||
"#230C4C",
|
||||
"#240C4F",
|
||||
"#260C51",
|
||||
"#280B53",
|
||||
"#290B55",
|
||||
"#2B0B57",
|
||||
"#2D0B59",
|
||||
"#2F0A5B",
|
||||
"#310A5C",
|
||||
"#320A5E",
|
||||
"#340A5F",
|
||||
"#360961",
|
||||
"#380962",
|
||||
"#390963",
|
||||
"#3B0964",
|
||||
"#3D0965",
|
||||
"#3E0966",
|
||||
"#400A67",
|
||||
"#420A68",
|
||||
"#440A68",
|
||||
"#450A69",
|
||||
"#470B6A",
|
||||
"#490B6A",
|
||||
"#4A0C6B",
|
||||
"#4C0C6B",
|
||||
"#4D0D6C",
|
||||
"#4F0D6C",
|
||||
"#510E6C",
|
||||
"#520E6D",
|
||||
"#540F6D",
|
||||
"#550F6D",
|
||||
"#57106E",
|
||||
"#59106E",
|
||||
"#5A116E",
|
||||
"#5C126E",
|
||||
"#5D126E",
|
||||
"#5F136E",
|
||||
"#61136E",
|
||||
"#62146E",
|
||||
"#64156E",
|
||||
"#65156E",
|
||||
"#67166E",
|
||||
"#69166E",
|
||||
"#6A176E",
|
||||
"#6C186E",
|
||||
"#6D186E",
|
||||
"#6F196E",
|
||||
"#71196E",
|
||||
"#721A6E",
|
||||
"#741A6E",
|
||||
"#751B6E",
|
||||
"#771C6D",
|
||||
"#781C6D",
|
||||
"#7A1D6D",
|
||||
"#7C1D6D",
|
||||
"#7D1E6D",
|
||||
"#7F1E6C",
|
||||
"#801F6C",
|
||||
"#82206C",
|
||||
"#84206B",
|
||||
"#85216B",
|
||||
"#87216B",
|
||||
"#88226A",
|
||||
"#8A226A",
|
||||
"#8C2369",
|
||||
"#8D2369",
|
||||
"#8F2469",
|
||||
"#902568",
|
||||
"#922568",
|
||||
"#932667",
|
||||
"#952667",
|
||||
"#972766",
|
||||
"#982766",
|
||||
"#9A2865",
|
||||
"#9B2964",
|
||||
"#9D2964",
|
||||
"#9F2A63",
|
||||
"#A02A63",
|
||||
"#A22B62",
|
||||
"#A32C61",
|
||||
"#A52C60",
|
||||
"#A62D60",
|
||||
"#A82E5F",
|
||||
"#A92E5E",
|
||||
"#AB2F5E",
|
||||
"#AD305D",
|
||||
"#AE305C",
|
||||
"#B0315B",
|
||||
"#B1325A",
|
||||
"#B3325A",
|
||||
"#B43359",
|
||||
"#B63458",
|
||||
"#B73557",
|
||||
"#B93556",
|
||||
"#BA3655",
|
||||
"#BC3754",
|
||||
"#BD3853",
|
||||
"#BF3952",
|
||||
"#C03A51",
|
||||
"#C13A50",
|
||||
"#C33B4F",
|
||||
"#C43C4E",
|
||||
"#C63D4D",
|
||||
"#C73E4C",
|
||||
"#C83F4B",
|
||||
"#CA404A",
|
||||
"#CB4149",
|
||||
"#CC4248",
|
||||
"#CE4347",
|
||||
"#CF4446",
|
||||
"#D04545",
|
||||
"#D24644",
|
||||
"#D34743",
|
||||
"#D44842",
|
||||
"#D54A41",
|
||||
"#D74B3F",
|
||||
"#D84C3E",
|
||||
"#D94D3D",
|
||||
"#DA4E3C",
|
||||
"#DB503B",
|
||||
"#DD513A",
|
||||
"#DE5238",
|
||||
"#DF5337",
|
||||
"#E05536",
|
||||
"#E15635",
|
||||
"#E25734",
|
||||
"#E35933",
|
||||
"#E45A31",
|
||||
"#E55C30",
|
||||
"#E65D2F",
|
||||
"#E75E2E",
|
||||
"#E8602D",
|
||||
"#E9612B",
|
||||
"#EA632A",
|
||||
"#EB6429",
|
||||
"#EB6628",
|
||||
"#EC6726",
|
||||
"#ED6925",
|
||||
"#EE6A24",
|
||||
"#EF6C23",
|
||||
"#EF6E21",
|
||||
"#F06F20",
|
||||
"#F1711F",
|
||||
"#F1731D",
|
||||
"#F2741C",
|
||||
"#F3761B",
|
||||
"#F37819",
|
||||
"#F47918",
|
||||
"#F57B17",
|
||||
"#F57D15",
|
||||
"#F67E14",
|
||||
"#F68013",
|
||||
"#F78212",
|
||||
"#F78410",
|
||||
"#F8850F",
|
||||
"#F8870E",
|
||||
"#F8890C",
|
||||
"#F98B0B",
|
||||
"#F98C0A",
|
||||
"#F98E09",
|
||||
"#FA9008",
|
||||
"#FA9207",
|
||||
"#FA9407",
|
||||
"#FB9606",
|
||||
"#FB9706",
|
||||
"#FB9906",
|
||||
"#FB9B06",
|
||||
"#FB9D07",
|
||||
"#FC9F07",
|
||||
"#FCA108",
|
||||
"#FCA309",
|
||||
"#FCA50A",
|
||||
"#FCA60C",
|
||||
"#FCA80D",
|
||||
"#FCAA0F",
|
||||
"#FCAC11",
|
||||
"#FCAE12",
|
||||
"#FCB014",
|
||||
"#FCB216",
|
||||
"#FCB418",
|
||||
"#FBB61A",
|
||||
"#FBB81D",
|
||||
"#FBBA1F",
|
||||
"#FBBC21",
|
||||
"#FBBE23",
|
||||
"#FAC026",
|
||||
"#FAC228",
|
||||
"#FAC42A",
|
||||
"#FAC62D",
|
||||
"#F9C72F",
|
||||
"#F9C932",
|
||||
"#F9CB35",
|
||||
"#F8CD37",
|
||||
"#F8CF3A",
|
||||
"#F7D13D",
|
||||
"#F7D340",
|
||||
"#F6D543",
|
||||
"#F6D746",
|
||||
"#F5D949",
|
||||
"#F5DB4C",
|
||||
"#F4DD4F",
|
||||
"#F4DF53",
|
||||
"#F4E156",
|
||||
"#F3E35A",
|
||||
"#F3E55D",
|
||||
"#F2E661",
|
||||
"#F2E865",
|
||||
"#F2EA69",
|
||||
"#F1EC6D",
|
||||
"#F1ED71",
|
||||
"#F1EF75",
|
||||
"#F1F179",
|
||||
"#F2F27D",
|
||||
"#F2F482",
|
||||
"#F3F586",
|
||||
"#F3F68A",
|
||||
"#F4F88E",
|
||||
"#F5F992",
|
||||
"#F6FA96",
|
||||
"#F8FB9A",
|
||||
"#F9FC9D",
|
||||
"#FAFDA1",
|
||||
"#FCFFA4"
|
||||
]
|
||||
}
|
262
root/res/colormaps/magma.json
Normal file
262
root/res/colormaps/magma.json
Normal file
@ -0,0 +1,262 @@
|
||||
{
|
||||
"name": "Magma",
|
||||
"author": "B.I.D.S.",
|
||||
"map": [
|
||||
"#000004",
|
||||
"#010005",
|
||||
"#010106",
|
||||
"#010108",
|
||||
"#020109",
|
||||
"#02020B",
|
||||
"#02020D",
|
||||
"#03030F",
|
||||
"#030312",
|
||||
"#040414",
|
||||
"#050416",
|
||||
"#060518",
|
||||
"#06051A",
|
||||
"#07061C",
|
||||
"#08071E",
|
||||
"#090720",
|
||||
"#0A0822",
|
||||
"#0B0924",
|
||||
"#0C0926",
|
||||
"#0D0A29",
|
||||
"#0E0B2B",
|
||||
"#100B2D",
|
||||
"#110C2F",
|
||||
"#120D31",
|
||||
"#130D34",
|
||||
"#140E36",
|
||||
"#150E38",
|
||||
"#160F3B",
|
||||
"#180F3D",
|
||||
"#19103F",
|
||||
"#1A1042",
|
||||
"#1C1044",
|
||||
"#1D1147",
|
||||
"#1E1149",
|
||||
"#20114B",
|
||||
"#21114E",
|
||||
"#221150",
|
||||
"#241253",
|
||||
"#251255",
|
||||
"#271258",
|
||||
"#29115A",
|
||||
"#2A115C",
|
||||
"#2C115F",
|
||||
"#2D1161",
|
||||
"#2F1163",
|
||||
"#311165",
|
||||
"#331067",
|
||||
"#341069",
|
||||
"#36106B",
|
||||
"#38106C",
|
||||
"#390F6E",
|
||||
"#3B0F70",
|
||||
"#3D0F71",
|
||||
"#3F0F72",
|
||||
"#400F74",
|
||||
"#420F75",
|
||||
"#440F76",
|
||||
"#451077",
|
||||
"#471078",
|
||||
"#491078",
|
||||
"#4A1079",
|
||||
"#4C117A",
|
||||
"#4E117B",
|
||||
"#4F127B",
|
||||
"#51127C",
|
||||
"#52137C",
|
||||
"#54137D",
|
||||
"#56147D",
|
||||
"#57157E",
|
||||
"#59157E",
|
||||
"#5A167E",
|
||||
"#5C167F",
|
||||
"#5D177F",
|
||||
"#5F187F",
|
||||
"#601880",
|
||||
"#621980",
|
||||
"#641A80",
|
||||
"#651A80",
|
||||
"#671B80",
|
||||
"#681C81",
|
||||
"#6A1C81",
|
||||
"#6B1D81",
|
||||
"#6D1D81",
|
||||
"#6E1E81",
|
||||
"#701F81",
|
||||
"#721F81",
|
||||
"#732081",
|
||||
"#752181",
|
||||
"#762181",
|
||||
"#782281",
|
||||
"#792282",
|
||||
"#7B2382",
|
||||
"#7C2382",
|
||||
"#7E2482",
|
||||
"#802582",
|
||||
"#812581",
|
||||
"#832681",
|
||||
"#842681",
|
||||
"#862781",
|
||||
"#882781",
|
||||
"#892881",
|
||||
"#8B2981",
|
||||
"#8C2981",
|
||||
"#8E2A81",
|
||||
"#902A81",
|
||||
"#912B81",
|
||||
"#932B80",
|
||||
"#942C80",
|
||||
"#962C80",
|
||||
"#982D80",
|
||||
"#992D80",
|
||||
"#9B2E7F",
|
||||
"#9C2E7F",
|
||||
"#9E2F7F",
|
||||
"#A02F7F",
|
||||
"#A1307E",
|
||||
"#A3307E",
|
||||
"#A5317E",
|
||||
"#A6317D",
|
||||
"#A8327D",
|
||||
"#AA337D",
|
||||
"#AB337C",
|
||||
"#AD347C",
|
||||
"#AE347B",
|
||||
"#B0357B",
|
||||
"#B2357B",
|
||||
"#B3367A",
|
||||
"#B5367A",
|
||||
"#B73779",
|
||||
"#B83779",
|
||||
"#BA3878",
|
||||
"#BC3978",
|
||||
"#BD3977",
|
||||
"#BF3A77",
|
||||
"#C03A76",
|
||||
"#C23B75",
|
||||
"#C43C75",
|
||||
"#C53C74",
|
||||
"#C73D73",
|
||||
"#C83E73",
|
||||
"#CA3E72",
|
||||
"#CC3F71",
|
||||
"#CD4071",
|
||||
"#CF4070",
|
||||
"#D0416F",
|
||||
"#D2426F",
|
||||
"#D3436E",
|
||||
"#D5446D",
|
||||
"#D6456C",
|
||||
"#D8456C",
|
||||
"#D9466B",
|
||||
"#DB476A",
|
||||
"#DC4869",
|
||||
"#DE4968",
|
||||
"#DF4A68",
|
||||
"#E04C67",
|
||||
"#E24D66",
|
||||
"#E34E65",
|
||||
"#E44F64",
|
||||
"#E55064",
|
||||
"#E75263",
|
||||
"#E85362",
|
||||
"#E95462",
|
||||
"#EA5661",
|
||||
"#EB5760",
|
||||
"#EC5860",
|
||||
"#ED5A5F",
|
||||
"#EE5B5E",
|
||||
"#EF5D5E",
|
||||
"#F05F5E",
|
||||
"#F1605D",
|
||||
"#F2625D",
|
||||
"#F2645C",
|
||||
"#F3655C",
|
||||
"#F4675C",
|
||||
"#F4695C",
|
||||
"#F56B5C",
|
||||
"#F66C5C",
|
||||
"#F66E5C",
|
||||
"#F7705C",
|
||||
"#F7725C",
|
||||
"#F8745C",
|
||||
"#F8765C",
|
||||
"#F9785D",
|
||||
"#F9795D",
|
||||
"#F97B5D",
|
||||
"#FA7D5E",
|
||||
"#FA7F5E",
|
||||
"#FA815F",
|
||||
"#FB835F",
|
||||
"#FB8560",
|
||||
"#FB8761",
|
||||
"#FC8961",
|
||||
"#FC8A62",
|
||||
"#FC8C63",
|
||||
"#FC8E64",
|
||||
"#FC9065",
|
||||
"#FD9266",
|
||||
"#FD9467",
|
||||
"#FD9668",
|
||||
"#FD9869",
|
||||
"#FD9A6A",
|
||||
"#FD9B6B",
|
||||
"#FE9D6C",
|
||||
"#FE9F6D",
|
||||
"#FEA16E",
|
||||
"#FEA36F",
|
||||
"#FEA571",
|
||||
"#FEA772",
|
||||
"#FEA973",
|
||||
"#FEAA74",
|
||||
"#FEAC76",
|
||||
"#FEAE77",
|
||||
"#FEB078",
|
||||
"#FEB27A",
|
||||
"#FEB47B",
|
||||
"#FEB67C",
|
||||
"#FEB77E",
|
||||
"#FEB97F",
|
||||
"#FEBB81",
|
||||
"#FEBD82",
|
||||
"#FEBF84",
|
||||
"#FEC185",
|
||||
"#FEC287",
|
||||
"#FEC488",
|
||||
"#FEC68A",
|
||||
"#FEC88C",
|
||||
"#FECA8D",
|
||||
"#FECC8F",
|
||||
"#FECD90",
|
||||
"#FECF92",
|
||||
"#FED194",
|
||||
"#FED395",
|
||||
"#FED597",
|
||||
"#FED799",
|
||||
"#FED89A",
|
||||
"#FDDA9C",
|
||||
"#FDDC9E",
|
||||
"#FDDEA0",
|
||||
"#FDE0A1",
|
||||
"#FDE2A3",
|
||||
"#FDE3A5",
|
||||
"#FDE5A7",
|
||||
"#FDE7A9",
|
||||
"#FDE9AA",
|
||||
"#FDEBAC",
|
||||
"#FCECAE",
|
||||
"#FCEEB0",
|
||||
"#FCF0B2",
|
||||
"#FCF2B4",
|
||||
"#FCF4B6",
|
||||
"#FCF6B8",
|
||||
"#FCF7B9",
|
||||
"#FCF9BB",
|
||||
"#FCFBBD",
|
||||
"#FCFDBF"
|
||||
]
|
||||
}
|
262
root/res/colormaps/plasma.json
Normal file
262
root/res/colormaps/plasma.json
Normal file
@ -0,0 +1,262 @@
|
||||
{
|
||||
"name": "Plasma",
|
||||
"author": "B.I.D.S.",
|
||||
"map": [
|
||||
"#0D0887",
|
||||
"#100788",
|
||||
"#130789",
|
||||
"#16078A",
|
||||
"#19068C",
|
||||
"#1B068D",
|
||||
"#1D068E",
|
||||
"#20068F",
|
||||
"#220690",
|
||||
"#240691",
|
||||
"#260591",
|
||||
"#280592",
|
||||
"#2A0593",
|
||||
"#2C0594",
|
||||
"#2E0595",
|
||||
"#2F0596",
|
||||
"#310597",
|
||||
"#330597",
|
||||
"#350498",
|
||||
"#370499",
|
||||
"#38049A",
|
||||
"#3A049A",
|
||||
"#3C049B",
|
||||
"#3E049C",
|
||||
"#3F049C",
|
||||
"#41049D",
|
||||
"#43039E",
|
||||
"#44039E",
|
||||
"#46039F",
|
||||
"#48039F",
|
||||
"#4903A0",
|
||||
"#4B03A1",
|
||||
"#4C02A1",
|
||||
"#4E02A2",
|
||||
"#5002A2",
|
||||
"#5102A3",
|
||||
"#5302A3",
|
||||
"#5502A4",
|
||||
"#5601A4",
|
||||
"#5801A4",
|
||||
"#5901A5",
|
||||
"#5B01A5",
|
||||
"#5C01A6",
|
||||
"#5E01A6",
|
||||
"#6001A6",
|
||||
"#6100A7",
|
||||
"#6300A7",
|
||||
"#6400A7",
|
||||
"#6600A7",
|
||||
"#6700A8",
|
||||
"#6900A8",
|
||||
"#6A00A8",
|
||||
"#6C00A8",
|
||||
"#6E00A8",
|
||||
"#6F00A8",
|
||||
"#7100A8",
|
||||
"#7201A8",
|
||||
"#7401A8",
|
||||
"#7501A8",
|
||||
"#7701A8",
|
||||
"#7801A8",
|
||||
"#7A02A8",
|
||||
"#7B02A8",
|
||||
"#7D03A8",
|
||||
"#7E03A8",
|
||||
"#8004A8",
|
||||
"#8104A7",
|
||||
"#8305A7",
|
||||
"#8405A7",
|
||||
"#8606A6",
|
||||
"#8707A6",
|
||||
"#8808A6",
|
||||
"#8A09A5",
|
||||
"#8B0AA5",
|
||||
"#8D0BA5",
|
||||
"#8E0CA4",
|
||||
"#8F0DA4",
|
||||
"#910EA3",
|
||||
"#920FA3",
|
||||
"#9410A2",
|
||||
"#9511A1",
|
||||
"#9613A1",
|
||||
"#9814A0",
|
||||
"#99159F",
|
||||
"#9A169F",
|
||||
"#9C179E",
|
||||
"#9D189D",
|
||||
"#9E199D",
|
||||
"#A01A9C",
|
||||
"#A11B9B",
|
||||
"#A21D9A",
|
||||
"#A31E9A",
|
||||
"#A51F99",
|
||||
"#A62098",
|
||||
"#A72197",
|
||||
"#A82296",
|
||||
"#AA2395",
|
||||
"#AB2494",
|
||||
"#AC2694",
|
||||
"#AD2793",
|
||||
"#AE2892",
|
||||
"#B02991",
|
||||
"#B12A90",
|
||||
"#B22B8F",
|
||||
"#B32C8E",
|
||||
"#B42E8D",
|
||||
"#B52F8C",
|
||||
"#B6308B",
|
||||
"#B7318A",
|
||||
"#B83289",
|
||||
"#BA3388",
|
||||
"#BB3488",
|
||||
"#BC3587",
|
||||
"#BD3786",
|
||||
"#BE3885",
|
||||
"#BF3984",
|
||||
"#C03A83",
|
||||
"#C13B82",
|
||||
"#C23C81",
|
||||
"#C33D80",
|
||||
"#C43E7F",
|
||||
"#C5407E",
|
||||
"#C6417D",
|
||||
"#C7427C",
|
||||
"#C8437B",
|
||||
"#C9447A",
|
||||
"#CA457A",
|
||||
"#CB4679",
|
||||
"#CC4778",
|
||||
"#CC4977",
|
||||
"#CD4A76",
|
||||
"#CE4B75",
|
||||
"#CF4C74",
|
||||
"#D04D73",
|
||||
"#D14E72",
|
||||
"#D24F71",
|
||||
"#D35171",
|
||||
"#D45270",
|
||||
"#D5536F",
|
||||
"#D5546E",
|
||||
"#D6556D",
|
||||
"#D7566C",
|
||||
"#D8576B",
|
||||
"#D9586A",
|
||||
"#DA5A6A",
|
||||
"#DA5B69",
|
||||
"#DB5C68",
|
||||
"#DC5D67",
|
||||
"#DD5E66",
|
||||
"#DE5F65",
|
||||
"#DE6164",
|
||||
"#DF6263",
|
||||
"#E06363",
|
||||
"#E16462",
|
||||
"#E26561",
|
||||
"#E26660",
|
||||
"#E3685F",
|
||||
"#E4695E",
|
||||
"#E56A5D",
|
||||
"#E56B5D",
|
||||
"#E66C5C",
|
||||
"#E76E5B",
|
||||
"#E76F5A",
|
||||
"#E87059",
|
||||
"#E97158",
|
||||
"#E97257",
|
||||
"#EA7457",
|
||||
"#EB7556",
|
||||
"#EB7655",
|
||||
"#EC7754",
|
||||
"#ED7953",
|
||||
"#ED7A52",
|
||||
"#EE7B51",
|
||||
"#EF7C51",
|
||||
"#EF7E50",
|
||||
"#F07F4F",
|
||||
"#F0804E",
|
||||
"#F1814D",
|
||||
"#F1834C",
|
||||
"#F2844B",
|
||||
"#F3854B",
|
||||
"#F3874A",
|
||||
"#F48849",
|
||||
"#F48948",
|
||||
"#F58B47",
|
||||
"#F58C46",
|
||||
"#F68D45",
|
||||
"#F68F44",
|
||||
"#F79044",
|
||||
"#F79143",
|
||||
"#F79342",
|
||||
"#F89441",
|
||||
"#F89540",
|
||||
"#F9973F",
|
||||
"#F9983E",
|
||||
"#F99A3E",
|
||||
"#FA9B3D",
|
||||
"#FA9C3C",
|
||||
"#FA9E3B",
|
||||
"#FB9F3A",
|
||||
"#FBA139",
|
||||
"#FBA238",
|
||||
"#FCA338",
|
||||
"#FCA537",
|
||||
"#FCA636",
|
||||
"#FCA835",
|
||||
"#FCA934",
|
||||
"#FDAB33",
|
||||
"#FDAC33",
|
||||
"#FDAE32",
|
||||
"#FDAF31",
|
||||
"#FDB130",
|
||||
"#FDB22F",
|
||||
"#FDB42F",
|
||||
"#FDB52E",
|
||||
"#FEB72D",
|
||||
"#FEB82C",
|
||||
"#FEBA2C",
|
||||
"#FEBB2B",
|
||||
"#FEBD2A",
|
||||
"#FEBE2A",
|
||||
"#FEC029",
|
||||
"#FDC229",
|
||||
"#FDC328",
|
||||
"#FDC527",
|
||||
"#FDC627",
|
||||
"#FDC827",
|
||||
"#FDCA26",
|
||||
"#FDCB26",
|
||||
"#FCCD25",
|
||||
"#FCCE25",
|
||||
"#FCD025",
|
||||
"#FCD225",
|
||||
"#FBD324",
|
||||
"#FBD524",
|
||||
"#FBD724",
|
||||
"#FAD824",
|
||||
"#FADA24",
|
||||
"#F9DC24",
|
||||
"#F9DD25",
|
||||
"#F8DF25",
|
||||
"#F8E125",
|
||||
"#F7E225",
|
||||
"#F7E425",
|
||||
"#F6E626",
|
||||
"#F6E826",
|
||||
"#F5E926",
|
||||
"#F5EB27",
|
||||
"#F4ED27",
|
||||
"#F3EE27",
|
||||
"#F3F027",
|
||||
"#F2F227",
|
||||
"#F1F426",
|
||||
"#F1F525",
|
||||
"#F0F724",
|
||||
"#F0F921"
|
||||
]
|
||||
}
|
262
root/res/colormaps/turbo.json
Normal file
262
root/res/colormaps/turbo.json
Normal file
@ -0,0 +1,262 @@
|
||||
{
|
||||
"name": "Turbo",
|
||||
"author": "Google AI",
|
||||
"map": [
|
||||
"#30123B",
|
||||
"#321543",
|
||||
"#33184A",
|
||||
"#341B51",
|
||||
"#351E58",
|
||||
"#36215F",
|
||||
"#372466",
|
||||
"#38276D",
|
||||
"#392A73",
|
||||
"#3A2D79",
|
||||
"#3B2F80",
|
||||
"#3C3286",
|
||||
"#3D358B",
|
||||
"#3E3891",
|
||||
"#3F3B97",
|
||||
"#3F3E9C",
|
||||
"#4040A2",
|
||||
"#4143A7",
|
||||
"#4146AC",
|
||||
"#4249B1",
|
||||
"#424BB5",
|
||||
"#434EBA",
|
||||
"#4451BF",
|
||||
"#4454C3",
|
||||
"#4456C7",
|
||||
"#4559CB",
|
||||
"#455CCF",
|
||||
"#455ED3",
|
||||
"#4661D6",
|
||||
"#4664DA",
|
||||
"#4666DD",
|
||||
"#4669E0",
|
||||
"#466BE3",
|
||||
"#476EE6",
|
||||
"#4771E9",
|
||||
"#4773EB",
|
||||
"#4776EE",
|
||||
"#4778F0",
|
||||
"#477BF2",
|
||||
"#467DF4",
|
||||
"#4680F6",
|
||||
"#4682F8",
|
||||
"#4685FA",
|
||||
"#4687FB",
|
||||
"#458AFC",
|
||||
"#458CFD",
|
||||
"#448FFE",
|
||||
"#4391FE",
|
||||
"#4294FF",
|
||||
"#4196FF",
|
||||
"#4099FF",
|
||||
"#3E9BFE",
|
||||
"#3D9EFE",
|
||||
"#3BA0FD",
|
||||
"#3AA3FC",
|
||||
"#38A5FB",
|
||||
"#37A8FA",
|
||||
"#35ABF8",
|
||||
"#33ADF7",
|
||||
"#31AFF5",
|
||||
"#2FB2F4",
|
||||
"#2EB4F2",
|
||||
"#2CB7F0",
|
||||
"#2AB9EE",
|
||||
"#28BCEB",
|
||||
"#27BEE9",
|
||||
"#25C0E7",
|
||||
"#23C3E4",
|
||||
"#22C5E2",
|
||||
"#20C7DF",
|
||||
"#1FC9DD",
|
||||
"#1ECBDA",
|
||||
"#1CCDD8",
|
||||
"#1BD0D5",
|
||||
"#1AD2D2",
|
||||
"#1AD4D0",
|
||||
"#19D5CD",
|
||||
"#18D7CA",
|
||||
"#18D9C8",
|
||||
"#18DBC5",
|
||||
"#18DDC2",
|
||||
"#18DEC0",
|
||||
"#18E0BD",
|
||||
"#19E2BB",
|
||||
"#19E3B9",
|
||||
"#1AE4B6",
|
||||
"#1CE6B4",
|
||||
"#1DE7B2",
|
||||
"#1FE9AF",
|
||||
"#20EAAC",
|
||||
"#22EBAA",
|
||||
"#25ECA7",
|
||||
"#27EEA4",
|
||||
"#2AEFA1",
|
||||
"#2CF09E",
|
||||
"#2FF19B",
|
||||
"#32F298",
|
||||
"#35F394",
|
||||
"#38F491",
|
||||
"#3CF58E",
|
||||
"#3FF68A",
|
||||
"#43F787",
|
||||
"#46F884",
|
||||
"#4AF880",
|
||||
"#4EF97D",
|
||||
"#52FA7A",
|
||||
"#55FA76",
|
||||
"#59FB73",
|
||||
"#5DFC6F",
|
||||
"#61FC6C",
|
||||
"#65FD69",
|
||||
"#69FD66",
|
||||
"#6DFE62",
|
||||
"#71FE5F",
|
||||
"#75FE5C",
|
||||
"#79FE59",
|
||||
"#7DFF56",
|
||||
"#80FF53",
|
||||
"#84FF51",
|
||||
"#88FF4E",
|
||||
"#8BFF4B",
|
||||
"#8FFF49",
|
||||
"#92FF47",
|
||||
"#96FE44",
|
||||
"#99FE42",
|
||||
"#9CFE40",
|
||||
"#9FFD3F",
|
||||
"#A1FD3D",
|
||||
"#A4FC3C",
|
||||
"#A7FC3A",
|
||||
"#A9FB39",
|
||||
"#ACFB38",
|
||||
"#AFFA37",
|
||||
"#B1F936",
|
||||
"#B4F836",
|
||||
"#B7F735",
|
||||
"#B9F635",
|
||||
"#BCF534",
|
||||
"#BEF434",
|
||||
"#C1F334",
|
||||
"#C3F134",
|
||||
"#C6F034",
|
||||
"#C8EF34",
|
||||
"#CBED34",
|
||||
"#CDEC34",
|
||||
"#D0EA34",
|
||||
"#D2E935",
|
||||
"#D4E735",
|
||||
"#D7E535",
|
||||
"#D9E436",
|
||||
"#DBE236",
|
||||
"#DDE037",
|
||||
"#DFDF37",
|
||||
"#E1DD37",
|
||||
"#E3DB38",
|
||||
"#E5D938",
|
||||
"#E7D739",
|
||||
"#E9D539",
|
||||
"#EBD339",
|
||||
"#ECD13A",
|
||||
"#EECF3A",
|
||||
"#EFCD3A",
|
||||
"#F1CB3A",
|
||||
"#F2C93A",
|
||||
"#F4C73A",
|
||||
"#F5C53A",
|
||||
"#F6C33A",
|
||||
"#F7C13A",
|
||||
"#F8BE39",
|
||||
"#F9BC39",
|
||||
"#FABA39",
|
||||
"#FBB838",
|
||||
"#FBB637",
|
||||
"#FCB336",
|
||||
"#FCB136",
|
||||
"#FDAE35",
|
||||
"#FDAC34",
|
||||
"#FEA933",
|
||||
"#FEA732",
|
||||
"#FEA431",
|
||||
"#FEA130",
|
||||
"#FE9E2F",
|
||||
"#FE9B2D",
|
||||
"#FE992C",
|
||||
"#FE962B",
|
||||
"#FE932A",
|
||||
"#FE9029",
|
||||
"#FD8D27",
|
||||
"#FD8A26",
|
||||
"#FC8725",
|
||||
"#FC8423",
|
||||
"#FB8122",
|
||||
"#FB7E21",
|
||||
"#FA7B1F",
|
||||
"#F9781E",
|
||||
"#F9751D",
|
||||
"#F8721C",
|
||||
"#F76F1A",
|
||||
"#F66C19",
|
||||
"#F56918",
|
||||
"#F46617",
|
||||
"#F36315",
|
||||
"#F26014",
|
||||
"#F15D13",
|
||||
"#F05B12",
|
||||
"#EF5811",
|
||||
"#ED5510",
|
||||
"#EC530F",
|
||||
"#EB500E",
|
||||
"#EA4E0D",
|
||||
"#E84B0C",
|
||||
"#E7490C",
|
||||
"#E5470B",
|
||||
"#E4450A",
|
||||
"#E2430A",
|
||||
"#E14109",
|
||||
"#DF3F08",
|
||||
"#DD3D08",
|
||||
"#DC3B07",
|
||||
"#DA3907",
|
||||
"#D83706",
|
||||
"#D63506",
|
||||
"#D43305",
|
||||
"#D23105",
|
||||
"#D02F05",
|
||||
"#CE2D04",
|
||||
"#CC2B04",
|
||||
"#CA2A04",
|
||||
"#C82803",
|
||||
"#C52603",
|
||||
"#C32503",
|
||||
"#C12302",
|
||||
"#BE2102",
|
||||
"#BC2002",
|
||||
"#B91E02",
|
||||
"#B71D02",
|
||||
"#B41B01",
|
||||
"#B21A01",
|
||||
"#AF1801",
|
||||
"#AC1701",
|
||||
"#A91601",
|
||||
"#A71401",
|
||||
"#A41301",
|
||||
"#A11201",
|
||||
"#9E1001",
|
||||
"#9B0F01",
|
||||
"#980E01",
|
||||
"#950D01",
|
||||
"#920B01",
|
||||
"#8E0A01",
|
||||
"#8B0902",
|
||||
"#880802",
|
||||
"#850702",
|
||||
"#810602",
|
||||
"#7E0502",
|
||||
"#7A0403"
|
||||
]
|
||||
}
|
262
root/res/colormaps/viridis.json
Normal file
262
root/res/colormaps/viridis.json
Normal file
@ -0,0 +1,262 @@
|
||||
{
|
||||
"name": "Viridis",
|
||||
"author": "B.I.D.S.",
|
||||
"map": [
|
||||
"#440154",
|
||||
"#440256",
|
||||
"#450457",
|
||||
"#450559",
|
||||
"#46075A",
|
||||
"#46085C",
|
||||
"#460A5D",
|
||||
"#460B5E",
|
||||
"#470D60",
|
||||
"#470E61",
|
||||
"#471063",
|
||||
"#471164",
|
||||
"#471365",
|
||||
"#481467",
|
||||
"#481668",
|
||||
"#481769",
|
||||
"#48186A",
|
||||
"#481A6C",
|
||||
"#481B6D",
|
||||
"#481C6E",
|
||||
"#481D6F",
|
||||
"#481F70",
|
||||
"#482071",
|
||||
"#482173",
|
||||
"#482374",
|
||||
"#482475",
|
||||
"#482576",
|
||||
"#482677",
|
||||
"#482878",
|
||||
"#482979",
|
||||
"#472A7A",
|
||||
"#472C7A",
|
||||
"#472D7B",
|
||||
"#472E7C",
|
||||
"#472F7D",
|
||||
"#46307E",
|
||||
"#46327E",
|
||||
"#46337F",
|
||||
"#463480",
|
||||
"#453581",
|
||||
"#453781",
|
||||
"#453882",
|
||||
"#443983",
|
||||
"#443A83",
|
||||
"#443B84",
|
||||
"#433D84",
|
||||
"#433E85",
|
||||
"#423F85",
|
||||
"#424086",
|
||||
"#424186",
|
||||
"#414287",
|
||||
"#414487",
|
||||
"#404588",
|
||||
"#404688",
|
||||
"#3F4788",
|
||||
"#3F4889",
|
||||
"#3E4989",
|
||||
"#3E4A89",
|
||||
"#3E4C8A",
|
||||
"#3D4D8A",
|
||||
"#3D4E8A",
|
||||
"#3C4F8A",
|
||||
"#3C508B",
|
||||
"#3B518B",
|
||||
"#3B528B",
|
||||
"#3A538B",
|
||||
"#3A548C",
|
||||
"#39558C",
|
||||
"#39568C",
|
||||
"#38588C",
|
||||
"#38598C",
|
||||
"#375A8C",
|
||||
"#375B8D",
|
||||
"#365C8D",
|
||||
"#365D8D",
|
||||
"#355E8D",
|
||||
"#355F8D",
|
||||
"#34608D",
|
||||
"#34618D",
|
||||
"#33628D",
|
||||
"#33638D",
|
||||
"#32648E",
|
||||
"#32658E",
|
||||
"#31668E",
|
||||
"#31678E",
|
||||
"#31688E",
|
||||
"#30698E",
|
||||
"#306A8E",
|
||||
"#2F6B8E",
|
||||
"#2F6C8E",
|
||||
"#2E6D8E",
|
||||
"#2E6E8E",
|
||||
"#2E6F8E",
|
||||
"#2D708E",
|
||||
"#2D718E",
|
||||
"#2C718E",
|
||||
"#2C728E",
|
||||
"#2C738E",
|
||||
"#2B748E",
|
||||
"#2B758E",
|
||||
"#2A768E",
|
||||
"#2A778E",
|
||||
"#2A788E",
|
||||
"#29798E",
|
||||
"#297A8E",
|
||||
"#297B8E",
|
||||
"#287C8E",
|
||||
"#287D8E",
|
||||
"#277E8E",
|
||||
"#277F8E",
|
||||
"#27808E",
|
||||
"#26818E",
|
||||
"#26828E",
|
||||
"#26828E",
|
||||
"#25838E",
|
||||
"#25848E",
|
||||
"#25858E",
|
||||
"#24868E",
|
||||
"#24878E",
|
||||
"#23888E",
|
||||
"#23898E",
|
||||
"#238A8D",
|
||||
"#228B8D",
|
||||
"#228C8D",
|
||||
"#228D8D",
|
||||
"#218E8D",
|
||||
"#218F8D",
|
||||
"#21908D",
|
||||
"#21918C",
|
||||
"#20928C",
|
||||
"#20928C",
|
||||
"#20938C",
|
||||
"#1F948C",
|
||||
"#1F958B",
|
||||
"#1F968B",
|
||||
"#1F978B",
|
||||
"#1F988B",
|
||||
"#1F998A",
|
||||
"#1F9A8A",
|
||||
"#1E9B8A",
|
||||
"#1E9C89",
|
||||
"#1E9D89",
|
||||
"#1F9E89",
|
||||
"#1F9F88",
|
||||
"#1FA088",
|
||||
"#1FA188",
|
||||
"#1FA187",
|
||||
"#1FA287",
|
||||
"#20A386",
|
||||
"#20A486",
|
||||
"#21A585",
|
||||
"#21A685",
|
||||
"#22A785",
|
||||
"#22A884",
|
||||
"#23A983",
|
||||
"#24AA83",
|
||||
"#25AB82",
|
||||
"#25AC82",
|
||||
"#26AD81",
|
||||
"#27AD81",
|
||||
"#28AE80",
|
||||
"#29AF7F",
|
||||
"#2AB07F",
|
||||
"#2CB17E",
|
||||
"#2DB27D",
|
||||
"#2EB37C",
|
||||
"#2FB47C",
|
||||
"#31B57B",
|
||||
"#32B67A",
|
||||
"#34B679",
|
||||
"#35B779",
|
||||
"#37B878",
|
||||
"#38B977",
|
||||
"#3ABA76",
|
||||
"#3BBB75",
|
||||
"#3DBC74",
|
||||
"#3FBC73",
|
||||
"#40BD72",
|
||||
"#42BE71",
|
||||
"#44BF70",
|
||||
"#46C06F",
|
||||
"#48C16E",
|
||||
"#4AC16D",
|
||||
"#4CC26C",
|
||||
"#4EC36B",
|
||||
"#50C46A",
|
||||
"#52C569",
|
||||
"#54C568",
|
||||
"#56C667",
|
||||
"#58C765",
|
||||
"#5AC864",
|
||||
"#5CC863",
|
||||
"#5EC962",
|
||||
"#60CA60",
|
||||
"#63CB5F",
|
||||
"#65CB5E",
|
||||
"#67CC5C",
|
||||
"#69CD5B",
|
||||
"#6CCD5A",
|
||||
"#6ECE58",
|
||||
"#70CF57",
|
||||
"#73D056",
|
||||
"#75D054",
|
||||
"#77D153",
|
||||
"#7AD151",
|
||||
"#7CD250",
|
||||
"#7FD34E",
|
||||
"#81D34D",
|
||||
"#84D44B",
|
||||
"#86D549",
|
||||
"#89D548",
|
||||
"#8BD646",
|
||||
"#8ED645",
|
||||
"#90D743",
|
||||
"#93D741",
|
||||
"#95D840",
|
||||
"#98D83E",
|
||||
"#9BD93C",
|
||||
"#9DD93B",
|
||||
"#A0DA39",
|
||||
"#A2DA37",
|
||||
"#A5DB36",
|
||||
"#A8DB34",
|
||||
"#AADC32",
|
||||
"#ADDC30",
|
||||
"#B0DD2F",
|
||||
"#B2DD2D",
|
||||
"#B5DE2B",
|
||||
"#B8DE29",
|
||||
"#BADE28",
|
||||
"#BDDF26",
|
||||
"#C0DF25",
|
||||
"#C2DF23",
|
||||
"#C5E021",
|
||||
"#C8E020",
|
||||
"#CAE11F",
|
||||
"#CDE11D",
|
||||
"#D0E11C",
|
||||
"#D2E21B",
|
||||
"#D5E21A",
|
||||
"#D8E219",
|
||||
"#DAE319",
|
||||
"#DDE318",
|
||||
"#DFE318",
|
||||
"#E2E418",
|
||||
"#E5E419",
|
||||
"#E7E419",
|
||||
"#EAE51A",
|
||||
"#ECE51B",
|
||||
"#EFE51C",
|
||||
"#F1E51D",
|
||||
"#F4E61E",
|
||||
"#F6E620",
|
||||
"#F8E621",
|
||||
"#FBE723",
|
||||
"#FDE725"
|
||||
]
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
#include <rtltcp_client.h>
|
||||
#include <imgui.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <new_module.h>
|
||||
#include <module.h>
|
||||
#include <gui/gui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <core.h>
|
||||
#include <options.h>
|
||||
#include <gui/style.h>
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
@ -17,6 +18,34 @@ SDRPP_MOD_INFO {
|
||||
/* Max instances */ 1
|
||||
};
|
||||
|
||||
ConfigManager config;
|
||||
|
||||
const double sampleRates[] = {
|
||||
250000,
|
||||
1024000,
|
||||
1536000,
|
||||
1792000,
|
||||
1920000,
|
||||
2048000,
|
||||
2160000,
|
||||
2560000,
|
||||
2880000,
|
||||
3200000
|
||||
};
|
||||
|
||||
const char* sampleRatesTxt[] = {
|
||||
"250KHz",
|
||||
"1.024MHz",
|
||||
"1.536MHz",
|
||||
"1.792MHz",
|
||||
"1.92MHz",
|
||||
"2.048MHz",
|
||||
"2.16MHz",
|
||||
"2.56MHz",
|
||||
"2.88MHz",
|
||||
"3.2MHz"
|
||||
};
|
||||
|
||||
class RTLTCPSourceModule : public ModuleManager::Instance {
|
||||
public:
|
||||
RTLTCPSourceModule(std::string name) {
|
||||
@ -24,6 +53,24 @@ public:
|
||||
|
||||
sampleRate = 2560000.0;
|
||||
|
||||
int srCount = sizeof(sampleRatesTxt) / sizeof(char*);
|
||||
for (int i = 0; i < srCount; i++) {
|
||||
srTxt += sampleRatesTxt[i];
|
||||
srTxt += '\0';
|
||||
}
|
||||
srId = 7;
|
||||
|
||||
config.aquire();
|
||||
std::string hostStr = config.conf["host"];
|
||||
port = config.conf["port"];
|
||||
directSamplingMode = config.conf["directSamplingMode"];
|
||||
rtlAGC = config.conf["rtlAGC"];
|
||||
tunerAGC = config.conf["tunerAGC"];
|
||||
gain = config.conf["gainIndex"];
|
||||
hostStr = hostStr.substr(0, 1023);
|
||||
strcpy(ip, hostStr.c_str());
|
||||
config.release();
|
||||
|
||||
handler.ctx = this;
|
||||
handler.selectHandler = menuSelected;
|
||||
handler.deselectHandler = menuDeselected;
|
||||
@ -72,12 +119,13 @@ private:
|
||||
spdlog::error("Could not connect to {0}:{1}", _this->ip, _this->port);
|
||||
return;
|
||||
}
|
||||
spdlog::warn("Setting sample rate to {0}", _this->sampleRate);
|
||||
_this->client.setFrequency(_this->freq);
|
||||
_this->client.setSampleRate(_this->sampleRate);
|
||||
_this->client.setGainIndex(_this->gain);
|
||||
_this->client.setGainMode(!_this->tunerAGC);
|
||||
_this->client.setDirectSampling(_this->directSamplingMode);
|
||||
_this->client.setAGCMode(_this->rtlAGC);
|
||||
_this->client.setGainIndex(_this->gain);
|
||||
_this->running = true;
|
||||
_this->workerThread = std::thread(worker, _this);
|
||||
spdlog::info("RTLTCPSourceModule '{0}': Start!", _this->name);
|
||||
@ -110,22 +158,36 @@ private:
|
||||
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||
float portWidth = ImGui::CalcTextSize("00000").x + 20;
|
||||
|
||||
if (_this->running) { style::beginDisabled(); }
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth - portWidth);
|
||||
ImGui::InputText(CONCAT("##_ip_select_", _this->name), _this->ip, 1024);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(portWidth);
|
||||
ImGui::InputInt(CONCAT("##_port_select_", _this->name), &_this->port, 0);
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth);
|
||||
if (ImGui::Combo(CONCAT("##_rtltcp_sr_", _this->name), &_this->srId, _this->srTxt.c_str())) {
|
||||
_this->sampleRate = sampleRates[_this->srId];
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
}
|
||||
|
||||
if (_this->running) { style::endDisabled(); }
|
||||
|
||||
ImGui::SetNextItemWidth(ImGui::CalcTextSize("OOOOOOOOOO").x);
|
||||
if (ImGui::Combo("Direct sampling", &_this->directSamplingMode, "Disabled\0I branch\0Q branch\0")) {
|
||||
if (ImGui::Combo(CONCAT("Direct Sampling##_rtltcp_ds_", _this->name), &_this->directSamplingMode, "Disabled\0I branch\0Q branch\0")) {
|
||||
if (_this->running) {
|
||||
_this->client.setDirectSampling(_this->directSamplingMode);
|
||||
_this->client.setGainIndex(_this->gain);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox("RTL AGC", &_this->rtlAGC)) {
|
||||
if (_this->running) {
|
||||
_this->client.setAGCMode(_this->rtlAGC);
|
||||
if (!_this->rtlAGC) {
|
||||
_this->client.setGainIndex(_this->gain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,18 +212,17 @@ private:
|
||||
|
||||
static void worker(void* ctx) {
|
||||
RTLTCPSourceModule* _this = (RTLTCPSourceModule*)ctx;
|
||||
int blockSize = _this->sampleRate / 200.0;
|
||||
int blockSize = _this->sampleRate / 200.0f;
|
||||
uint8_t* inBuf = new uint8_t[blockSize * 2];
|
||||
|
||||
while (true) {
|
||||
// Read samples here
|
||||
_this->client.receiveData(inBuf, blockSize * 2);
|
||||
if (_this->stream.aquire() < 0) { break; }
|
||||
for (int i = 0; i < blockSize; i++) {
|
||||
_this->stream.data[i].q = ((double)inBuf[i * 2] - 128.0) / 128.0;
|
||||
_this->stream.data[i].i = ((double)inBuf[(i * 2) + 1] - 128.0) / 128.0;
|
||||
_this->stream.writeBuf[i].q = ((double)inBuf[i * 2] - 128.0) / 128.0;
|
||||
_this->stream.writeBuf[i].i = ((double)inBuf[(i * 2) + 1] - 128.0) / 128.0;
|
||||
}
|
||||
_this->stream.write(blockSize);
|
||||
if (!_this->stream.swap(blockSize)) { break; };
|
||||
}
|
||||
|
||||
delete[] inBuf;
|
||||
@ -182,10 +243,22 @@ private:
|
||||
bool rtlAGC = false;
|
||||
bool tunerAGC = false;
|
||||
int directSamplingMode = 0;
|
||||
int srId = 0;
|
||||
|
||||
std::string srTxt = "";
|
||||
};
|
||||
|
||||
MOD_EXPORT void _INIT_() {
|
||||
// Do your one time init here
|
||||
config.setPath(options::opts.root + "/rtl_tcp_config.json");
|
||||
json defConf;
|
||||
defConf["host"] = "localhost";
|
||||
defConf["port"] = 1234;
|
||||
defConf["directSamplingMode"] = 0;
|
||||
defConf["rtlAGC"] = false;
|
||||
defConf["tunerAGC"] = false;
|
||||
defConf["gainIndex"] = 0;
|
||||
config.load(defConf);
|
||||
config.enableAutoSave();
|
||||
}
|
||||
|
||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||
@ -197,5 +270,6 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
|
||||
}
|
||||
|
||||
MOD_EXPORT void _END_() {
|
||||
// Do your one shutdown here
|
||||
config.disableAutoSave();
|
||||
config.save();
|
||||
}
|
@ -43,6 +43,9 @@ public:
|
||||
struct addrinfo *ptr = NULL;
|
||||
struct addrinfo hints;
|
||||
|
||||
WSADATA wsaData;
|
||||
WSAStartup(MAKEWORD(2,2), &wsaData);
|
||||
|
||||
ZeroMemory( &hints, sizeof(hints) );
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
@ -54,7 +57,7 @@ public:
|
||||
int iResult = getaddrinfo(host, buf, &hints, &result);
|
||||
if (iResult != 0) {
|
||||
// TODO: log error
|
||||
printf("A");
|
||||
printf("\n%s\n", gai_strerror(iResult));
|
||||
WSACleanup();
|
||||
return false;
|
||||
}
|
||||
|
@ -11,20 +11,21 @@
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
#define ADC_RATE 128000000
|
||||
#define ADC_RATE 64000000
|
||||
#define XFER_TIMEOUT 5000
|
||||
|
||||
#define SEL0 (8) // SEL0 GPIO26
|
||||
#define SEL1 (16) // SEL1 GPIO27
|
||||
|
||||
MOD_INFO {
|
||||
/* Name: */ "rx888_source",
|
||||
/* Description: */ "RX888 input module for SDR++",
|
||||
/* Author: */ "Ryzerth",
|
||||
/* Version: */ "0.1.0"
|
||||
SDRPP_MOD_INFO {
|
||||
/* Name: */ "rx888_source",
|
||||
/* Description: */ "RX888 source module for SDR++",
|
||||
/* Author: */ "Ryzerth",
|
||||
/* Version: */ 0, 1, 0,
|
||||
/* Max instances */ 1
|
||||
};
|
||||
|
||||
class RX888SourceModule {
|
||||
class RX888SourceModule : public ModuleManager::Instance {
|
||||
public:
|
||||
RX888SourceModule(std::string name) {
|
||||
this->name = name;
|
||||
@ -58,6 +59,18 @@ public:
|
||||
spdlog::info("RX888SourceModule '{0}': Instance deleted!", name);
|
||||
}
|
||||
|
||||
void enable() {
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
void disable() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
bool isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static void menuSelected(void* ctx) {
|
||||
@ -125,7 +138,7 @@ private:
|
||||
|
||||
static void _usbWorker(RX888SourceModule* _this) {
|
||||
// Calculate hardware block siz
|
||||
int realBlockSize = ADC_RATE / 200;
|
||||
int realBlockSize = ADC_RATE / 100;
|
||||
int i;
|
||||
for (i = 1; i < realBlockSize; i = (i << 1));
|
||||
realBlockSize = (i >> 1);
|
||||
@ -170,9 +183,8 @@ private:
|
||||
// Check if the incomming data is bulk I/Q and end transfer
|
||||
if (EndPt->Attributes == 2) {
|
||||
if (EndPt->FinishDataXfer((PUCHAR)buffer, rLen, &inOvLap, context)) {
|
||||
if (_this->realStream.aquire() < 0) { break; }
|
||||
memcpy(_this->realStream.data, buffer, rLen);
|
||||
_this->realStream.write(rLen / 2);
|
||||
memcpy(_this->realStream.writeBuf, buffer, rLen);
|
||||
_this->realStream.swap(rLen / 2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,7 +208,7 @@ private:
|
||||
|
||||
while (count >= 0) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
iqbuffer[i].q = (float)_this->realStream.data[i] / 32768.0f;
|
||||
iqbuffer[i].q = (float)_this->realStream.readBuf[i] / 32768.0f;
|
||||
}
|
||||
_this->realStream.flush();
|
||||
|
||||
@ -205,21 +217,20 @@ private:
|
||||
lv_32fc_t phaseDelta = lv_cmake(std::cos(delta), std::sin(delta));
|
||||
|
||||
// Apply translation
|
||||
if (_this->stream.aquire() < 0) { break; }
|
||||
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)_this->stream.data, (lv_32fc_t*)iqbuffer, phaseDelta, &phase, count);
|
||||
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)_this->stream.writeBuf, (lv_32fc_t*)iqbuffer, phaseDelta, &phase, count);
|
||||
|
||||
// Decimate
|
||||
blockSize = count;
|
||||
for (int d = 0; d < (_this->decimation - 1); d++) {
|
||||
blockSize = (blockSize >> 1);
|
||||
for (int i = 0; i < blockSize; i++) {
|
||||
_this->stream.data[i].i = (_this->stream.data[i*2].i + _this->stream.data[(i*2)+1].i) * 0.5f;
|
||||
_this->stream.data[i].q = (_this->stream.data[i*2].q + _this->stream.data[(i*2)+1].q) * 0.5f;
|
||||
_this->stream.writeBuf[i].i = (_this->stream.writeBuf[i*2].i + _this->stream.writeBuf[(i*2)+1].i) * 0.5f;
|
||||
_this->stream.writeBuf[i].q = (_this->stream.writeBuf[i*2].q + _this->stream.writeBuf[(i*2)+1].q) * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
// Write to output stream
|
||||
_this->stream.write(blockSize);
|
||||
_this->stream.swap(blockSize);
|
||||
|
||||
// Read from real stream
|
||||
count = _this->realStream.read();
|
||||
@ -242,20 +253,20 @@ private:
|
||||
double sampleRate;
|
||||
int decimation;
|
||||
bool running = false;
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
MOD_EXPORT void _INIT_() {
|
||||
|
||||
}
|
||||
|
||||
MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) {
|
||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||
return new RX888SourceModule(name);
|
||||
}
|
||||
|
||||
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||
MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
|
||||
delete (RX888SourceModule*)instance;
|
||||
}
|
||||
|
||||
MOD_EXPORT void _STOP_() {
|
||||
MOD_EXPORT void _END_() {
|
||||
|
||||
}
|
@ -19,6 +19,8 @@ if (MSVC)
|
||||
|
||||
# Misc headers
|
||||
target_include_directories(sdrpp_core PUBLIC "C:/Program Files/PothosSDR/include/")
|
||||
|
||||
target_link_libraries(sdrpp_core PUBLIC SoapySDR)
|
||||
else (MSVC)
|
||||
find_package(PkgConfig)
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
#include <imgui.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <new_module.h>
|
||||
#include <module.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/widgets/stepped_slider.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <SoapySDR/Device.hpp>
|
||||
#include <SoapySDR/Modules.hpp>
|
||||
@ -67,6 +68,14 @@ public:
|
||||
bool isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string to_string_with_precision(const T a_value, const int n = 6) {
|
||||
std::ostringstream out;
|
||||
out.precision(n);
|
||||
out << std::fixed << a_value;
|
||||
return out.str();
|
||||
}
|
||||
|
||||
private:
|
||||
void refresh() {
|
||||
@ -79,6 +88,21 @@ private:
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
float selectBwBySr(double samplerate) {
|
||||
float cur = bandwidthList[1];
|
||||
std::vector<float> bwListReversed = bandwidthList;
|
||||
std::reverse(bwListReversed.begin(), bwListReversed.end());
|
||||
for(auto bw : bwListReversed) {
|
||||
if(bw >= samplerate) {
|
||||
cur = bw;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
spdlog::info("Bandwidth for samplerate {0} is {1}", samplerate, cur);
|
||||
return cur;
|
||||
}
|
||||
|
||||
void selectSampleRate(double samplerate) {
|
||||
spdlog::info("Setting sample rate to {0}", samplerate);
|
||||
@ -131,19 +155,42 @@ private:
|
||||
gainList = dev->listGains(SOAPY_SDR_RX, channelId);
|
||||
delete[] uiGains;
|
||||
uiGains = new float[gainList.size()];
|
||||
gainRanges.clear();
|
||||
|
||||
for (auto gain : gainList) {
|
||||
gainRanges.push_back(dev->getGainRange(SOAPY_SDR_RX, channelId, gain));
|
||||
}
|
||||
|
||||
SoapySDR::RangeList bandwidthRange = dev->getBandwidthRange(SOAPY_SDR_RX, channelId);
|
||||
|
||||
txtBwList = "";
|
||||
bandwidthList.clear();
|
||||
bandwidthList.push_back(-1);
|
||||
txtBwList += "Auto";
|
||||
txtBwList += '\0';
|
||||
|
||||
for(auto bwr : bandwidthRange) {
|
||||
float bw = bwr.minimum();
|
||||
bandwidthList.push_back(bw);
|
||||
if (bw > 1.0e3 && bw <= 1.0e6) {
|
||||
txtBwList += to_string_with_precision((bw / 1.0e3), 2) + " kHz";
|
||||
} else if (bw > 1.0e6) {
|
||||
txtBwList += to_string_with_precision((bw / 1.0e6), 2) + " MHz";
|
||||
} else {
|
||||
txtBwList += to_string_with_precision(bw, 0);
|
||||
}
|
||||
txtBwList += '\0';
|
||||
}
|
||||
|
||||
sampleRates = dev->listSampleRates(SOAPY_SDR_RX, channelId);
|
||||
txtSrList = "";
|
||||
for (double sr : sampleRates) {
|
||||
if (sr > 1.0e3 && sr <= 1.0e6) {
|
||||
txtSrList += std::to_string((sr / 1.0e3)) + " kHz";
|
||||
txtSrList += to_string_with_precision((sr / 1.0e3), 2) + " kHz";
|
||||
} else if (sr > 1.0e6) {
|
||||
txtSrList += std::to_string((sr / 1.0e6)) + " MHz";
|
||||
txtSrList += to_string_with_precision((sr / 1.0e6), 2) + " MHz";
|
||||
} else {
|
||||
txtSrList += std::to_string((int) sr);
|
||||
txtSrList += to_string_with_precision(sr, 0);
|
||||
}
|
||||
txtSrList += '\0';
|
||||
}
|
||||
@ -164,6 +211,11 @@ private:
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if(config.conf["devices"][name].contains("bandwidth")) {
|
||||
uiBandwidthId = config.conf["devices"][name]["bandwidth"];
|
||||
} else if(bandwidthList.size() > 2) {
|
||||
uiBandwidthId = 0;
|
||||
}
|
||||
if (hasAgc && config.conf["devices"][name].contains("agc")) {
|
||||
agc = config.conf["devices"][name]["agc"];
|
||||
}
|
||||
@ -183,6 +235,8 @@ private:
|
||||
uiGains[i] = gainRanges[i].minimum();
|
||||
i++;
|
||||
}
|
||||
if(bandwidthList.size() > 2)
|
||||
uiBandwidthId = 0;
|
||||
if (hasAgc) {
|
||||
agc = false;
|
||||
}
|
||||
@ -200,6 +254,8 @@ private:
|
||||
conf["gains"][gain] = uiGains[i];
|
||||
i++;
|
||||
}
|
||||
if(bandwidthList.size() > 2)
|
||||
conf["bandwidth"] = uiBandwidthId;
|
||||
if (hasAgc) {
|
||||
conf["agc"] = agc;
|
||||
}
|
||||
@ -228,16 +284,23 @@ private:
|
||||
|
||||
_this->dev->setSampleRate(SOAPY_SDR_RX, _this->channelId, _this->sampleRate);
|
||||
|
||||
if(_this->bandwidthList.size() > 2) {
|
||||
if(_this->bandwidthList[_this->uiBandwidthId] == -1)
|
||||
_this->dev->setBandwidth(SOAPY_SDR_RX, _this->channelId, _this->selectBwBySr(_this->sampleRates[_this->srId]));
|
||||
else
|
||||
_this->dev->setBandwidth(SOAPY_SDR_RX, _this->channelId, _this->bandwidthList[_this->uiBandwidthId]);
|
||||
}
|
||||
|
||||
if (_this->hasAgc) {
|
||||
_this->dev->setGainMode(SOAPY_SDR_RX, _this->channelId, _this->agc);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (auto gain : _this->gainList) {
|
||||
_this->dev->setGain(SOAPY_SDR_RX, _this->channelId, gain, _this->uiGains[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (_this->hasAgc) {
|
||||
_this->dev->setGainMode(SOAPY_SDR_RX, _this->channelId, _this->agc);
|
||||
}
|
||||
|
||||
_this->dev->setFrequency(SOAPY_SDR_RX, _this->channelId, _this->freq);
|
||||
|
||||
_this->devStream = _this->dev->setupStream(SOAPY_SDR_RX, "CF32");
|
||||
@ -291,6 +354,8 @@ private:
|
||||
|
||||
if (ImGui::Combo(CONCAT("##_sr_select_", _this->name), &_this->srId, _this->txtSrList.c_str())) {
|
||||
_this->selectSampleRate(_this->sampleRates[_this->srId]);
|
||||
if(_this->bandwidthList.size() > 2 && _this->running && _this->bandwidthList[_this->uiBandwidthId] == -1)
|
||||
_this->dev->setBandwidth(SOAPY_SDR_RX, _this->channelId, _this->selectBwBySr(_this->sampleRates[_this->srId]));
|
||||
_this->saveCurrent();
|
||||
}
|
||||
|
||||
@ -334,8 +399,14 @@ private:
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(gainNameLen);
|
||||
ImGui::SetNextItemWidth(menuWidth - gainNameLen);
|
||||
if (ImGui::SliderFloat((gain + std::string("##_gain_sel_") + _this->name).c_str(), &_this->uiGains[i],
|
||||
_this->gainRanges[i].minimum(), _this->gainRanges[i].maximum())) {
|
||||
float step = _this->gainRanges[i].step();
|
||||
bool res;
|
||||
if(step == 0.0f) {
|
||||
res = ImGui::SliderFloat((std::string("##_gain_sel_") + _this->name + gain).c_str(), &_this->uiGains[i], _this->gainRanges[i].minimum(), _this->gainRanges[i].maximum());
|
||||
} else {
|
||||
res = ImGui::SliderFloatWithSteps((std::string("##_gain_sel_") + _this->name + gain).c_str(), &_this->uiGains[i], _this->gainRanges[i].minimum(), _this->gainRanges[i].maximum(), step);
|
||||
}
|
||||
if(res) {
|
||||
if (_this->running) {
|
||||
_this->dev->setGain(SOAPY_SDR_RX, _this->channelId, gain, _this->uiGains[i]);
|
||||
}
|
||||
@ -343,6 +414,23 @@ private:
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if(_this->bandwidthList.size() > 2) {
|
||||
float bwLen = ImGui::CalcTextSize("Bandwidth").x + 5.0f;
|
||||
ImGui::Text("Bandwidth");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(bwLen);
|
||||
ImGui::SetNextItemWidth(menuWidth - bwLen);
|
||||
|
||||
if (ImGui::Combo(CONCAT("##_bw_select_", _this->name), &_this->uiBandwidthId, _this->txtBwList.c_str())) {
|
||||
if(_this->running) {
|
||||
if(_this->bandwidthList[_this->uiBandwidthId] == -1)
|
||||
_this->dev->setBandwidth(SOAPY_SDR_RX, _this->channelId, _this->selectBwBySr(_this->sampleRates[_this->srId]));
|
||||
else
|
||||
_this->dev->setBandwidth(SOAPY_SDR_RX, _this->channelId, _this->bandwidthList[_this->uiBandwidthId]);
|
||||
}
|
||||
_this->saveCurrent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _worker(SoapyModule* _this) {
|
||||
@ -351,12 +439,11 @@ private:
|
||||
long long timeMs = 0;
|
||||
|
||||
while (_this->running) {
|
||||
if (_this->stream.aquire() < 0) { break; }
|
||||
int res = _this->dev->readStream(_this->devStream, (void**)&_this->stream.data, blockSize, flags, timeMs);
|
||||
int res = _this->dev->readStream(_this->devStream, (void**)&_this->stream.writeBuf, blockSize, flags, timeMs);
|
||||
if (res < 1) {
|
||||
continue;
|
||||
}
|
||||
_this->stream.write(res);
|
||||
if (!_this->stream.swap(res)) { return; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -384,6 +471,9 @@ private:
|
||||
int channelId = 0;
|
||||
std::vector<std::string> gainList;
|
||||
std::vector<SoapySDR::Range> gainRanges;
|
||||
int uiBandwidthId = 0;
|
||||
std::vector<float> bandwidthList;
|
||||
std::string txtBwList;
|
||||
};
|
||||
|
||||
MOD_EXPORT void _INIT_() {
|
||||
@ -406,4 +496,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
|
||||
MOD_EXPORT void _END_() {
|
||||
config.disableAutoSave();
|
||||
config.save();
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <spyserver_client.h>
|
||||
#include <imgui.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <new_module.h>
|
||||
#include <module.h>
|
||||
#include <gui/gui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <core.h>
|
||||
|
@ -74,7 +74,7 @@ bool SpyServerClient::connectToSpyserver(char* host, int port) {
|
||||
unsigned long mode = 1;
|
||||
ioctlsocket(sock, FIONBIO, &mode);
|
||||
#else
|
||||
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK)
|
||||
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
|
||||
#endif
|
||||
|
||||
connected = true;
|
||||
@ -117,7 +117,7 @@ int SpyServerClient::receive(char* buf, int count) {
|
||||
#ifdef _WIN32
|
||||
return checkError(recv(sock, (char*)buf, count, 0), count);
|
||||
#else
|
||||
return checkError(read(sockfd, buf, count));
|
||||
return checkError(read(sockfd, buf, count), count);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user