Skip to content

The spectrum charts improvements. #833

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 32 commits into
base: master
Choose a base branch
from

Conversation

demvlad
Copy link
Contributor

@demvlad demvlad commented May 13, 2025

This PR shows my questions and improving suggestions to compute spectrums data and the charts plot.
The spectrum improvement work started in the next draft PRs:
#819, #820, #821, #826, #827

There are my opinion about current spectrum in master branch after some research

  1. The fft call has any size input and 2*size output.
  2. The source code uses fixed 600000 (in case of my log rate settings) input and 1200000 output. The most values are zero in this case. I suspect that this is "FFT Zero padding" method to get high fft resolution. But 600000 points is too big values for our charts i think
  3. The trying to slice input by actual data size works, but has sometime very low ftt computing speed. (Added missing slices of data samples arrays in spectrum calc #819)
  4. The using of power 2 input size value gives fine speed (this PR).
  5. The FFT output has 2* input size. It contains Re, Im pairs of complex values parts sequence for two side symetrical spectrum. We need to use just half data output.
  6. The source code uses output Re Im values directly as separate value at the chart, what is wrong IMO. It uses output data what size is similar input data. The spectrum chart has 600000points for my log rate settings. The charts show sequence Re,Im,Re,Im... values. This is wrong. We should show magnitude values at the charts as Math.hypo(Re, Im). Magnitudes array has 1/4 input data size

I am not expert in FFT, i study it step by step til.
I would appreciate any help. @KarateBrot , @Quick-Flash , @ledvinap @ctzsnooze @mituritsyn

The previous improving PRs used actual samples count as fft input (#819). Its worked, but had slow hang fft computing sometime. This issue was reason of this PR.
This PR uses dynamical power at 2 fft input size. The samples input size extended to the next power at 2 value by using zero padding. Probably, it needs to limit minimal size value to prevent low fft resolution. It will give balance between speed and quality. Will look.
After improvement of fft computing, this PR joins the previous work:

  1. Resolved issue of magnitude computing at the spectrum charts (Resolved issue of magnitude computing at the spectrum charts #821 )
    The spectrum magnitude is computed by complex Re and Im value as Math.hypot(Re, Im)
    The log file:
    Uploading btfl_all.txt…
    The log files what had very slow spectrum drawing:
    Uploading slow_log.txt…
    log.txt
    Its work fast now.
    spectrum
    spectrum_rpm
    spectrum_thr

  2. Power spectral density curves (Added power spectral density curves at the spectrum chart #820). I tryed to implement Welch PSD computing method follow python SciPy library and Python Spectral Analysis tutorial.
    The PSD value shows in the standard dBm/Hz units. The charts show maximal noise point and have mouse cursor to meassure current mouse position..
    The vertical slider changes curves smoothing (changes samples per segment value in the Welch method).
    PSD_curve
    PSD_curve_nosmooth

  3. Power spectral density vs throttle and RPM heat map charts Added power spectral density charts #827
    The charts have mouse cursor to meassure current mouse position.. The heat map shows PSD values in the standard dBm/Hz units as red color map by using scaling: -40dBm/Hz is black and +10dBm/Hz is white colors. The logoryphmic PSD can show the all values range at the heat map chart.
    PSD_rpm
    PSD_thr

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Added Power Spectral Density (PSD) calculation using Welch’s method with power-of-two segment sizes and overlap.
    • Introduced new spectrum types: Power Spectral Density, PSD vs Throttle, and PSD vs RPM.
    • Added graph types to visualize PSD data with axis labeling, grid lines, and maximum noise frequency markers.
  • Improvements
    • Standardized FFT buffer sizes to powers of two with zero-padding for efficient processing.
    • Increased frequency graph bar density for finer resolution.
    • Enhanced graph rendering with adjustable grid ticks, gradient opacity, refined margins, and extended mouse interaction for PSD types.
    • Updated frequency analysis outputs for consistent normalization and slicing.
  • Bug Fixes
    • Corrected magnitude calculations using Euclidean norms for complex FFT outputs.
    • Fixed indexing and slicing in frequency data processing for accuracy.

Copy link

coderabbitai bot commented May 13, 2025

"""

Walkthrough

The changes standardize FFT buffer sizes to powers of two throughout spectrum calculation functions, update array allocation, slicing, and bin indexing accordingly, and add a new method for Power Spectral Density (PSD) computation using Welch's method. Several function signatures are updated to reflect these changes, and comments highlight needed corrections in magnitude calculation. Additionally, a new PSD graph type with rendering, labeling, and interaction support is implemented. The HTML spectrum type selector is updated to include new PSD options, and the main analysis logic incorporates these new spectrum types with dynamic updates on zoom changes.

Changes

File(s) Change Summary
src/graph_spectrum_calc.js Unified FFT buffer sizing to powers of two; zero-padded FFT inputs; updated array slicing and bin index calculations; added dataLoadPSD method implementing Welch PSD with segment overlap and windowing; added private _psd and _fft_segmented methods; removed explicit FFT length parameters; corrected magnitude calculations using Euclidean norm; updated function signatures and variable declarations accordingly.
src/graph_spectrum_plot.js Added new spectrum types POWER_SPECTRAL_DENSITY, PSD_VS_THROTTLE, and PSD_VS_RPM with updated enum; implemented _drawPowerSpectralDensityGraph for PSD visualization; updated frequency graph bar density; adjusted heatmap scaling and throttle loop limit; improved vertical grid lines with optional ticks parameter; enhanced gradient opacity for fullscreen; extended mouse position display for PSD; updated margin calculation to include PSD spectrum types; renamed max noise marker label; added helper methods to retrieve PSD values by frequency and matrix indices; updated relevant method signatures to support PSD rendering flags.
index.html Modified "spectrumTypeSelect" dropdown options: reordered existing spectrum types and added new options for Power Spectral Density, PSD vs Throttle, and PSD vs RPM with updated numeric values.
src/graph_spectrum.js Added support for new spectrum types PSD_VS_THROTTLE, PSD_VS_RPM, and POWER_SPECTRAL_DENSITY in data loading switch; reloads PSD data on vertical zoom changes for dynamic updates; no changes to exported entities.
src/css/main.css Adjusted .analyser #overdrawSpectrumType CSS rule to shift element 10px right by increasing left from 120px to 130px.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant GraphSpectrumCalc
    participant FFT
    User->>GraphSpectrumCalc: dataLoadFrequency()
    GraphSpectrumCalc->>FFT: Prepare buffer (power-of-two size), zero-pad if needed
    FFT-->>GraphSpectrumCalc: FFT output
    GraphSpectrumCalc->>GraphSpectrumCalc: _normalizeFft(fftOutput)
    GraphSpectrumCalc-->>User: Normalized spectrum

    User->>GraphSpectrumCalc: dataLoadPSD(analyserZoomY)
    GraphSpectrumCalc->>FFT: Prepare segment (largest power-of-two ≤ sample size)
    FFT-->>GraphSpectrumCalc: FFT output for each segment
    GraphSpectrumCalc->>GraphSpectrumCalc: Aggregate PSD results with overlap and windowing
    GraphSpectrumCalc-->>User: PSD data

    User->>GraphSpectrumPlot: Select POWER_SPECTRAL_DENSITY spectrum type
    GraphSpectrumPlot->>GraphSpectrumPlot: _drawPowerSpectralDensityGraph(canvasCtx)
    GraphSpectrumPlot-->>User: Render PSD graph with labels and max noise marker
Loading

Poem

In the land of FFT and code so true,
The buffers now fit—powers of two!
Zeroes are padded, arrays realigned,
Magnitude notes left for future to find.
With spectra now tidy and logic anew,
This rabbit hops on, with thanks to the crew!
🐇✨
"""

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@demvlad demvlad marked this pull request as draft May 13, 2025 14:43
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/graph_spectrum_calc.js (2)

112-118: Nit — typo and clearer variable naming

multipiler is miss-spelled in two places and the intent (“zoom multiplier”) is not immediately obvious. A single, well-named variable makes the block easier to read.

-const multipiler = Math.floor(1 / analyserZoomY); // 0. ... 10
-if (multipiler == 0) {
+const zoomMultiplier = Math.floor(1 / analyserZoomY); // 0 … 10
+if (zoomMultiplier === 0) {
   pointsPerSegment = 256;
-} else if (multipiler > 1) {
-  pointsPerSegment *= 2 ** Math.floor(multipiler / 2);
+} else if (zoomMultiplier > 1) {
+  pointsPerSegment *= 2 ** Math.floor(zoomMultiplier / 2);
 }

Also applies to: 120-124


159-165: Heavy allocations inside the FFT loop hurt performance

A new FFT instance and two large typed arrays are created for every sliding
window. For a 5-minute trace with the default settings this can run into
thousands of allocations and GC pauses.

Minimal fix (reuse across iterations):

-const fft = new FFT.complex(fftBufferSize, false);
-for (let fftChunkIndex = 0; …) {
-  const fftInput  = new Float64Array(fftBufferSize);
-  let   fftOutput = new Float64Array(fftBufferSize * 2);
+const fft       = new FFT.complex(fftBufferSize, false);
+const fftInput  = new Float64Array(fftBufferSize);
+const fftOutput = new Float64Array(fftBufferSize * 2);
+for (let fftChunkIndex = 0; …) {
   …
 }

The same input/output buffers can be safely overwritten each iteration, cutting
memory churn dramatically.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abfac2c and ec62340.

📒 Files selected for processing (1)
  • src/graph_spectrum_calc.js (9 hunks)
🔇 Additional comments (1)
src/graph_spectrum_calc.js (1)

347-353: Zero-padding check — please verify counts

samples.slice(0, fftBufferSize) returns a buffer that may be longer than the
actual data (samplesCount). Later, a Hann window is applied only to
samplesCount points, leaving the zero-padded tail untouched, which is fine.
Just make sure downstream code (plots, labels) interprets count correctly
(i.e. as pre-pad length).

@demvlad

This comment was marked as outdated.

@mituritsyn
Copy link
Contributor

mituritsyn commented May 13, 2025

I would appreciate any help. @KarateBrot , @Quick-Flash , @ledvinap @ctzsnooze @mituritsyn

You are doing a huge job and made several steps forward compared to me. I was always using Python libraries for FFT without deep diving into the math.

@demvlad
Copy link
Contributor Author

demvlad commented May 13, 2025

You are doing a huge job and made several steps forward compared to me. I was always using Python libraries for FFT without deep diving into the math.

You have much drone tuning experience! These improvements change spectrum views, will very helpful to know what is good.
This PR can compute spectrum very fast, but probably it can low spectrum resolution for short time intervals.

@demvlad demvlad marked this pull request as ready for review May 13, 2025 17:17
@demvlad demvlad force-pushed the fft_input_size_issue branch from ec62340 to 966e0cd Compare May 13, 2025 19:07
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
src/graph_spectrum_calc.js (3)

503-519: ⚠️ Potential issue

Incorrect FFT magnitude calculation in _normalizeFft.

Like the issue in the VS-X path, this function also calculates magnitude incorrectly by taking the absolute value of only the real part of each complex pair.

Implement the correct magnitude calculation as suggested in the previous review comments:

-const  fftLength = fftOutput.length / 2;
-// TODO: This is wrong spectrum magnitude calculation as abs of separate complex Re, Im values.
-// We should use hypot(Re, Im) instead of and return divide by 2 (fftOutput.length / 4) arrays size
-for (let i = 0; i < fftLength; i++) {
-  fftOutput[i] = Math.abs(fftOutput[i]);
-  if (i > noiseLowEndIdx && fftOutput[i] > maxNoise) {
-    maxNoise = fftOutput[i];
-    maxNoiseIdx = i;
-  }
-}
+const complexPairs = fftOutput.length / 2;
+const magnitudes = new Float64Array(complexPairs);
+for (let i = 0; i < complexPairs; i++) {
+  const re = fftOutput[2 * i];
+  const im = fftOutput[2 * i + 1];
+  magnitudes[i] = Math.hypot(re, im);
+  if (i > noiseLowEndIdx && magnitudes[i] > maxNoise) {
+    maxNoise = magnitudes[i];
+    maxNoiseIdx = i;
+  }
+}
+// Replace fftOutput with magnitudes for further processing
+fftOutput = magnitudes;

178-186: ⚠️ Potential issue

Incorrect FFT magnitude calculation flagged by TODO.

The current implementation only uses the real part of each complex pair and applies Math.abs() to that value alone, instead of computing the proper magnitude.

This is exactly the issue mentioned in the previous review comments. You should implement the magnitude calculation using Math.hypot(re, im) as already done in the new _fft_segmented method:

-fftOutput = fftOutput.slice(0, fftBufferSize); // The fft output contains two side spectrum, we use the first part only to get one side
-// TODO: This is wrong spectrum magnitude calculation as abs of separate complex Re, Im values.
-// We should use hypot(Re, Im) instead of and return divide by 2 (fftOutput.length / 4) arrays size
-// Use only abs values
-for (let i = 0; i < fftBufferSize; i++) {
-  fftOutput[i] = Math.abs(fftOutput[i]);
-  maxNoise = Math.max(fftOutput[i], maxNoise);
-}
+// Convert complex FFT output to magnitude spectrum
+const magnitudes = new Float64Array(fftBufferSize / 2);
+for (let i = 0; i < fftBufferSize / 2; i++) {
+  const re = fftOutput[2 * i];
+  const im = fftOutput[2 * i + 1];
+  magnitudes[i] = Math.hypot(re, im);
+  maxNoise = Math.max(magnitudes[i], maxNoise);
+}
+// Replace fftOutput with magnitudes for further processing
+fftOutput = magnitudes;

222-230: 🛠️ Refactor suggestion

Update matrix storage to use proper FFT magnitude size.

The matrixFftOutput array is sized to fftBufferSize * 2, which is twice as large as needed when using proper magnitude calculations.

After implementing the correct magnitude calculation, you should reduce the matrix size:

const fftData = {
  fieldIndex   : this._dataBuffer.fieldIndex,
  fieldName  : this._dataBuffer.fieldName,
-  fftLength  : fftBufferSize,
-  fftOutput  : matrixFftOutput,
+  fftLength  : fftBufferSize / 2, // One-sided spectrum length after magnitude calculation
+  fftOutput  : matrixFftOutput,   // This should also be updated to store magnitudes with half the size
  maxNoise   : maxNoise,
  blackBoxRate : this._blackBoxRate,
  vsRange    : { min: flightSamples.minValue, max: flightSamples.maxValue},
};
🧹 Nitpick comments (1)
src/graph_spectrum_calc.js (1)

162-169: Zero-padding implementation could be improved.

The current implementation uses a loop to copy samples into a power-of-two sized buffer, with a TODO comment about finding a better resize method.

For better performance, consider using TypedArray.set():

-//TODO: to find method to just resize samples array to fftBufferSize
-const samples = flightSamples.samples.slice(fftChunkIndex, fftChunkIndex + fftChunkLength);
-for (let i = 0; i < fftChunkLength; i++) {
-  fftInput[i] = samples[i];
-}
+// Efficiently copy samples to power-of-two sized buffer (zero-padding)
+const samples = flightSamples.samples.slice(fftChunkIndex, fftChunkIndex + fftChunkLength);
+fftInput.set(samples);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec62340 and 966e0cd.

📒 Files selected for processing (1)
  • src/graph_spectrum_calc.js (10 hunks)
🔇 Additional comments (9)
src/graph_spectrum_calc.js (9)

97-97: Good clarification in comment.

The comment clarifies that the Hanning window is applied only to the actual sample count values, not the entire buffer.


104-104: Parameter removed from _normalizeFft call.

The explicit fftLength parameter has been removed, which is good as the function now calculates this value internally.


109-140: Well-implemented PSD calculation using Welch's method.

The new dataLoadPSD method is a valuable addition that:

  1. Dynamically calculates segment size based on zoom level
  2. Ensures segment size is a power of two for optimal FFT performance
  3. Sets appropriate overlap for Welch's method
  4. Returns structured output with min/max values and max noise frequency

This implementation follows best practices for spectral density estimation.


149-150: Good optimization using power-of-two FFT buffer size.

Using a power-of-two size for FFT buffers is crucial for optimal FFT performance, as mentioned in the PR objectives. This change implements that optimization correctly.


197-197: Improved bin index calculation.

Changing from Math.floor to Math.round provides more accurate binning for frequency vs X charts.


328-351: Good optimization with power-of-two sample size.

Adding the scaled parameter and ensuring the returned samples are padded to a power-of-two length improves FFT performance. This is a well-implemented change.


465-468: Memory optimization by slicing arrays to actual sample count.

This change ensures that only the necessary data is returned, which is more memory-efficient.


535-619: Excellent implementation of Power Spectral Density calculation.

The new _psd method correctly implements Welch's method for PSD calculation with:

  1. Proper window scaling for both 'density' and 'spectrum' output types
  2. Correct averaging of scaled power values
  3. Conversion to dB scale
  4. Tracking of min/max values and max noise frequency

This is a significant improvement for spectrum analysis.


622-646: Correctly implemented FFT segmentation with proper magnitude calculation.

The _fft_segmented method:

  1. Handles overlapping segments properly
  2. Applies optional Hanning windowing
  3. Correctly calculates the magnitude of complex FFT outputs using Math.hypot(re, im)

This implementation addresses one of the key issues mentioned in the PR objectives.

@demvlad demvlad marked this pull request as draft May 13, 2025 19:16
@demvlad demvlad marked this pull request as ready for review May 13, 2025 19:22
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/graph_spectrum_calc.js (1)

151-156: 🛠️ Refactor suggestion

FFT matrix allocates 2 × more memory than required

Only magnitudeLength values are written and read, yet each row is created
with fftBufferSize * 2. This doubles RAM usage and cache traffic and makes
the later averaging loop iterate over thousands of untouched zeros.

-const matrixFftOutput = new Array(NUM_VS_BINS)
-  .fill(null)
-  .map(() => new Float64Array(fftBufferSize * 2));
+const matrixFftOutput = new Array(NUM_VS_BINS)
+  .fill(null)
+  .map(() => new Float64Array(magnitudeLength));
🧹 Nitpick comments (3)
src/graph_spectrum_plot.js (2)

372-381: ticksCount is forwarded unchanged – may still be zero

Even with the above guard, callers might still provide 0 explicitly.
Consider clamping inside _drawVerticalGridLines() or here:

this._drawVerticalGridLines(
  canvasCtx,
  LEFT,
  TOP,
  WIDTH,
  HEIGHT,
  minY,
  maxY,
  "dBm/Hz",
  Math.max(1, ticksCount),   // safe forwarding
);

1076-1094: Unused LEFT parameter – remove to simplify the signature

LEFT is never referenced inside _drawVerticalGridLines. Keeping unused
parameters adds noise and invites confusion.

-GraphSpectrumPlot._drawVerticalGridLines = function (
-  canvasCtx,
-  LEFT,
-  TOP,
-  WIDTH,
-  HEIGHT,
+GraphSpectrumPlot._drawVerticalGridLines = function (
+  canvasCtx,
+  /* LEFT */, /* TOP */,
+  WIDTH,
+  HEIGHT,
   minValue,
   maxValue,
   label,
   ticks = 5,
) {
src/graph_spectrum_calc.js (1)

543-574: Heavy window re-allocation inside _psd()

window is rebuilt for every PSD call. For large pointsPerSegment
(>= 4096) this allocates & initialises tens of kB unnecessarily.

Consider caching Hanning windows keyed by pointsPerSegment
(e.g. this._hanningCache[size]) to reuse the array.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 966e0cd and 5d2749a.

📒 Files selected for processing (2)
  • src/graph_spectrum_calc.js (10 hunks)
  • src/graph_spectrum_plot.js (14 hunks)
🔇 Additional comments (2)
src/graph_spectrum_plot.js (1)

336-341: ⚠️ Potential issue

Guard against ticksCount === 0 to avoid division-by-zero in scaling & grid drawing

If the PSD happens to be completely flat ( this._fftData.minimum === this._fftData.maximum ) the ticksCount becomes 0, which will later be
passed to _drawVerticalGridLines() and used as a divisor in HEIGHT / ticks.
That triggers an Infinity height, completely breaking the rendering.

-const ticksCount = (maxY - minY) / dbStep;
+let ticksCount = (maxY - minY) / dbStep;
+// Ensure at least one tick in degenerate cases
+if (ticksCount < 1) {
+  ticksCount = 1;
+}

Likely an incorrect or invalid review comment.

src/graph_spectrum_calc.js (1)

348-352: Zero-sample guard for FFT buffer sizing

When the selected time range is empty, samplesCount is 0;
Math.log2(0) is -Infinity, resulting in fftBufferSize = NaN and an
exception downstream.

-const fftBufferSize = Math.pow(2, Math.ceil(Math.log2(samplesCount)));
+const fftBufferSize = samplesCount > 0
+  ? Math.pow(2, Math.ceil(Math.log2(samplesCount)))
+  : 1; // minimal, prevents NaN

@demvlad
Copy link
Contributor Author

demvlad commented May 14, 2025

The next step will join #827

Copy link

coderabbitai bot commented May 15, 2025

✅ Actions performed

Full review triggered.

Copy link

coderabbitai bot commented May 15, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/graph_spectrum_plot.js (1)

329-364: ⚠️ Potential issue

First line segment starts at (0, 0) instead of the first data point – visible artifact.

moveTo(0, 0) draws an unwanted diagonal from the canvas origin to the first PSD point. Start the path at the correct y-coordinate instead.

-canvasCtx.moveTo(0, 0);
+const y0 = HEIGHT - (this._fftData.psdOutput[0] - minY) * scaleY;
+canvasCtx.moveTo(0, y0);
🧹 Nitpick comments (2)
index.html (1)

460-464: Align dropdown values with JS spectrumType enum and improve grouping
Ensure that the numeric values (2–6) for the new options exactly match the IDs used in your JavaScript spectrumType enum and switch/case logic—any discrepancy will break chart selection. For clearer UX, consider wrapping the PSD entries in an <optgroup label="Power Spectral Density">…</optgroup> and separating the “Error vs Setpoint” into its own group.

src/graph_spectrum_calc.js (1)

226-294: Well-structured implementation of PSD vs X calculation.

The implementation parallels the structure of _dataLoadFrequencyVsX but with appropriate adaptations for PSD calculation. Key strengths include:

  1. Reuse of common patterns for bin index calculation
  2. Proper handling of background values
  3. Correct accumulation and averaging of PSD values per bin

One minor issue is the use of the magic number -200 as a background value (line 238), which could be defined as a constant for better maintainability.

Consider defining a named constant for the background PSD value:

-const backgroundValue = -200;
+const BACKGROUND_PSD_VALUE = -200; // dBm/Hz, effectively -infinity for PSD
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 6eb3711 and 3f72d63.

📒 Files selected for processing (4)
  • index.html (1 hunks)
  • src/graph_spectrum.js (2 hunks)
  • src/graph_spectrum_calc.js (13 hunks)
  • src/graph_spectrum_plot.js (21 hunks)
🔇 Additional comments (37)
src/graph_spectrum.js (3)

115-122: Good addition of new spectrum types for PSD analysis.

The implementation correctly adds support for the new Power Spectral Density (PSD) spectrum types by integrating with the appropriate data loading methods from GraphSpectrumCalc.


127-130: LGTM - Consistent handling of the new POWER_SPECTRAL_DENSITY type.

The implementation correctly integrates the new PSD spectrum type into the existing spectrum selection logic.


202-206: Essential update for dynamic PSD recalculation on zoom changes.

This is a crucial addition that ensures the PSD is recalculated when the vertical zoom level changes. This is necessary because the segment sizes for Welch's method of PSD calculation are affected by the zoom level, making this dynamic update essential for accurate visualization.

src/graph_spectrum_plot.js (17)

14-16: Good addition of constants for dBm range.

Defining MIN_DBM_VALUE and MAX_DBM_VALUE as constants improves code maintainability and makes the dBm range easily adjustable throughout the codebase.


21-25: LGTM - Clean extension of the SPECTRUM_TYPE enum.

The new spectrum types are logically organized and maintain backward compatibility with the existing enum values.


177-183: Good integration of PSD spectrum types with drawing functions.

The implementation efficiently handles the new PSD-related spectrum types by reusing the existing frequency vs X graph drawing function with a PSD flag, which reduces code duplication.


189-191: LGTM - Clean implementation of PSD graph drawing.

This new case correctly handles the POWER_SPECTRAL_DENSITY spectrum type with a dedicated drawing function.


209-210: Doubled the bar density with halved step size.

The change from a step size of 10 to 5 in the FFT visualization doubles the bar density, providing a more detailed spectrum display without overly crowding the visualization.


233-237: LGTM - Updated loop step size to match bar density.

This change to iterate by 5 instead of 10 properly matches the updated bar width calculation.


411-416: Good helper method to get PSD value by frequency.

This utility method enables retrieving PSD values for a given frequency, useful for interaction features and accurate value readouts when hovering over the graph.


418-421: LGTM - Flexible implementation with drawPSD parameter.

The updated method signature with an optional drawPSD parameter allows reusing the existing frequency vs X graph drawing function for both standard FFT and PSD visualization, reducing code duplication.


465-478: Clean update to include PSD types in vertical grid lines logic.

The conditional statements are properly extended to handle the new PSD-related spectrum types when drawing vertical grid lines.


492-520: Good implementation of dual-purpose heat map scaling.

The heat map drawing function now properly handles both standard FFT and PSD data with appropriate value clamping and scaling. The comment on line 494-495 correctly explains why the scale factor was reduced from 1.3 to 1.1 to account for the magnitude calculation change.


536-546: Good helper for retrieving matrix FFT values.

This utility method provides a clean API for accessing matrix FFT values by frequency and vertical scale argument with appropriate bounds checking.


1117-1126: LGTM - Flexible vertical grid lines with configurable ticks.

The addition of an optional ticks parameter to the _drawVerticalGridLines method makes the grid line drawing more flexible and configurable.


1261-1262: Improved contrast with increased opacity for fullscreen mode.

Increasing the opacity to 1.0/0.9 from 0.9/0.7 provides better contrast and readability in fullscreen mode.


1284-1290: Enhanced interest frequency labels with PSD values.

For the POWER_SPECTRAL_DENSITY type, the interest frequency label now includes both frequency and PSD values with appropriate units, significantly improving the information available to users.


1525-1534: Good implementation of PSD value display at cursor position.

This enhancement shows the PSD value at the mouse position, providing essential feedback when analyzing the spectrum.


1573-1583: LGTM - Clean PSD value readout for VS graphs.

The implementation properly displays PSD values for PSD_VS_THROTTLE and PSD_VS_RPM graphs when hovering with the mouse, improving the user experience.


1637-1639: Properly updated margin calculation for new spectrum types.

The margin left calculation now includes the new PSD spectrum types in the group that uses a nonzero left margin, ensuring consistent layout across all related spectrum types.

src/graph_spectrum_calc.js (17)

21-21: Good decision to export NUM_VS_BINS constant.

Exporting this constant makes it available to other modules, facilitating consistent bin counts across the codebase and improving maintainability.


81-81: Fixed a bug in setOutTime method.

The code correctly updates the reference to this._analyserTimeRange.in when calculating the maximum allowed out time. This fixes a potential bug where the wrong value might be used.


91-93: Good utility for finding next power-of-two value.

This helper function standardizes the calculation of power-of-two buffer sizes throughout the code, making the FFT calculations more efficient and consistent.


100-100: Improved Hanning window application with explicit length parameter.

The code now explicitly passes the sample count to _hanningWindow, ensuring that only the actual data points (not padding) receive the window function.


107-107: LGTM - Simplified normalization with no explicit size parameters.

The code now passes just the output to _normalizeFft without explicit size parameters, as they can be derived from the array itself. This simplifies the API and reduces the chance of errors.


112-137: Excellent implementation of Power Spectral Density calculation.

The dataLoadPSD function implements Welch's method for Power Spectral Density estimation using overlapping segments of power-of-two size. The segment size adjustment based on the vertical zoom level (analyserZoomY) allows users to balance between frequency resolution and variance reduction.

Two points worth noting:

  1. The segment size doubling with zoom levels provides a logical progression
  2. The 50% overlap is a good standard choice for PSD estimation

146-148: LGTM - Optimized FFT buffer sizes and magnitude length calculation.

The changes use power-of-two buffer sizes for FFT calculation, which significantly improves FFT performance. The magnitude length is correctly set to half the buffer size to account for the symmetrical nature of the FFT output.


156-161: Good buffer allocation with proper zero-padding.

The implementation correctly creates buffers of power-of-two size and uses set() to copy actual sample data, effectively zero-padding the input data for optimal FFT performance.


169-178: Crucial fix to FFT magnitude calculation.

This change is essential for accurate spectral analysis. Instead of incorrectly using only the real part of the FFT output, the code now properly computes the magnitude as the Euclidean norm (hypotenuse) of the real and imaginary parts using Math.hypot(re, im).


189-189: Improved bin index calculation prevents array bounds issues.

Using Math.round((NUM_VS_BINS - 1) * ...) instead of Math.round(NUM_VS_BINS * ...) is an elegant solution that naturally constrains the result to be within the valid array index range [0, NUM_VS_BINS-1], eliminating the need for a separate bounds check.


300-302: LGTM - Clean implementation of throttle-based PSD.

This function provides a simple interface to the more general _dataLoadPowerSpectralDensityVsX method with appropriate parameters for throttle.


304-310: Elegant dual-purpose implementation with flag parameter.

The updated method efficiently handles both standard FFT and PSD cases with a single boolean parameter, reducing code duplication and simplifying the API.


393-418: Improved flight samples retrieval with optional scaling.

The updated method now:

  1. Accepts an optional scaled parameter to allow bypassing the curve lookup scaling
  2. Uses the proper power-of-two buffer size
  3. Correctly slices the samples array to match the buffer size

These changes improve both flexibility and efficiency.


430-506: LGTM - Consistent improvements to frequency vs X samples retrieval.

The changes parallel those in _getFlightSamplesFreq, adding optional scaling and proper array slicing. The consistent pattern across these related methods improves maintainability.


573-596: Critical fix to FFT magnitude calculation in normalization.

This change correctly computes the magnitude from complex FFT output pairs using Math.hypot(re, im) rather than just the real components. The enhanced detection of maximum noise frequency also improves marker placement for visualization.


609-696: Excellent implementation of Welch's method for PSD estimation.

This comprehensive implementation:

  1. Computes FFT for multiple overlapping segments
  2. Correctly applies window function scaling
  3. Converts power to dB scale (10*log10)
  4. Handles edge cases and provides accurate min/max tracking
  5. Determines the maximum noise frequency

The early exit at line 648 for zero segments is a good defensive programming practice.


698-723: Well-structured implementation of segmented FFT calculation.

This helper function cleanly handles the process of dividing input data into potentially overlapping segments, optionally applying a window function, and computing the FFT magnitude for each segment. The use of array slicing and proper magnitude calculation from complex pairs ensures accuracy.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/graph_spectrum_calc.js (2)

112-137: Well-implemented PSD calculation with Welch's method.

The new dataLoadPSD method correctly implements Power Spectral Density calculation using Welch's method with adjustable smoothing (via analyserZoomY). The variable segment size approach based on zoom level is a good design that balances performance vs. resolution.

However, consider adding a minimum size check for very small samples:

  // Use power 2 fft size what is not bigger flightSamples.samples.length
  if (pointsPerSegment > flightSamples.samples.length) {
      pointsPerSegment = this.getNearPower2Value(flightSamples.samples.length);
+     // Ensure minimum size for adequate frequency resolution
+     pointsPerSegment = Math.max(pointsPerSegment, 256);
  }

146-150: Optimize matrix memory allocation.

While the FFT calculations now use the correct magnitude calculation, the matrixFftOutput array is still sized to fftBufferSize * 2, which is larger than needed since you're only storing magnitudes of length magnitudeLength in each bin.

Consider reducing the memory allocation:

- const matrixFftOutput = new Array(NUM_VS_BINS).fill(null).map(() => new Float64Array(fftBufferSize * 2));
+ const matrixFftOutput = new Array(NUM_VS_BINS).fill(null).map(() => new Float64Array(magnitudeLength));
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 3f72d63 and 2391421.

📒 Files selected for processing (1)
  • src/graph_spectrum_calc.js (13 hunks)
🔇 Additional comments (9)
src/graph_spectrum_calc.js (9)

21-21: Good practice exporting the constant.

Exporting NUM_VS_BINS is a good practice since this constant is likely needed by other modules that interact with the spectrum calculation code. This promotes consistency and reduces the risk of magic numbers throughout the codebase.


91-93: Great FFT optimization technique.

The getNearPower2Value method efficiently calculates the next power of 2 for a given input size, which is a standard optimization for FFT calculations. Using power-of-2 sizes significantly improves FFT performance due to the way the algorithm divides and conquers.


169-178: Excellent FFT magnitude computation improvement.

This code correctly calculates the magnitude of complex FFT outputs using Math.hypot(re, im) instead of just taking the absolute value of the real part. This addresses a critical issue identified in previous reviews and ensures mathematically accurate spectrum magnitude calculations.


189-189: Smart bin index calculation improvement.

This is an excellent improvement to the bin index calculation. Using Math.round((NUM_VS_BINS - 1) * ...) instead of Math.round(NUM_VS_BINS * ...) is more elegant because:

  1. It ensures the maximum possible bin index is exactly NUM_VS_BINS - 1 when avgVsValue equals flightSamples.maxValue
  2. It eliminates the need for a bounds check after calculation
  3. The code becomes cleaner and more direct

238-242: Good approach for background values.

Using a specific background value (BACKGROUND_PSD_VALUE) for initializing the PSD output matrix is a good approach for visualizing missing data points in the heatmap. This clear distinguishing value (-200 dBm/Hz) helps identify areas with no data versus those with measured low power.


259-259: Consistent bin indexing across methods.

Good job maintaining consistency by using the same improved bin index calculation formula in both frequency and PSD processing methods. Consistent binning behavior is crucial for comparable visualizations between different spectrum types.


583-593: Fixed FFT magnitude calculation in normalization.

This correctly fixes the magnitude calculation in the _normalizeFft method, addressing the issue mentioned in previous reviews. Using Math.hypot(re, im) to calculate the proper complex magnitude ensures mathematically accurate spectrum representation.


609-696: Well-implemented PSD calculation with industry standard approaches.

The _psd method provides a thorough implementation of Welch's method for PSD calculation, which is the standard approach for reducing noise in spectral estimates. The implementation includes:

  1. Proper scaling based on window function and scaling type (density vs. spectrum)
  2. Segment averaging to reduce variance
  3. Appropriate conversion to dB scale using 10*log10
  4. Noise peak detection logic
  5. Minimum value thresholding to avoid log(0) issues

The overall implementation follows scientific best practices for spectral analysis.


701-723: Clean implementation of segmented FFT with overlap.

The _fft_segmented method efficiently breaks input data into segments with overlap, applies window functions as needed, and computes magnitude spectra for each segment. This helper function is well organized and properly supports the Welch method implementation above.

@demvlad
Copy link
Contributor Author

demvlad commented May 16, 2025

The small improvements:
The ws throttle chart before:
PSD_throttle_olde
The ws throttle chart now:
PSD_throttle_new

Copy link

coderabbitai bot commented May 16, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@demvlad demvlad requested a review from nerdCopter May 16, 2025 16:15
@mituritsyn
Copy link
Contributor

moving vertical scale lower than default position has the unexpected effect of making us feel like we've increased it
image
image

@mituritsyn
Copy link
Contributor

master:
image
this PR:
image

@mituritsyn
Copy link
Contributor

vertical scale doesn't affect PSD vs X plots - is it intended?
image

@demvlad
Copy link
Contributor Author

demvlad commented May 17, 2025

vertical scale doesn't affect PSD vs X plots - is it intended?

IMO we no need to scale dBm units PSD values. Its show the wide -40...+10dBm values range. The max/min=10e5 in linear non log scale.
Maybe try to change the -40, +10 a small bit by slider, to look at "lighter" or "darker" areas

@demvlad
Copy link
Contributor Author

demvlad commented May 17, 2025

Maybe use short "PSD" caption instead of full Power....
But one full name will helpfull.
Will extend elements space size.

@demvlad
Copy link
Contributor Author

demvlad commented May 17, 2025

moving vertical scale lower than default position has the unexpected effect of making us feel like we've increased it

The lower number per segment values has low frequency resolution at the chart and rude curves look. Therefore i've limited it values by down slider moving

@mituritsyn
Copy link
Contributor

Maybe try to change the -40, +10 a small bit by slider, to look at "lighter" or "darker" areas

Exactly, we need to see noise in the filtered data.

@demvlad
Copy link
Contributor Author

demvlad commented May 17, 2025

Maybe try to change the -40, +10 a small bit by slider, to look at "lighter" or "darker" areas

Exactly, we need to see noise in the filtered data.

Ok. Will try to do shifts by 10dBm step into "dark" area, will look as -50...0, -60...-10, -70...-20... dBm range by slider up movement, and some steps into light area - -30...20, -20...30 dBm by slider down.

@demvlad
Copy link
Contributor Author

demvlad commented May 17, 2025

master: image this PR: image

Resolved.
select

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/graph_spectrum_plot.js (1)

329-409: First line segment starts at (0, 0) instead of the first data point – visible artefact

moveTo(0, 0) draws an unwanted diagonal from the canvas origin to the first
PSD point. Start the path at the correct y-coordinate instead.

-canvasCtx.moveTo(0, 0);
+const y0 = HEIGHT - (this._fftData.psdOutput[0] - minY) * scaleY;
+canvasCtx.moveTo(0, y0);
🧹 Nitpick comments (2)
src/graph_spectrum_plot.js (2)

492-529: Simplify the dBm range shift calculation logic.

The current implementation for calculating the dBm range shift is complex and difficult to follow. Consider extracting this logic into a separate helper method with clearer intention.

-  //Compute the dbm range shift from zoomY as[-30, -20, -10, 0, +10, +20]
-  let dBmRangeShift = Math.floor(1 / this._zoomY) - 1; // -1 ... 9
-  if (dBmRangeShift > 0) {
-    dBmRangeShift = -10 * Math.round(dBmRangeShift / 3); //-10, -20, -30
-  } else if (dBmRangeShift < 0) {
-    dBmRangeShift = -10 * Math.round(dBmRangeShift * 2); //+10, +20
-  }
-  const dBmValueMin = MIN_DBM_VALUE + dBmRangeShift,
-        dBmValueMax = MAX_DBM_VALUE + dBmRangeShift;
+  const dBmRangeShift = this._calculateDBmRangeShift(this._zoomY);
+  const dBmValueMin = MIN_DBM_VALUE + dBmRangeShift,
+        dBmValueMax = MAX_DBM_VALUE + dBmRangeShift;

// Then add this helper method elsewhere in the class:
+GraphSpectrumPlot._calculateDBmRangeShift = function(zoomY) {
+  // Calculate dBm range shift from zoomY as[-30, -20, -10, 0, +10, +20]
+  let shift = Math.floor(1 / zoomY) - 1; // -1 ... 9
+  
+  if (shift > 0) {
+    // For values > 0, shift by -10, -20, or -30 dBm
+    return -10 * Math.round(shift / 3);
+  } else if (shift < 0) {
+    // For values < 0, shift by +10 or +20 dBm
+    return -10 * Math.round(shift * 2);
+  }
+  
+  // No shift (0) for neutral zoom level
+  return 0;
+}

545-555: Add input validation to the matrix FFT value retrieval.

This method should validate input parameters to prevent potential issues with invalid frequency or vsArgument values.

GraphSpectrumPlot.getValueFromMatrixFFT  = function(frequency, vsArgument) {
+  if (typeof frequency !== 'number' || isNaN(frequency) || 
+      typeof vsArgument !== 'number' || isNaN(vsArgument)) {
+    console.warn("Invalid parameters for getValueFromMatrixFFT:", frequency, vsArgument);
+    return 0;
+  }
+
  const matrixFFT = this._fftData;
+  if (!matrixFFT || !matrixFFT.fftOutput || !matrixFFT.vsRange) {
+    return 0;
+  }
+
  let vsArgumentIndex = Math.round(NUM_VS_BINS * (vsArgument - matrixFFT.vsRange.min) / (matrixFFT.vsRange.max - matrixFFT.vsRange.min));
  if (vsArgumentIndex >= NUM_VS_BINS) {
    vsArgumentIndex = NUM_VS_BINS - 1;
  }
+  vsArgumentIndex = Math.max(0, vsArgumentIndex);
+
  let freqIndex = Math.round(2 * frequency / matrixFFT.blackBoxRate * (matrixFFT.fftLength - 1));
  freqIndex = Math.max(freqIndex, 0);
  freqIndex = Math.min(freqIndex, matrixFFT.fftLength - 1);
  return matrixFFT.fftOutput[vsArgumentIndex][freqIndex];
};
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between e6d4fe4 and 1b9eccd.

📒 Files selected for processing (1)
  • src/graph_spectrum_plot.js (24 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/graph_spectrum_plot.js (1)
src/graph_spectrum_calc.js (19)
  • i (173-173)
  • i (193-193)
  • i (200-200)
  • i (263-263)
  • i (270-270)
  • i (331-331)
  • i (337-337)
  • i (348-348)
  • i (446-446)
  • i (551-551)
  • i (588-588)
  • i (664-664)
  • i (708-708)
  • i (717-717)
  • j (202-202)
  • j (272-272)
  • j (666-666)
  • NUM_VS_BINS (21-21)
  • NUM_VS_BINS (21-21)
🔇 Additional comments (10)
src/graph_spectrum_plot.js (10)

3-3: LGTM: Imported the necessary constant.

The NUM_VS_BINS constant imported from graph_spectrum_calc.js is appropriately used in the getValueFromMatrixFFT method for frequency vs. value calculations.


14-16: LGTM: Constants for dBm range of Power Spectral Density.

These constants define the default range for Power Spectral Density (dBm/Hz) visualization, with appropriate values for typical signal strength measurements.


21-25: LGTM: Extended spectrum type enum with new PSD-related options.

Good enhancement of the spectrum type enumeration to include new Power Spectral Density (PSD) related visualization options.


177-191: LGTM: Added support for new PSD visualization types.

The switch statement has been appropriately extended to handle the new PSD spectrum types, reusing existing draw methods with the PSD flag where possible.


411-416: LGTM: Good helper method for retrieving PSD value by frequency.

This is a well-implemented utility function that correctly handles edge cases by constraining the frequency index within array bounds.


418-430: LGTM: Enhanced function to support both FFT and PSD visualization.

Good approach to reuse the existing frequency vs. X graph drawing functionality for PSD, using a boolean parameter to control the behavior.


1292-1299: LGTM: Added informative frequency labels with PSD values.

Good enhancement to show PSD values alongside frequency when in PSD mode, providing more useful information to the user.


1515-1549: LGTM: Enhanced mouse position display for PSD visualizations.

The code properly handles displaying PSD values when the mouse hovers over the graph, making the PSD visualization more interactive and informative.


1588-1598: LGTM: Added PSD value display for frequency vs. throttle/RPM visualizations.

Good addition to show PSD values when hovering over the heatmap, providing useful measurement capabilities.


1652-1654: LGTM: Updated margin handling for new spectrum types.

The code correctly adds the new PSD spectrum types to the margin calculation logic, ensuring consistent layout across visualization types.

@demvlad demvlad force-pushed the fft_input_size_issue branch from 1b9eccd to 257f5cc Compare May 17, 2025 20:53
Copy link

Preview URL: https://257f5cc8.betaflight-blackbox.pages.dev

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/graph_spectrum_plot.js (1)

329-409: ⚠️ Potential issue

Fix the PSD graph path starting point.

The moveTo(0, 0) call in line 359 creates an unwanted diagonal line from the origin to the first PSD point. This creates a visible artifact in the rendered graph.

Apply this fix:

-canvasCtx.moveTo(0, 0);
+const y0 = HEIGHT - (this._fftData.psdOutput[0] - minY) * scaleY;
+canvasCtx.moveTo(0, y0);
🧹 Nitpick comments (1)
src/graph_spectrum_plot.js (1)

465-479: Consider refactoring repeated spectrum type checks.

The pattern of checking for specific spectrum types (FREQ_VS_THROTTLE, PSD_VS_THROTTLE, FREQ_VS_RPM, PSD_VS_RPM) is repeated in multiple places in the code. Consider creating helper methods to simplify these checks.

+// Add these helper methods to GraphSpectrumPlot
+GraphSpectrumPlot._isThrottleBasedSpectrum = function() {
+  return this._spectrumType === SPECTRUM_TYPE.FREQ_VS_THROTTLE ||
+         this._spectrumType === SPECTRUM_TYPE.PSD_VS_THROTTLE;
+};
+
+GraphSpectrumPlot._isRpmBasedSpectrum = function() {
+  return this._spectrumType === SPECTRUM_TYPE.FREQ_VS_RPM ||
+         this._spectrumType === SPECTRUM_TYPE.PSD_VS_RPM;
+};

// Then use them in conditions like:
-if (this._spectrumType === SPECTRUM_TYPE.FREQ_VS_THROTTLE ||
-    this._spectrumType === SPECTRUM_TYPE.PSD_VS_THROTTLE) {
+if (this._isThrottleBasedSpectrum()) {
  // ...
-} else if (this._spectrumType === SPECTRUM_TYPE.FREQ_VS_RPM ||
-           this._spectrumType === SPECTRUM_TYPE.PSD_VS_RPM) {
+} else if (this._isRpmBasedSpectrum()) {
  // ...
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 1b9eccd and 257f5cc.

📒 Files selected for processing (1)
  • src/graph_spectrum_plot.js (24 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/graph_spectrum_plot.js (1)
src/graph_spectrum_calc.js (19)
  • i (173-173)
  • i (193-193)
  • i (200-200)
  • i (263-263)
  • i (270-270)
  • i (331-331)
  • i (337-337)
  • i (348-348)
  • i (446-446)
  • i (551-551)
  • i (588-588)
  • i (664-664)
  • i (708-708)
  • i (717-717)
  • j (202-202)
  • j (272-272)
  • j (666-666)
  • NUM_VS_BINS (21-21)
  • NUM_VS_BINS (21-21)
🔇 Additional comments (14)
src/graph_spectrum_plot.js (14)

3-3: Good addition of the NUM_VS_BINS import.

Adding this import ensures consistency with the FFT data structure defined in graph_spectrum_calc.js and is needed for accurate indexing of vertical spectrum bins.


14-16: Good addition of constants for zoom and dBm value ranges.

These constants provide clear boundaries for the power spectral density measurements and improve code readability by replacing magic numbers with named constants.


21-25: Good extension of the SPECTRUM_TYPE enum with new PSD display options.

The new spectrum types properly extend the visualization capabilities to include Power Spectral Density (PSD) and its variations with throttle and RPM.


177-191: Good implementation of new spectrum type handling.

The switch statement has been correctly extended to handle the new PSD spectrum types, reusing the existing vs-X graph rendering function with a PSD flag for throttle and RPM variants, and implementing a dedicated method for the base PSD visualization.


209-209: Good adjustment to bar width calculation for better visualization.

The updated calculation for bar width ensures proper spacing and visualization of frequency data in the FFT display.


411-416: Good implementation of PSD frequency lookup.

This helper method efficiently retrieves the PSD value for a specific frequency, handling bounds checking and edge cases appropriately.


418-418: Good enhancement with optional PSD parameter.

Adding the optional drawPSD parameter to the _drawFrequencyVsXGraph method allows reusing the same function for both regular and PSD spectrum types, reducing code duplication.


492-530: Good implementation of PSD heatmap scaling.

The heatmap generation has been enhanced to support PSD visualization with appropriate scaling between dBm values. The comment on line 494-495 correctly explains the magnitude adjustment for the heatmap scale.

The dBm range shifting based on zoom levels (lines 507-515) is a clever approach to allow users to focus on different parts of the spectrum by adjusting the vertical zoom.


545-555: Good implementation of matrix FFT value lookup.

This helper method correctly handles the mapping between frequency/vertical coordinates and their corresponding indices in the FFT data matrix, with appropriate bounds checking.


1126-1136: Good enhancement of vertical grid lines with configurable ticks.

The addition of the optional ticks parameter makes the vertical grid lines more flexible and adaptable to different visualization requirements, especially for the PSD display which may need different tick counts.


1292-1300: Good enhancement of interest frequency labeling for PSD.

The code now displays both the frequency and corresponding PSD value for interest markers when in PSD mode, providing more valuable information to the user.


1515-1549: Good implementation of PSD value display in mouse position.

This enhancement shows the PSD value at the mouse cursor position, greatly improving usability for analyzing specific frequency components in the spectrum.


1588-1599: Good implementation of matrix PSD value display for VS types.

This addition shows the PSD value at the intersection of frequency and throttle/RPM when hovering over PSD heat maps, making the visualization much more informative.


1647-1671: Good margin handling for new spectrum types.

The _getActualMarginLeft method has been properly updated to handle the new PSD spectrum types, ensuring consistent layout across all visualization modes.

🧰 Tools
🪛 Biome (1.9.4)

[error] 1665-1665: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.

(lint/complexity/noUselessSwitchCase)

@demvlad
Copy link
Contributor Author

demvlad commented May 17, 2025

The vertical slider works at the PSD heatmap charts.
It shifts "black" and "white" color dBm values together at -30, -20, -10, 0, +10 dBm, to look at white or black area.
0 slider [-40 ... +10] dBm range
1
Shift -10dBm [-50 ... 0] dBm range
2
Shift -20dBm [-60 ... -10] dBm range
3
Shift -30dBm [-70 ... -20] dBm range
4

@mituritsyn
Copy link
Contributor

mituritsyn commented May 18, 2025

Functionally, it looks fine.

@demvlad
Copy link
Contributor Author

demvlad commented May 18, 2025

Functionally, it looks fine.

Thank's!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants