mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2025-02-02 12:54:44 +01:00
Fixed resampling bug + added waterfall colormap selection + general bugfix
This commit is contained in:
parent
7c4e442432
commit
979928ded8
@ -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;
|
||||
@ -156,6 +157,16 @@ 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()) {
|
||||
|
@ -132,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;
|
||||
|
||||
};
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include <dsp/block.h>
|
||||
#include <fftw3.h>
|
||||
#include <volk/volk.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <string.h>
|
||||
@ -89,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);
|
||||
}
|
||||
@ -109,15 +110,30 @@ 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; }
|
||||
|
||||
level = pow(10, ((10.0f * log10f(level)) - (_CorrectedFallRate * count)) / 10.0f);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
level = (fabsf(_in->readBuf[i]) * _ratio) + (level * (1.0f - _ratio));
|
||||
out.writeBuf[i] = _in->readBuf[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();
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
@ -127,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;
|
||||
|
||||
};
|
||||
|
@ -109,6 +109,7 @@ namespace dsp {
|
||||
|
||||
// Write to output
|
||||
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) {
|
||||
int phase = i % _interp;
|
||||
@ -155,10 +156,10 @@ namespace dsp {
|
||||
for(int tap = 0; tap < tapsPerPhase; tap++) {
|
||||
for (int phase = 0; phase < phases; phase++) {
|
||||
if(currentTap < tapCount) {
|
||||
tapPhases[phase][tap] = taps[currentTap++];
|
||||
tapPhases[(_interp - 1) - phase][tap] = taps[currentTap++];
|
||||
}
|
||||
else{
|
||||
tapPhases[phase][tap] = 0;
|
||||
tapPhases[(_interp - 1) - phase][tap] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -187,4 +188,80 @@ namespace dsp {
|
||||
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;
|
||||
|
||||
};
|
||||
}
|
49
core/src/gui/colormaps.cpp
Normal file
49
core/src/gui/colormaps.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include <gui/colormaps.h>
|
||||
#include <filesystem>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <fstream>
|
||||
#include <json.hpp>
|
||||
|
||||
using nlohmann::json;
|
||||
|
||||
namespace colormaps {
|
||||
std::map<std::string, Map> maps;
|
||||
|
||||
void loadMap(std::string path) {
|
||||
if (!std::filesystem::is_regular_file(path)) {
|
||||
spdlog::error("Could not load {0}, file doesn't exist", path);
|
||||
return;
|
||||
}
|
||||
|
||||
std::ifstream file(path.c_str());
|
||||
json data;
|
||||
file >> data;
|
||||
file.close();
|
||||
|
||||
Map map;
|
||||
std::vector<std::string> mapTxt;
|
||||
|
||||
try {
|
||||
map.name = data["name"];
|
||||
map.author = data["author"];
|
||||
mapTxt = data["map"].get<std::vector<std::string>>();
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
spdlog::error("Could not load {0}", path);
|
||||
return;
|
||||
}
|
||||
|
||||
map.entryCount = mapTxt.size();
|
||||
map.map = new float[mapTxt.size() * 3];
|
||||
int i = 0;
|
||||
for(auto const& col : mapTxt) {
|
||||
uint8_t r, g, b, a;
|
||||
map.map[i * 3] = std::stoi(col.substr(1, 2), NULL, 16);
|
||||
map.map[(i * 3) + 1] = std::stoi(col.substr(3, 2), NULL, 16);
|
||||
map.map[(i * 3) + 2] = std::stoi(col.substr(5, 2), NULL, 16);
|
||||
i++;
|
||||
}
|
||||
|
||||
maps[map.name] = map;
|
||||
}
|
||||
}
|
18
core/src/gui/colormaps.h
Normal file
18
core/src/gui/colormaps.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <module.h>
|
||||
#include <map>
|
||||
|
||||
namespace colormaps {
|
||||
struct Map {
|
||||
std::string name;
|
||||
std::string author;
|
||||
float* map;
|
||||
int entryCount;
|
||||
};
|
||||
|
||||
void loadMap(std::string path);
|
||||
|
||||
SDRPP_EXPORT std::map<std::string, Map> maps;
|
||||
}
|
@ -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,
|
||||
@ -121,6 +122,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 +183,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();
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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() {
|
||||
@ -568,6 +568,7 @@ namespace ImGui {
|
||||
}
|
||||
|
||||
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 +580,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 +645,7 @@ namespace ImGui {
|
||||
}
|
||||
|
||||
void WaterFall::setViewBandwidth(double bandWidth) {
|
||||
std::lock_guard<std::mutex> lck(buf_mtx);
|
||||
if (bandWidth == viewBandwidth) {
|
||||
return;
|
||||
}
|
||||
@ -651,6 +670,7 @@ namespace ImGui {
|
||||
}
|
||||
|
||||
void WaterFall::setViewOffset(double offset) {
|
||||
std::lock_guard<std::mutex> lck(buf_mtx);
|
||||
if (offset == viewOffset) {
|
||||
return;
|
||||
}
|
||||
@ -690,6 +710,7 @@ namespace ImGui {
|
||||
}
|
||||
|
||||
void WaterFall::setWaterfallMin(float min) {
|
||||
std::lock_guard<std::mutex> lck(buf_mtx);
|
||||
if (min == waterfallMin) {
|
||||
return;
|
||||
}
|
||||
@ -702,6 +723,7 @@ namespace ImGui {
|
||||
}
|
||||
|
||||
void WaterFall::setWaterfallMax(float max) {
|
||||
std::lock_guard<std::mutex> lck(buf_mtx);
|
||||
if (max == waterfallMax) {
|
||||
return;
|
||||
}
|
||||
@ -720,7 +742,7 @@ 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));
|
||||
@ -729,7 +751,6 @@ namespace ImGui {
|
||||
rawFFTs = (float*)malloc(rawFFTSize * waterfallHeight * sizeof(float));
|
||||
}
|
||||
memset(rawFFTs, 0, rawFFTSize * waterfallHeight * sizeof(float));
|
||||
if (lock) { buf_mtx.unlock(); }
|
||||
}
|
||||
|
||||
void WaterfallVFO::setOffset(double offset) {
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -42,7 +42,7 @@ public:
|
||||
|
||||
demod.init(&squelch.out);
|
||||
|
||||
agc.init(&demod.out, 1.0f / 125.0f);
|
||||
agc.init(&demod.out, 20.0f, bbSampRate);
|
||||
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
win.init(audioBW, audioBW, bbSampRate);
|
||||
@ -151,6 +151,11 @@ private:
|
||||
void setBandwidth(float bandWidth) {
|
||||
bw = bandWidth;
|
||||
_vfo->setBandwidth(bw);
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
win.setCutoff(audioBW);
|
||||
win.setTransWidth(audioBW);
|
||||
resamp.updateWindow(&win);
|
||||
}
|
||||
|
||||
void setSnapInterval(float snapInt) {
|
||||
|
@ -53,7 +53,7 @@ public:
|
||||
|
||||
c2r.init(&xlator.out);
|
||||
|
||||
agc.init(&c2r.out, 1.0f / 125.0f);
|
||||
agc.init(&c2r.out, 20.0f, audioSampRate);
|
||||
|
||||
m2s.init(&agc.out);
|
||||
}
|
||||
@ -104,6 +104,7 @@ public:
|
||||
}
|
||||
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);
|
||||
@ -170,7 +171,7 @@ private:
|
||||
|
||||
const float bwMax = 500;
|
||||
const float bwMin = 100;
|
||||
const float bbSampRate = 500;
|
||||
const float bbSampRate = 6000;
|
||||
|
||||
std::string uiPrefix;
|
||||
float snapInterval = 10;
|
||||
|
@ -42,7 +42,7 @@ public:
|
||||
|
||||
demod.init(&squelch.out, bbSampRate, bandWidth, dsp::SSBDemod::MODE_DSB);
|
||||
|
||||
agc.init(&demod.out, 1.0f / 125.0f);
|
||||
agc.init(&demod.out, 20.0f, bbSampRate);
|
||||
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw / 2.0f);
|
||||
win.init(audioBW, audioBW, bbSampRate);
|
||||
|
@ -42,7 +42,7 @@ public:
|
||||
|
||||
demod.init(&squelch.out, bbSampRate, bandWidth, dsp::SSBDemod::MODE_LSB);
|
||||
|
||||
agc.init(&demod.out, 1.0f / 125.0f);
|
||||
agc.init(&demod.out, 20.0f, bbSampRate);
|
||||
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw);
|
||||
win.init(audioBW, audioBW, bbSampRate);
|
||||
@ -151,6 +151,12 @@ private:
|
||||
void setBandwidth(float bandWidth) {
|
||||
bw = bandWidth;
|
||||
_vfo->setBandwidth(bw);
|
||||
demod.setBandWidth(bw);
|
||||
float audioBW = std::min<float>(audioSampRate / 2.0f, bw);
|
||||
win.setSampleRate(bbSampRate * resamp.getInterpolation());
|
||||
win.setCutoff(audioBW);
|
||||
win.setTransWidth(audioBW);
|
||||
resamp.updateWindow(&win);
|
||||
}
|
||||
|
||||
void setSnapInterval(float snapInt) {
|
||||
|
@ -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) {
|
||||
|
@ -253,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->readBuf[i].l * 0x7FFF;
|
||||
sampleBuf[(i * 2) + 1] = _this->audioStream->readBuf[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;
|
||||
|
19
root/res/colormaps/classic.json
Normal file
19
root/res/colormaps/classic.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "Classic",
|
||||
"author": "Youssef Touil",
|
||||
"map": [
|
||||
"#4A0000",
|
||||
"#750000",
|
||||
"#9F0000",
|
||||
"#C60000",
|
||||
"#FF0000",
|
||||
"#FE6D16",
|
||||
"#FFFF00",
|
||||
"#FFFFFF",
|
||||
"#1E90FF",
|
||||
"#000091",
|
||||
"#000050",
|
||||
"#000030",
|
||||
"#000020"
|
||||
]
|
||||
}
|
@ -212,7 +212,7 @@ 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) {
|
||||
|
@ -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_() {
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user