89 Commits

Author SHA1 Message Date
a2d93915e8 Added airspy_source to defaults 2021-02-04 14:53:12 +01:00
29e9db184f Fixed small bug in SSB tuning 2021-02-02 21:49:35 +01:00
2f93c7ae58 Fixed wrong sample rate at startup 2021-01-30 02:21:30 +01:00
4abfe407da Removed bad files 2021-01-29 18:29:02 +01:00
9b27e81091 Fixed autobuild 3 2021-01-29 18:26:18 +01:00
39787743fd Fixed autobuild 2 2021-01-29 17:44:56 +01:00
22e47807b8 Fixed autobuild 2021-01-29 17:42:02 +01:00
898525a6d8 Added automatic build to actions 2021-01-29 17:24:10 +01:00
1ebcfe7d80 UI improvements 2021-01-29 16:50:57 +01:00
1dbc39b970 Added persistant config to airspyhf_source 2021-01-29 16:15:13 +01:00
80f5f6c288 Added persistant config to airspy_source + bugfix 2021-01-29 15:07:45 +01:00
b18acd469f Fixed missing dependency in CI 2021-01-28 21:13:09 +01:00
cefcd18269 Added airspy module + changes to the UI for scaling 2021-01-28 21:10:53 +01:00
4de3ac176d Update cmake.yml 2021-01-11 21:47:35 +01:00
afd5699ff1 Added new contributors + fixed waterfall bug 2021-01-11 03:19:09 +01:00
b79461e3ce Merge pull request #60 from mnhauke/master
Add bandplan for German mobile networks
2021-01-03 02:49:20 +01:00
de6ab8ecdf Add bandplan for LTE bands used in Germany 2021-01-02 23:43:49 +01:00
d0180d42a8 Add bandplan for German mobile networks 2021-01-02 21:59:13 +01:00
2e504b40f6 Fixed invalid colormap 2021-01-02 15:18:57 +01:00
9b00304c29 Fixed resampling bug + added waterfall colormap selection + general bugfix 2020-12-31 14:42:09 +01:00
979928ded8 Fixed resampling bug + added waterfall colormap selection + general bugfix 2020-12-31 14:26:12 +01:00
7c4e442432 Fixed gain not updated on RTL-SDR 2020-12-28 14:47:34 +01:00
0dd445f101 Merge pull request #55 from cropinghigh/master
Stepped sliders+bandwidth
2020-12-28 14:40:30 +01:00
f217804838 push before merge 2020-12-28 14:39:30 +01:00
9a630fff06 Merge branch 'master' of https://github.com/cropinghigh/SDRPlusPlus 2020-12-28 16:06:34 +03:00
db508214d7 Merge branch 'master' of https://github.com/AlexandreRouma/SDRPlusPlus 2020-12-28 16:05:57 +03:00
8e764f48ae Fix unusable bw 2020-12-28 16:05:35 +03:00
2583063f5f Update
Update
2020-12-27 15:30:51 +03:00
dd4ec22b39 + contributors list 2020-12-26 23:12:09 +01:00
42dbcec93f Update
Update
2020-12-27 01:11:05 +03:00
9bbf634f5d Merge pull request #52 from zakrent/master
Added SIMD to polyphase resampler
2020-12-26 23:08:45 +01:00
d6b09759de Merge pull request #51 from cropinghigh/patch-1
Addition to linux guide
2020-12-26 23:08:00 +01:00
5bb8a943ad push before merge 2020-12-26 23:02:07 +01:00
b370eda0d5 Fix bugs+move widget 2020-12-27 00:56:39 +03:00
69bcbf6f27 :Added SIMD to polyphase resampler 2020-12-26 22:18:34 +01:00
04823abb83 Fix bugs 2020-12-26 21:36:16 +03:00
7269a0ea12 Merge branch 'patch-1' of https://github.com/cropinghigh/SDRPlusPlus 2020-12-26 21:02:43 +03:00
153b58fbbd Stepped sliders 2020-12-26 21:00:09 +03:00
149af55e61 Addition to linux guide 2020-12-26 11:47:37 +03:00
9cac95fd82 push before merge 2020-12-26 00:48:12 +01:00
09498f3b18 Merge pull request #50 from AlexandreRouma/revert-49-master
Revert " Added recorder volume meter "
2020-12-26 00:47:22 +01:00
bb919d0f32 Revert " Added recorder volume meter " 2020-12-26 00:46:52 +01:00
6d0abd73a5 Merge pull request #49 from zakrent/master
Added recorder volume meter
2020-12-26 00:42:32 +01:00
717f2a822b Merge branch 'master' of https://github.com/zakrent/SDRPlusPlus 2020-12-26 01:17:37 +01:00
8946b4b4b6 Added recorder volume meter 2020-12-26 01:07:07 +01:00
db279d2b36 Fixed audio freeze on linux 2020-12-25 19:58:52 +01:00
bb7965b3c4 Fixed luckup bug 2020-12-25 18:17:43 +01:00
a33fe5a4cc Added cropinghigh to contributor's list 2020-12-25 17:22:24 +01:00
4a03f0870c Merge pull request #47 from cropinghigh/patch-3
Update russian bandplan
2020-12-25 17:10:33 +01:00
bfe15aff19 Merge pull request #48 from AlexandreRouma/double_bufferd_streams
switched all streams to double buffering
2020-12-25 17:10:13 +01:00
42bc2d01f7 switched all streams to double buffering 2020-12-25 16:58:07 +01:00
0cb9fc0df8 Update russian bandplan 2020-12-25 16:39:24 +03:00
450896b122 OpenGL version fix for shitty SoCs 2020-12-24 23:38:45 +01:00
cc0b89dbe2 fixed wrong dependency in readme 2020-12-24 19:44:17 +01:00
c887b96a77 fixed wrong dependency in readme 2020-12-24 19:44:07 +01:00
22541ae0f4 updated readme 2020-12-24 16:27:12 +01:00
d83da38d79 Added windows build script 2020-12-24 14:43:14 +01:00
b21f8abbd6 fixed missing module 2020-12-24 00:11:33 +01:00
e9aade4d0d fixed debian build again again 2020-12-23 23:39:34 +01:00
2bf2fff3d6 fixed debian build again 2020-12-23 23:34:56 +01:00
463a22fdfb fixed debian build 2 2020-12-23 23:20:28 +01:00
3175022b31 fixed debian build 2020-12-23 23:18:44 +01:00
504d910226 removed automated debian package build 2020-12-23 22:21:46 +01:00
c2769e1a72 fixed CI 2 2020-12-23 21:59:05 +01:00
7577253dbf fixed CI 2020-12-23 21:19:02 +01:00
e4c5b2dbd1 fixed directories 2020-12-23 21:10:24 +01:00
fafd76ff94 Added debian package build 2020-12-23 20:58:02 +01:00
e354d11820 Added debian package build 2020-12-23 20:57:41 +01:00
a3374c7eca fixed airspyhf module missing function 2020-12-23 20:22:20 +01:00
552b886cea fixed volk version in CI 2020-12-23 20:10:55 +01:00
ff9a19381b switch to more recent ubuntu version for CI 2020-12-23 20:09:08 +01:00
77aacc2e5d Fixed compiler version 2020-12-23 19:50:54 +01:00
6a1fa2c13b added more recent GCC version to CI 2020-12-23 19:44:34 +01:00
c3bb64bf6e Update cmake.yml 2020-12-23 19:39:14 +01:00
da68ab4ed0 Create cmake.yml 2020-12-23 19:36:12 +01:00
1aedf92bcd fixes to the rtl-tcp source 2020-12-23 19:23:47 +01:00
abcf484506 removed un-necessary directory 2020-12-23 00:12:52 +01:00
a93681a980 New system for band plans 2020-12-23 00:11:12 +01:00
a08758ea54 fixed directory bug on linux 2 2020-12-22 23:10:49 +01:00
0a0f5b8e8c fixed directory bug on linux 2020-12-22 23:04:46 +01:00
84f67a3ac1 updated readme about new directory system 2020-12-22 22:45:27 +01:00
22d18a9e58 new directory system on linux 2020-12-22 22:39:24 +01:00
d1a8425d43 Create FUNDING.yml 2020-12-22 21:42:16 +01:00
65d94f03e4 Fixed compile bugs 2020-12-22 21:23:49 +01:00
3a49041f27 other fix 2020-12-22 21:09:49 +01:00
eec2f7c4a0 fixed bug in spyserevr source 2020-12-22 20:44:49 +01:00
720df5ce89 Fixed build issues 2 2020-12-22 20:37:10 +01:00
d7cea16d4a Fixed build issues 2020-12-22 20:35:31 +01:00
d5c0fdd525 fixed linux build bug 2020-12-22 20:00:51 +01:00
94 changed files with 4169 additions and 403 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,3 @@
# These are supported funding model platforms
patreon: ryzerth

