aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClyne Sullivan <clyne@bitgloo.com>2023-09-23 11:44:50 -0400
committerClyne Sullivan <clyne@bitgloo.com>2023-09-23 11:44:50 -0400
commitf41cfe8196909c6ace58a9dc7e5a7b4cb24c23ba (patch)
tree959b82ca52c5a9a049681b5c8bd11986b853a9db
parent54e1e399ee8079a8b9d9f9093dd8330e27ccbdc9 (diff)
add frequency plot
-rw-r--r--.gitmodules3
-rw-r--r--gui/Makefile7
m---------gui/kissfft0
-rw-r--r--gui/source/device.cpp4
-rw-r--r--gui/source/gui_device.cpp108
5 files changed, 108 insertions, 14 deletions
diff --git a/.gitmodules b/.gitmodules
index 57329a9..2decf58 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -14,3 +14,6 @@
[submodule "gui/source/ImGuiColorTextEdit"]
path = gui/source/ImGuiColorTextEdit
url = https://github.com/BalazsJako/ImGuiColorTextEdit
+[submodule "gui/kissfft"]
+ path = gui/kissfft
+ url = https://github.com/mborgerding/kissfft
diff --git a/gui/Makefile b/gui/Makefile
index 0ff9c48..0a5494c 100644
--- a/gui/Makefile
+++ b/gui/Makefile
@@ -1,6 +1,8 @@
CXX = g++
CXXFILES := \
+ kissfft/kiss_fft.c \
+ kissfft/kiss_fftr.c \
source/serial/src/serial.cc \
source/imgui/backends/imgui_impl_sdl2.cpp \
source/imgui/backends/imgui_impl_opengl2.cpp \
@@ -13,7 +15,8 @@ CXXFILES := \
$(wildcard source/stmdsp/*.cpp) \
$(wildcard source/*.cpp)
-CXXFLAGS := -std=c++20 -O2 \
+CXXFLAGS := -std=c++20 -Og -ggdb -g3 \
+ -Ikissfft \
-Isource -Isource/imgui -Isource/stmdsp -Isource/serial/include \
-Isource/ImGuiColorTextEdit -Isource/ImGuiFileDialog \
$(shell sdl2-config --cflags) \
@@ -32,7 +35,7 @@ LDFLAGS = $(shell sdl2-config --libs) -lGL -lpthread
OUTPUT := stmdspgui
endif
-OFILES := $(patsubst %.cc, %.o, $(patsubst %.cpp, %.o, $(CXXFILES)))
+OFILES := $(patsubst %.c, %.o, $(patsubst %.cc, %.o, $(patsubst %.cpp, %.o, $(CXXFILES))))
all: $(OUTPUT)
diff --git a/gui/kissfft b/gui/kissfft
new file mode 160000
+Subproject 8f47a67f595a6641c566087bf5277034be64f24
diff --git a/gui/source/device.cpp b/gui/source/device.cpp
index 9c50a0d..d959c30 100644
--- a/gui/source/device.cpp
+++ b/gui/source/device.cpp
@@ -316,7 +316,7 @@ bool deviceConnect()
return false;
}
-void deviceStart(bool logResults, bool drawSamples)
+void deviceStart(bool fetchSamples)
{
if (!m_device) {
log("No device connected.");
@@ -336,7 +336,7 @@ void deviceStart(bool logResults, bool drawSamples)
log("Ready.");
} else {
m_device->continuous_start();
- if (drawSamples || logResults || wavOutput.valid())
+ if (fetchSamples || wavOutput.valid())
std::thread(drawSamplesTask, m_device).detach();
log("Running.");
diff --git a/gui/source/gui_device.cpp b/gui/source/gui_device.cpp
index 82ce5be..bab138f 100644
--- a/gui/source/gui_device.cpp
+++ b/gui/source/gui_device.cpp
@@ -2,7 +2,7 @@
#include "imgui.h"
#include "imgui_internal.h"
#include "ImGuiFileDialog.h"
-
+#include "kiss_fftr.h"
#include "stmdsp.hpp"
#include <array>
@@ -45,7 +45,7 @@ void deviceLoadAudioFile(const std::string& file);
void deviceLoadLogFile(const std::string& file);
void deviceSetSampleRate(unsigned int index);
void deviceSetInputDrawing(bool enabled);
-void deviceStart(bool logResults, bool drawSamples);
+void deviceStart(bool fetchSamples);
void deviceStartMeasurement();
void deviceUpdateDrawBufferSize(double timeframe);
std::size_t pullFromDrawQueue(
@@ -57,6 +57,7 @@ static std::string sampleRatePreview = "?";
static bool measureCodeTime = false;
static bool logResults = false;
static bool drawSamples = false;
+static bool drawFrequencies = false;
static bool popupRequestBuffer = false;
static bool popupRequestSiggen = false;
static bool popupRequestLog = false;
@@ -74,6 +75,7 @@ void deviceRenderDisconnect()
measureCodeTime = false;
logResults = false;
drawSamples = false;
+ drawFrequencies = false;
}
void deviceRenderMenu()
@@ -104,7 +106,7 @@ void deviceRenderMenu()
static std::string startLabel ("Start");
addMenuItem(startLabel, isConnected, [&] {
startLabel = isRunning ? "Start" : "Stop";
- deviceStart(logResults, drawSamples);
+ deviceStart(logResults || drawSamples || drawFrequencies);
if (logResults && isRunning)
logResults = false;
});
@@ -118,7 +120,8 @@ void deviceRenderMenu()
if (!isConnected || isRunning)
ImGui::PushDisabled(); // Hey, pushing disabled!
- ImGui::Checkbox("Draw samples", &drawSamples);
+ ImGui::Checkbox("Plot over time", &drawSamples);
+ ImGui::Checkbox("Plot over freq.", &drawFrequencies);
if (ImGui::Checkbox("Log results...", &logResults)) {
if (logResults)
popupRequestLog = true;
@@ -296,13 +299,16 @@ void deviceRenderWidgets()
void deviceRenderDraw()
{
- if (drawSamples) {
- static std::vector<stmdsp::dacsample_t> buffer;
- static std::vector<stmdsp::dacsample_t> bufferInput;
- static auto bufferCirc = CircularBuffer(buffer);
- static auto bufferInputCirc = CircularBuffer(bufferInput);
+ static std::vector<stmdsp::dacsample_t> buffer;
+ static std::vector<stmdsp::dacsample_t> bufferInput;
+ static std::vector<kiss_fft_scalar> bufferFFTIn;
+ static std::vector<kiss_fft_cpx> bufferFFTOut;
+ static auto bufferCirc = CircularBuffer(buffer);
+ static auto bufferInputCirc = CircularBuffer(bufferInput);
+ static bool drawSamplesInput = false;
+ static kiss_fftr_cfg kisscfg;
- static bool drawSamplesInput = false;
+ if (drawSamples) {
static unsigned int yMinMax = 4095;
ImGui::Begin("draw", &drawSamples);
@@ -434,6 +440,88 @@ void deviceRenderDraw()
}
ImGui::End();
+ } else if (drawFrequencies) {
+ ImGui::Begin("draw", &drawFrequencies);
+
+ ImGui::Text("Time: %0.3f sec", drawSamplesTimeframe);
+ ImGui::SameLine();
+ if (ImGui::Button("-", {30, 0})) {
+ drawSamplesTimeframe = std::max(drawSamplesTimeframe / 2., 0.0078125);
+ deviceUpdateDrawBufferSize(drawSamplesTimeframe);
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("+", {30, 0})) {
+ drawSamplesTimeframe = std::min(drawSamplesTimeframe * 2, 32.);
+ deviceUpdateDrawBufferSize(drawSamplesTimeframe);
+ }
+
+ auto newSize = pullFromDrawQueue(bufferCirc);
+ if (newSize > 0) {
+ buffer.resize(newSize);
+ bufferFFTIn.resize(newSize);
+ bufferFFTOut.resize(newSize);
+ bufferCirc = CircularBuffer(buffer);
+ pullFromDrawQueue(bufferCirc);
+
+ kiss_fftr_free(kisscfg);
+ kisscfg = kiss_fftr_alloc(buffer.size(), false, nullptr, nullptr);
+ }
+
+ std::copy(buffer.begin(), buffer.end(), bufferFFTIn.begin());
+ kiss_fftr(kisscfg, bufferFFTIn.data(), bufferFFTOut.data());
+
+ auto drawList = ImGui::GetWindowDrawList();
+ ImVec2 p0 = ImGui::GetWindowPos();
+ auto size = ImGui::GetWindowSize();
+ p0.y += 65;
+ size.y -= 70;
+ drawList->AddRectFilled(p0, {p0.x + size.x, p0.y + size.y}, IM_COL32_BLACK);
+
+ const auto lcMinor = ImGui::GetColorU32(IM_COL32(40, 40, 40, 255));
+ const auto lcMajor = ImGui::GetColorU32(IM_COL32(140, 140, 140, 255));
+
+ {
+ const float yinc = size.y / 10.f;
+ for (int i = 1; i < 10; ++i) {
+ drawList->AddLine({p0.x, p0.y + i * yinc}, {p0.x + size.x, p0.y + i * yinc}, (i % 2) ? lcMinor : lcMajor);
+ }
+ }
+ {
+ const float xinc = size.x / 10.f;
+ for (int i = 1; i < 10; ++i) {
+ drawList->AddLine({p0.x + i * xinc, p0.y}, {p0.x + i * xinc, p0.y + size.y}, (i % 2) ? lcMinor : lcMajor);
+ }
+ }
+
+ const auto Fs = m_device->get_sample_rate();
+
+ const float di = static_cast<float>(buffer.size() / 2) / size.x;
+ const float dx = std::ceil(size.x / static_cast<float>(buffer.size()));
+ ImVec2 pp = p0;
+ float i = 0;
+ while (pp.x < p0.x + size.x) {
+ unsigned int idx = i;
+ float n = std::clamp(bufferFFTOut[idx].r / Fs / 4.f, 0.f, 1.f);
+ i += di;
+
+ ImVec2 next (pp.x + dx, p0.y + size.y * (1 - n));
+ drawList->AddLine(pp, next, ImGui::GetColorU32(IM_COL32(255, 0, 0, 255)), 2.f);
+ pp = next;
+ }
+
+ const auto mouse = ImGui::GetMousePos();
+ if (mouse.x > p0.x && mouse.x < p0.x + size.x &&
+ mouse.y > p0.y && mouse.y < p0.y + size.y)
+ {
+ char buf[16];
+ drawList->AddLine({mouse.x, p0.y}, {mouse.x, p0.y + size.y}, IM_COL32(255, 255, 0, 255));
+
+ const std::size_t si = (mouse.x - p0.x) / size.x * Fs / 2;
+ snprintf(buf, sizeof(buf), " %5luHz", si);
+ drawList->AddText(mouse, IM_COL32(255, 0, 0, 255), buf);
+ }
+
+ ImGui::End();
}
}