56 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
59 changed files with 3764 additions and 294 deletions

View File

@ -1,4 +1,4 @@
name: CMake
name: Linux Build
on: [push]
@ -17,8 +17,11 @@ jobs:
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 libiio-dev libad9361-dev portaudio19-dev
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

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

@ -6,6 +6,7 @@
#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,11 +41,11 @@ public:
refresh();
selectFirst();
// 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);
}
@ -121,22 +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';
}
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);
@ -166,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));
@ -213,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(); }
@ -239,6 +305,11 @@ private:
airspyhf_set_hf_agc_threshold(_this->openDev, _this->agcMode - 1);
}
}
if (_this->selectedSerStr != "") {
config.aquire();
config.conf["devices"][_this->selectedSerStr]["agcMode"] = _this->agcMode;
config.release(true);
}
}
ImGui::Text("HF LNA");
@ -247,6 +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");
@ -257,16 +333,18 @@ 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;
}
@ -284,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;
@ -292,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) {
@ -309,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

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

@ -102,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;
@ -125,6 +126,8 @@ int sdrpp_main(int argc, char *argv[]) {
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();
@ -155,16 +158,35 @@ int sdrpp_main(int argc, char *argv[]) {
core::configManager.load(defConfig);
core::configManager.enableAutoSave();
// Fix config
core::configManager.aquire();
for (auto const& item : defConfig.items()) {
if (!core::configManager.conf.contains(item.key())) {
spdlog::warn("Missing key in config {0}, repairing", item.key());
core::configManager.conf[item.key()] = defConfig[item.key()];
}
}
core::configManager.release(true);
// Setup window
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit()) {
return 1;
}
#ifdef __APPLE__
// GL 3.2 + GLSL 150
const char* glsl_version = "#version 150";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
#else
// GL 3.0 + GLSL 120
const char* glsl_version = "#version 120";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
#endif
core::configManager.aquire();
int winWidth = core::configManager.conf["windowSize"]["w"];
@ -230,7 +252,7 @@ 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);
if (!style::setDarkStyle(resDir)) { return -1; }

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

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

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

@ -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;
@ -121,6 +123,7 @@ 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);
@ -181,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();
@ -276,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;
@ -286,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;
@ -439,7 +463,7 @@ void drawWindow() {
gui::freqSelect.draw();
//ImGui::SameLine();
ImGui::SameLine();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 9);
if (centerTuning) {
@ -528,6 +552,7 @@ void drawWindow() {
//sigpath::signalPath.setDCBiasCorrection(dcbias.val);
}
ImGui::Checkbox("Show demo window", &demoWindow);
ImGui::Checkbox("Experimental zoom", &experimentalZoom);
ImGui::Spacing();
}
@ -559,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

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

@ -25,7 +25,7 @@ namespace style {
ImGui::GetStyle().ScrollbarRounding = 0.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(), 42.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();
@ -52,7 +52,7 @@ namespace style {
ImGui::GetStyle().ScrollbarRounding = 0.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(), 42.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();

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

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

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

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 )

View File

@ -34,6 +34,7 @@ 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/

View File

@ -15,6 +15,9 @@ 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/

View File

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

@ -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,7 +3,7 @@
![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.
![CMake](https://github.com/AlexandreRouma/SDRPlusPlus/workflows/CMake/badge.svg)
![Linux Build](https://github.com/AlexandreRouma/SDRPlusPlus/workflows/Linux%20Build/badge.svg)
* [Patreon](https://patreon.com/ryzerth)
* [Discord Server](https://discord.gg/aFgWjyD)
@ -31,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
@ -126,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
@ -179,9 +186,9 @@ Then, you need to edit the `root_dev/config` file to point to the modules that w
You also need to change the location of the resource and module directories, for development, I recommend:
```json
...
"modulesDirectory": "../root_dev/modules",
"modulesDirectory": "./root_dev/modules",
...
"resourcesDirectory": "../root_dev/res",
"resourcesDirectory": "./root_dev/res",
...
```
@ -191,12 +198,12 @@ Off cours, remember to add entries for all modules that were built and that you
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++
@ -217,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

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

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

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

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

@ -2,6 +2,7 @@
#include <spdlog/spdlog.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_() {