spectrogram decimation

This commit is contained in:
2026-04-09 10:20:28 +02:00
parent 793df68ba1
commit 6846c853c0
6 changed files with 23 additions and 7 deletions

View File

@@ -702,6 +702,7 @@ void Application::loadConfig() {
cursors_.snapToPeaks = config_.getBool("snap_to_peaks", cursors_.snapToPeaks); cursors_.snapToPeaks = config_.getBool("snap_to_peaks", cursors_.snapToPeaks);
measurements_.traceMinFreq = config_.getFloat("trace_min_freq", measurements_.traceMinFreq); measurements_.traceMinFreq = config_.getFloat("trace_min_freq", measurements_.traceMinFreq);
measurements_.traceMaxFreq = config_.getFloat("trace_max_freq", measurements_.traceMaxFreq); measurements_.traceMaxFreq = config_.getFloat("trace_max_freq", measurements_.traceMaxFreq);
ui_.specMinPixPerBin = config_.getInt("spec_min_pix_per_bin", ui_.specMinPixPerBin);
// Clamp // Clamp
controlPanel_.fftSizeIdx = std::clamp(controlPanel_.fftSizeIdx, 0, ControlPanel::kNumFFTSizes - 1); controlPanel_.fftSizeIdx = std::clamp(controlPanel_.fftSizeIdx, 0, ControlPanel::kNumFFTSizes - 1);
@@ -766,6 +767,7 @@ void Application::saveConfig() const {
cfg.setBool("snap_to_peaks", cursors_.snapToPeaks); cfg.setBool("snap_to_peaks", cursors_.snapToPeaks);
cfg.setFloat("trace_min_freq", measurements_.traceMinFreq); cfg.setFloat("trace_min_freq", measurements_.traceMinFreq);
cfg.setFloat("trace_max_freq", measurements_.traceMaxFreq); cfg.setFloat("trace_max_freq", measurements_.traceMaxFreq);
cfg.setInt("spec_min_pix_per_bin", ui_.specMinPixPerBin);
int devIdx = audio_.deviceIdx(); int devIdx = audio_.deviceIdx();
if (devIdx >= 0 && devIdx < static_cast<int>(devices.size())) if (devIdx >= 0 && devIdx < static_cast<int>(devices.size()))

View File

@@ -178,6 +178,15 @@ void ControlPanel::render(AudioEngine& audio, UIState& ui,
} }
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Reset to 2x zoom"); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Reset to 2x zoom");
} }
{
ImGui::AlignTextToFramePadding();
ImGui::Text("Decim:");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::SliderInt("##decimation", &ui.specMinPixPerBin, 1, 8, "%d px/bin");
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Spectrum frequency decimation (peak-detect)");
}
} }
// ── Channels ── // ── Channels ──

View File

@@ -86,7 +86,7 @@ void DisplayPanel::renderSpectrum(AudioEngine& audio, UIState& ui,
specDisplay.draw(allSpectraScratch_, stylesScratch_, ui.minDB, ui.maxDB, specDisplay.draw(allSpectraScratch_, stylesScratch_, ui.minDB, ui.maxDB,
settings.sampleRate, settings.isIQ, ui.freqScale, settings.sampleRate, settings.isIQ, ui.freqScale,
specPosX, specPosY, specSizeX, specSizeY, specPosX, specPosY, specSizeX, specSizeY,
ui.viewLo, ui.viewHi); ui.viewLo, ui.viewHi, ui.specMinPixPerBin);
cursors.draw(specDisplay, specPosX, specPosY, specSizeX, specSizeY, cursors.draw(specDisplay, specPosX, specPosY, specSizeX, specSizeY,
settings.sampleRate, settings.isIQ, ui.freqScale, ui.minDB, ui.maxDB, settings.sampleRate, settings.isIQ, ui.freqScale, ui.minDB, ui.maxDB,

View File

@@ -60,9 +60,11 @@ static void buildPolyline(const std::vector<float>& spectrumDB,
bool isIQ, FreqScale freqScale, bool isIQ, FreqScale freqScale,
float posX, float posY, float sizeX, float sizeY, float posX, float posY, float sizeX, float sizeY,
float viewLo, float viewHi, float viewLo, float viewHi,
std::vector<ImVec2>& outPoints) { std::vector<ImVec2>& outPoints,
int minPixPerBin = 1) {
int bins = static_cast<int>(spectrumDB.size()); int bins = static_cast<int>(spectrumDB.size());
int displayPts = std::min(bins, static_cast<int>(sizeX)); int maxPts = std::max(1, static_cast<int>(sizeX) / std::max(1, minPixPerBin));
int displayPts = std::min(bins, maxPts);
if (displayPts < 2) displayPts = 2; if (displayPts < 2) displayPts = 2;
outPoints.resize(displayPts); outPoints.resize(displayPts);
@@ -108,7 +110,8 @@ void SpectrumDisplay::draw(const std::vector<std::vector<float>>& spectra,
FreqScale freqScale, FreqScale freqScale,
float posX, float posY, float posX, float posY,
float sizeX, float sizeY, float sizeX, float sizeY,
float viewLo, float viewHi) const { float viewLo, float viewHi,
int minPixPerBin) const {
if (spectra.empty() || spectra[0].empty() || sizeX <= 0 || sizeY <= 0) return; if (spectra.empty() || spectra[0].empty() || sizeX <= 0 || sizeY <= 0) return;
ImDrawList* dl = ImGui::GetWindowDrawList(); ImDrawList* dl = ImGui::GetWindowDrawList();
@@ -178,7 +181,7 @@ void SpectrumDisplay::draw(const std::vector<std::vector<float>>& spectra,
if (spectra[ch].empty()) continue; if (spectra[ch].empty()) continue;
buildPolyline(spectra[ch], minDB, maxDB, buildPolyline(spectra[ch], minDB, maxDB,
isIQ, freqScale, posX, posY, sizeX, sizeY, isIQ, freqScale, posX, posY, sizeX, sizeY,
viewLo, viewHi, allPoints[ch]); viewLo, viewHi, allPoints[ch], minPixPerBin);
} }
// Draw spectra: all fills first, then all lines on top. // Draw spectra: all fills first, then all lines on top.
@@ -237,7 +240,7 @@ void SpectrumDisplay::draw(const std::vector<std::vector<float>>& spectra,
ImU32 col = (st.lineColor & 0x00FFFFFF) | 0x90000000; ImU32 col = (st.lineColor & 0x00FFFFFF) | 0x90000000;
buildPolyline(peakHold_[ch], minDB, maxDB, buildPolyline(peakHold_[ch], minDB, maxDB,
isIQ, freqScale, posX, posY, sizeX, sizeY, isIQ, freqScale, posX, posY, sizeX, sizeY,
viewLo, viewHi, phPoints); viewLo, viewHi, phPoints, minPixPerBin);
if (phPoints.size() >= 2) if (phPoints.size() >= 2)
dl->AddPolyline(phPoints.data(), static_cast<int>(phPoints.size()), dl->AddPolyline(phPoints.data(), static_cast<int>(phPoints.size()),
col, ImDrawFlags_None, 1.0f); col, ImDrawFlags_None, 1.0f);

View File

@@ -22,7 +22,8 @@ public:
double sampleRate, bool isIQ, double sampleRate, bool isIQ,
FreqScale freqScale, FreqScale freqScale,
float posX, float posY, float sizeX, float sizeY, float posX, float posY, float sizeX, float sizeY,
float viewLo = 0.0f, float viewHi = 1.0f) const; float viewLo = 0.0f, float viewHi = 1.0f,
int minPixPerBin = 1) const;
// Convenience: single-channel draw (backward compat). // Convenience: single-channel draw (backward compat).
void draw(const std::vector<float>& spectrumDB, void draw(const std::vector<float>& spectrumDB,

View File

@@ -17,6 +17,7 @@ struct UIState {
int waterfallChannel = 0; int waterfallChannel = 0;
bool waterfallMultiCh = true; bool waterfallMultiCh = true;
int specMinPixPerBin = 1; // min screen pixels per spectrum bin (1 = no decimation)
std::array<bool, kMaxChannels> channelEnabled = {true,true,true,true,true,true,true,true}; std::array<bool, kMaxChannels> channelEnabled = {true,true,true,true,true,true,true,true};
// Complementary pairs: colors at indices 0+1, 2+3, 4+5, 6+7 sum to white. // Complementary pairs: colors at indices 0+1, 2+3, 4+5, 6+7 sum to white.
std::array<ImVec4, kMaxChannels> channelColors = {{ std::array<ImVec4, kMaxChannels> channelColors = {{