From 96f83ee55c2d2114f71163847799d61015a6217b Mon Sep 17 00:00:00 2001 From: Ryzerth Date: Fri, 30 Apr 2021 04:28:08 +0200 Subject: [PATCH] Added SNR meter --- core/src/gui/main_window.cpp | 12 ++++++ core/src/gui/widgets/snr_meter.cpp | 43 ++++++++++++++++++++ core/src/gui/widgets/snr_meter.h | 6 +++ core/src/gui/widgets/waterfall.cpp | 63 ++++++++++++++++++++++++++++-- core/src/gui/widgets/waterfall.h | 3 ++ 5 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 core/src/gui/widgets/snr_meter.cpp create mode 100644 core/src/gui/widgets/snr_meter.h diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index 5de9ea9c..a3b3b990 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -32,6 +32,7 @@ #include #include #include +#include int fftSize = 8192 * 8; @@ -103,6 +104,8 @@ bool centerTuning = false; dsp::stream dummyStream; bool demoWindow = false; +float testSNR = 50; + void windowInit() { LoadingScreen::show("Initializing UI"); gui::waterfall.init(); @@ -515,6 +518,13 @@ void drawWindow() { ImGui::SameLine(); + ImGui::SetCursorPosX(ImGui::GetWindowSize().x - 387); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5); + ImGui::SetNextItemWidth(300); + ImGui::SNRMeter((vfo != NULL) ? gui::waterfall.selectedVFOSNR : 0); + + ImGui::SameLine(); + // Logo button ImGui::SetCursorPosX(ImGui::GetWindowSize().x - 48); ImGui::SetCursorPosY(10); @@ -604,6 +614,8 @@ void drawWindow() { firstMenuRender = true; } + ImGui::SliderFloat("Testing SNR meter", &testSNR, 0, 100); + ImGui::Spacing(); } diff --git a/core/src/gui/widgets/snr_meter.cpp b/core/src/gui/widgets/snr_meter.cpp new file mode 100644 index 00000000..c244d5c7 --- /dev/null +++ b/core/src/gui/widgets/snr_meter.cpp @@ -0,0 +1,43 @@ +#include +#include + +#ifndef IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_MATH_OPERATORS +#endif +#include + +namespace ImGui { + void SNRMeter(float val, const ImVec2& size_arg = ImVec2(0, 0)) { + ImGuiWindow* window = GetCurrentWindow(); + ImGuiStyle& style = GImGui->Style; + + float pad = style.FramePadding.y; + + ImVec2 min = window->DC.CursorPos; + ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), 26); + ImRect bb(min, min + size); + + float lineHeight = size.y; + + ItemSize(size, style.FramePadding.y); + if (!ItemAdd(bb, 0)) { + return; + } + + val = std::clamp(val, 0, 100); + float ratio = size.x / 90; + float it = size.x / 9; + char buf[32]; + + window->DrawList->AddRectFilled(min + ImVec2(0, 1), min + ImVec2(roundf((float)val * ratio), 10), IM_COL32(0, 136, 255, 255)); + window->DrawList->AddLine(min, min + ImVec2(0, 9), IM_COL32(255, 255, 255, 255)); + window->DrawList->AddLine(min + ImVec2(0, 9), min + ImVec2(size.x + 1, 9), IM_COL32(255, 255, 255, 255)); + + for (int i = 0; i < 10; i++) { + window->DrawList->AddLine(min + ImVec2(roundf((float)i * it), 9), min + ImVec2(roundf((float)i * it), 14), IM_COL32(255, 255, 255, 255)); + sprintf(buf, "%d", i * 10); + ImVec2 sz = ImGui::CalcTextSize(buf); + window->DrawList->AddText(min + ImVec2(roundf(((float)i * it) - (sz.x/2.0)) + 1, 16), IM_COL32(255, 255, 255, 255), buf); + } + } +} \ No newline at end of file diff --git a/core/src/gui/widgets/snr_meter.h b/core/src/gui/widgets/snr_meter.h new file mode 100644 index 00000000..313a4b2e --- /dev/null +++ b/core/src/gui/widgets/snr_meter.h @@ -0,0 +1,6 @@ +#pragma once +#include + +namespace ImGui { + void SNRMeter(float val, const ImVec2& size_arg = ImVec2(0, 0)); +} \ No newline at end of file diff --git a/core/src/gui/widgets/waterfall.cpp b/core/src/gui/widgets/waterfall.cpp index f80e5c24..c1334faf 100644 --- a/core/src/gui/widgets/waterfall.cpp +++ b/core/src/gui/widgets/waterfall.cpp @@ -288,10 +288,6 @@ namespace ImGui { // Next, check if a VFO was selected if (!targetFound) { for (auto const& [name, _vfo] : vfos) { - if (name == selectedVFO) { - continue; - } - // If another VFO is selected, select it and cancel out if (IS_IN_AREA(mousePos, _vfo->rectMin, _vfo->rectMax) || IS_IN_AREA(mousePos, _vfo->wfRectMin, _vfo->wfRectMax)) { selectedVFO = name; @@ -436,6 +432,16 @@ namespace ImGui { printAndScale(_vfo->bandwidth, buf); ImGui::Text("Bandwidth: %sHz", buf); ImGui::Text("Bandwidth Locked: %s", _vfo->bandwidthLocked ? "Yes" : "No"); + + float strength, snr; + if (calculateVFOSignalInfo(_vfo, strength, snr)) { + ImGui::Text("Strength: %0.1fdBFS", strength); + ImGui::Text("SNR: %0.1fdB", snr); + } + else { + ImGui::Text("Strength: ---.-dBFS"); + ImGui::Text("SNR: ---.-dB"); + } } ImGui::EndTooltip(); @@ -445,6 +451,50 @@ namespace ImGui { } } + bool WaterFall::calculateVFOSignalInfo(WaterfallVFO* _vfo, float& strength, float& snr) { + if (rawFFTs == NULL || fftLines <= 0) { return false; } + + // Calculate FFT index data + double vfoMinSizeFreq = _vfo->centerOffset - _vfo->bandwidth; + double vfoMinFreq = _vfo->centerOffset - (_vfo->bandwidth/2.0); + double vfoMaxFreq = _vfo->centerOffset + (_vfo->bandwidth/2.0); + double vfoMaxSizeFreq = _vfo->centerOffset + _vfo->bandwidth; + int vfoMinSideOffset = std::clamp(((vfoMinSizeFreq / (wholeBandwidth/2.0)) * (double)(rawFFTSize/2)) + (rawFFTSize/2), 0, rawFFTSize); + int vfoMinOffset = std::clamp(((vfoMinFreq / (wholeBandwidth/2.0)) * (double)(rawFFTSize/2)) + (rawFFTSize/2), 0, rawFFTSize); + int vfoMaxOffset = std::clamp(((vfoMaxFreq / (wholeBandwidth/2.0)) * (double)(rawFFTSize/2)) + (rawFFTSize/2), 0, rawFFTSize); + int vfoMaxSideOffset = std::clamp(((vfoMaxSizeFreq / (wholeBandwidth/2.0)) * (double)(rawFFTSize/2)) + (rawFFTSize/2), 0, rawFFTSize); + + float* fftLine = &rawFFTs[currentFFTLine * rawFFTSize]; + + double avg = 0; + float max = -INFINITY; + int avgCount = 0; + + // Calculate Left average + for (int i = vfoMinSideOffset; i < vfoMinOffset; i++) { + avg += fftLine[i]; + avgCount++; + } + + // Calculate Right average + for (int i = vfoMaxOffset + 1; i < vfoMaxSideOffset; i++) { + avg += fftLine[i]; + avgCount++; + } + + avg /= (double)(avgCount); + + // Calculate max + for (int i = vfoMinOffset; i <= vfoMaxOffset; i++) { + if (fftLine[i] > max) { max = fftLine[i]; } + } + + strength = max; + snr = max - avg; + + return true; + } + void WaterFall::setFastFFT(bool fastFFT) { std::lock_guard lck(buf_mtx); _fastFFT = fastFFT; @@ -748,6 +798,11 @@ namespace ImGui { doZoom(drawDataStart, drawDataSize, dataWidth, rawFFTs, latestFFT, _fastFFT); fftLines = 1; } + + if (selectedVFO != "" && vfos.size() > 0) { + float dummy; + calculateVFOSignalInfo(vfos[selectedVFO], dummy, selectedVFOSNR); + } buf_mtx.unlock(); } diff --git a/core/src/gui/widgets/waterfall.h b/core/src/gui/widgets/waterfall.h index b759defc..dfb6c215 100644 --- a/core/src/gui/widgets/waterfall.h +++ b/core/src/gui/widgets/waterfall.h @@ -133,6 +133,8 @@ namespace ImGui { bool mouseInFFT = false; bool mouseInWaterfall = false; + float selectedVFOSNR = NAN; + std::map vfos; std::string selectedVFO = ""; bool selectedVFOChanged = false; @@ -162,6 +164,7 @@ namespace ImGui { void updateWaterfallFb(); void updateWaterfallTexture(); void updateAllVFOs(); + bool calculateVFOSignalInfo(WaterfallVFO* vfo, float& strength, float& snr); bool waterfallUpdate = false;