45
.github/workflows/cmake.yml vendored Normal file
View 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

View File

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

View 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
View 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();
}

View File

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

View File

@ -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");
@ -248,6 +318,11 @@ private:
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);
}
}
ImGui::Text("Attenuation");
@ -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();
}

View File

@ -14,3 +14,19 @@ include_directories("src/")
add_library(audio_sink SHARED ${SRC})
target_link_libraries(audio_sink PRIVATE sdrpp_core)
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)

View File

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

View File

@ -31,9 +31,6 @@ if (MSVC)
# Volk
target_link_libraries(sdrpp_core PUBLIC volk)
# SoapySDR
target_link_libraries(sdrpp_core PUBLIC SoapySDR)
# Glew
find_package(GLEW REQUIRED)
target_link_libraries(sdrpp_core PUBLIC GLEW::GLEW)
@ -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)

View File

@ -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;
@ -117,27 +138,62 @@ int sdrpp_main(int argc, char *argv[]) {
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");
core::configManager.setPath(options::opts.root + "/config.json");
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();

View File

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

View File

@ -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[] = {

View File

@ -1,5 +1,5 @@
#pragma once
#include <new_module.h>
#include <module.h>
namespace sdrpp_credits {
SDRPP_EXPORT const char* contributors[];

View File

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

View File

@ -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();
}
@ -89,10 +91,10 @@ namespace dsp {
workerThread.join();
}
for (auto const& in : inputs) {
for (auto& in : inputs) {
in->clearReadStop();
}
for (auto const& out : outputs) {
for (auto& out : outputs) {
out->clearWriteStop();
}
}

View File

@ -1,5 +1,6 @@
#pragma once
#include <dsp/block.h>
#include <string.h>
#define RING_BUF_SZ 1000000

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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
View 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;
}

View File

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

View File

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

View File

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

View File

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

View File

@ -14,5 +14,5 @@ namespace icons {
extern ImTextureID CENTER_TUNING;
GLuint loadTexture(std::string path);
void load();
bool load(std::string resDir);
}

View File

@ -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
@ -610,3 +636,7 @@ void drawWindow() {
void setViewBandwidthSlider(float bandwidth) {
bw = bandwidth;
}
bool sdrIsRunning() {
return playing;
}

View File

@ -6,3 +6,4 @@
void windowInit();
void drawWindow();
void setViewBandwidthSlider(float bandwidth);
bool sdrIsRunning();

View File

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

View File

@ -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 {
@ -34,6 +36,8 @@ namespace sourecmenu {
}
float itemWidth = ImGui::GetContentRegionAvailWidth();
if (sdrIsRunning()) { style::beginDisabled(); }
ImGui::SetNextItemWidth(itemWidth);
if (ImGui::Combo("##source", &sourceId, items.c_str())) {
sigpath::sourceManager.selectSource(sigpath::sourceManager.sourceNames[sourceId]);
@ -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);

View File

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

View File

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

View File

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

View File

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

View File

@ -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, ".");
}
}

View File

@ -2,7 +2,7 @@
#include <string>
#include <vector>
#include <map>
#include <new_module.h>
#include <module.h>
class Menu {
public:

View 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;
}
}

View 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");
}

View 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 ));
}
}
}

View 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));
}

View File

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

View File

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

View File

@ -1,4 +1,4 @@
#include <new_module.h>
#include <module.h>
#include <filesystem>
#include <spdlog/spdlog.h>

View File

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

View File

@ -1,6 +1,6 @@
#pragma once
#include <string>
#include <new_module.h>
#include <module.h>
namespace options {
struct CMDLineOptions {

View File

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

View File

@ -16,6 +16,7 @@ public:
class Sink {
public:
virtual ~Sink() {}
virtual void start() = 0;
virtual void stop() = 0;
virtual void menuHandler() = 0;

View File

@ -1,3 +1,3 @@
#pragma once
#define VERSION_STR "0.2.5_alpha"
#define VERSION_STR "0.2.5_beta"

View File

@ -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) {

View 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
View 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
View 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
View 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

View File

@ -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})

View File

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

View File

@ -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) {

View File

@ -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) {

View File

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

View File

@ -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) {

View File

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

View File

@ -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) {

View File

@ -3,6 +3,8 @@
![Screenshot](https://i.imgur.com/WejsiFN.png)
SDR++ is a cross-platform and open source SDR software with the aim of being bloat free and simple to use.
![Linux Build](https://github.com/AlexandreRouma/SDRPlusPlus/workflows/Linux%20Build/badge.svg)
* [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,9 +224,13 @@ 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)

View File

@ -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) {

View File

@ -1,7 +0,0 @@
{
"broadcast": "#0000FFFF",
"amateur": "#FF0000FF",
"aviation": "#00FF00FF",
"marine": "#00FFFFFF",
"military": "#FFFF00FF"
}

View 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
}
]
}

View 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
}
]
}

View 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"
]
}

View File

@ -0,0 +1,8 @@
{
"name": "Grey Scale",
"author": "Ryzerth",
"map": [
"#000000",
"#FFFFFF"
]
}

View 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"
]
}

View 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"
]
}

View 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"
]
}

View 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"
]
}

View 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"
]
}

View File

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

View File

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

View File

@ -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_() {
}

View File

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

View File

@ -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>
@ -68,6 +69,14 @@ public:
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() {
devList = SoapySDR::Device::enumerate();
@ -80,6 +89,21 @@ private:
}
}
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);
if (sampleRates.size() == 0) {
@ -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_() {

View File

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

View File

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