* @brief Updates the FFT data. */
| 370 | * @brief Updates the FFT data. |
| 371 | */ |
| 372 | void Widgets::FFTPlot::updateData() |
| 373 | { |
| 374 | static thread_local DSP::DownsampleWorkspace ws; |
| 375 | |
| 376 | if (!isEnabled()) |
| 377 | return; |
| 378 | |
| 379 | if (!VALIDATE_WIDGET(SerialStudio::DashboardFFT, m_index)) |
| 380 | return; |
| 381 | |
| 382 | const auto& data = UI::Dashboard::instance().fftData(m_index); |
| 383 | const int newSize = static_cast<int>(data.size()); |
| 384 | if (newSize != m_size && !rebuildFftPlan(newSize)) |
| 385 | return; |
| 386 | |
| 387 | if (newSize <= 0) |
| 388 | return; |
| 389 | |
| 390 | if (!m_plan) |
| 391 | return; |
| 392 | |
| 393 | const double* in = data.raw(); |
| 394 | std::size_t idx = data.frontIndex(); |
| 395 | const std::size_t mask = data.storageMask(); |
| 396 | const double offset = m_scaleIsValid ? -m_center : 0.0; |
| 397 | const double scale = m_scaleIsValid ? (1.0 / m_halfRange) : 1.0; |
| 398 | for (int i = 0; i < m_size; ++i) { |
| 399 | const double raw = in[idx]; |
| 400 | const float v = std::isfinite(raw) ? static_cast<float>((raw + offset) * scale) : 0.0f; |
| 401 | m_samples[i].r = v * m_window[i]; |
| 402 | m_samples[i].i = 0.0f; |
| 403 | idx = (idx + 1) & mask; |
| 404 | } |
| 405 | |
| 406 | kiss_fft(m_plan, m_samples.data(), m_fftOutput.data()); |
| 407 | const int spectrumSize = static_cast<size_t>(m_size) / 2; |
| 408 | computeSmoothedSpectrum(spectrumSize); |
| 409 | DSP::downsampleMonotonic(m_xData, m_yData, m_dataW, m_dataH, m_data, &ws); |
| 410 | } |
| 411 | |
| 412 | /** |
| 413 | * @brief Rebuilds the render data for ZOH or stem interpolation modes. |
nothing calls this directly
no test coverage detected