* @brief Pulls the latest time-domain samples, runs FFT, pushes a new row. */
| 650 | * @brief Pulls the latest time-domain samples, runs FFT, pushes a new row. |
| 651 | */ |
| 652 | void Widgets::Waterfall::updateData() |
| 653 | { |
| 654 | if (!isEnabled() || !isVisible()) |
| 655 | return; |
| 656 | |
| 657 | if (!UI::Dashboard::instance().waterfallRunning(m_index)) |
| 658 | return; |
| 659 | |
| 660 | if (!VALIDATE_WIDGET(SerialStudio::DashboardWaterfall, m_index)) |
| 661 | return; |
| 662 | |
| 663 | const auto& data = UI::Dashboard::instance().waterfallData(m_index); |
| 664 | const int newSize = static_cast<int>(data.size()); |
| 665 | if (newSize <= 0) |
| 666 | return; |
| 667 | |
| 668 | if (newSize != m_size) { |
| 669 | allocateFftPlan(newSize); |
| 670 | rebuildHistoryImage(); |
| 671 | Q_EMIT historySizeChanged(); |
| 672 | } |
| 673 | |
| 674 | if (!m_plan) |
| 675 | return; |
| 676 | |
| 677 | const double* in = data.raw(); |
| 678 | std::size_t idx = data.frontIndex(); |
| 679 | const std::size_t mask = data.storageMask(); |
| 680 | const double offset = m_scaleIsValid ? -m_center : 0.0; |
| 681 | const double scale = m_scaleIsValid ? (1.0 / m_halfRange) : 1.0; |
| 682 | |
| 683 | for (int i = 0; i < m_size; ++i) { |
| 684 | const double raw = in[idx]; |
| 685 | const float v = std::isfinite(raw) ? static_cast<float>((raw + offset) * scale) : 0.0f; |
| 686 | m_samples[i].r = v * m_window[i]; |
| 687 | m_samples[i].i = 0.0f; |
| 688 | idx = (idx + 1) & mask; |
| 689 | } |
| 690 | |
| 691 | kiss_fft(m_plan, m_samples.data(), m_fftOutput.data()); |
| 692 | |
| 693 | const int spectrumSize = m_size / 2; |
| 694 | const float normFactor = static_cast<float>(m_size) * static_cast<float>(m_size); |
| 695 | const float invNorm = 1.0f / normFactor; |
| 696 | for (int i = 0; i < spectrumSize; ++i) { |
| 697 | const float re = m_fftOutput[i].r; |
| 698 | const float im = m_fftOutput[i].i; |
| 699 | const float power = std::max((re * re + im * im) * invNorm, kEpsSquared); |
| 700 | m_dbCache[i] = std::max(10.0f * std::log10(power), kFloorDb); |
| 701 | } |
| 702 | |
| 703 | if (m_smoothed.size() < static_cast<size_t>(spectrumSize)) |
| 704 | m_smoothed.resize(spectrumSize); |
| 705 | |
| 706 | static constexpr float kInvSmoothingTaps[] = { |
| 707 | 0.0f, |
| 708 | 1.0f / 1.0f, |
| 709 | 1.0f / 2.0f, |
nothing calls this directly
no test coverage detected