Spectral Analysis

Overview

Introduction Spectral analysis is the discipline of decomposing a signal into frequency components so engineers can see what oscillations exist, how strong they are, and when they occur. In practical terms, it translates raw time-domain data (voltage, vibration, pressure, acoustic level, current, displacement) into interpretable frequency-domain information for diagnostics, design, and control. A high-level reference is the Spectral density article, which frames why power concentration by frequency is central to modern signal processing.

From a business perspective, spectral analysis reduces uncertainty in systems where failures and inefficiencies are frequency-dependent. Rotating equipment faults produce harmonics and sidebands before catastrophic failure. Electrical networks carry switching noise at specific bands that affect compliance and reliability. Structures resonate at natural frequencies, so design teams need to avoid exciting those modes. In each case, teams are not merely asking “what happened over time?” but “which physical mechanisms are active?” Frequency-domain evidence provides that answer.

This category groups the most common industrial spectral workflows into Excel-friendly calculators backed by scientific Python. The core estimators come from SciPy Signal, while FFT primitives use NumPy FFT. Together they support one-pass diagnostics, long-run monitoring, and advanced time-frequency decomposition:

  • ENG_PERIODOGRAM and ENG_WELCH estimate power spectral density (PSD) for stationary or approximately stationary data.
  • CSD and COHERENCE quantify cross-channel coupling in frequency.
  • STFT, SPECTROGRAM, and ISTFT handle nonstationary signals where content evolves over time.
  • RFFT and RFFTFREQ provide the direct one-sided transform and frequency bins for fast custom pipelines.
  • LOMBSCARGLE covers unevenly sampled data where FFT assumptions break.

This mapping also reflects Boardflare’s dual pillar strategy in technical work: physically grounded signal-processing methods on one side, and spreadsheet-operable decision workflows on the other. The result is not just “a chart,” but a repeatable process for turning sensor data into defensible engineering action.

When to Use It Spectral analysis is most valuable when the question is tied to periodicity, resonance, excitation, or coupling rather than simple time averages. Teams typically reach for these tools when time-domain plots look noisy or ambiguous, but domain knowledge suggests latent periodic structure.

One common scenario is predictive maintenance in rotating machinery. Suppose a reliability team monitors accelerometers on pumps and motors. RMS vibration may still be within limits, yet a spectral peak near bearing defect frequencies can appear weeks earlier. The team uses ENG_WELCH for robust PSD estimates across routine windows, then tracks peak growth and bandwidth over time. If two sensors on opposite housings show strong band-limited coupling via COHERENCE, the fault likely reflects a true mechanical path rather than local sensor noise. During root-cause work, CSD helps inspect phase-aware cross-power behavior between channels.

A second scenario is power electronics and grid-interfaced systems. Engineers validating an inverter care about switching harmonics, interharmonics, and control-loop artifacts. RFFT and RFFTFREQ support quick harmonic audits from short records, while ENG_PERIODOGRAM gives a baseline PSD estimate. For production test or noisy environments, ENG_WELCH is preferred because averaging lowers estimator variance and makes acceptance thresholds more stable. If current and voltage channels need causality clues in frequency, CSD and COHERENCE reveal where linear relationships are strong enough for transfer-function analysis.

A third scenario is structural dynamics and condition assessment for civil, aerospace, or automotive systems. Modal behavior is often time-varying: transient loads excite different bands as operating conditions change. Static PSD alone can hide this behavior. Here STFT and SPECTROGRAM show the time-frequency evolution, making it easier to isolate bursts, chirps, or intermittent resonances. After selective filtering or spectral editing, ISTFT reconstructs the time-domain signal for downstream fatigue or control simulations.

A fourth scenario appears in observational data where sampling is irregular, such as astronomy, remote telemetry, or field measurements with dropouts. Standard FFT methods assume uniform sample spacing and can produce misleading artifacts when that assumption is violated. LOMBSCARGLE is designed for this case and estimates periodic content from uneven timestamps without requiring naive interpolation.

In practice, teams combine these tools by workflow stage:

Use this category when decisions depend on separating mechanism from noise. If the objective is only central tendency or trend, simpler time-domain statistics are usually enough.

How It Works Spectral methods rely on representing a finite sampled signal as a sum of sinusoidal components. For a sampled sequence x[n], the discrete Fourier transform (DFT) is

X[k] = \sum_{n=0}^{N-1} x[n] e^{-j2\pi kn/N}

where k indexes frequency bins. For real-valued input, negative-frequency components mirror positive frequencies, so one-sided transforms via RFFT are efficient.

Frequency-axis interpretation is handled by RFFTFREQ:

f_k = \frac{k}{Nd}, \quad k=0,\dots,\left\lfloor\frac{N}{2}\right\rfloor

with sample spacing d=1/f_s. This axis is essential for converting “bin index” into engineering units (Hz).

For energy or power interpretation, teams estimate power spectral density (PSD). A basic estimate is the periodogram:

\hat{P}_{xx}(f) = \frac{1}{Nf_s}\left|X(f)\right|^2

implemented in ENG_PERIODOGRAM. The periodogram is simple and high-resolution, but its variance is high: adjacent runs can fluctuate strongly even when the underlying process is unchanged.

Welch’s method, implemented by ENG_WELCH, reduces this variance by splitting the signal into overlapping segments, windowing each segment, computing periodograms, and averaging:

\hat{P}^{\text{Welch}}_{xx}(f) = \frac{1}{M}\sum_{m=1}^{M} \hat{P}^{(m)}_{xx}(f)

This introduces a bias-variance tradeoff: better stability but reduced frequency resolution. Operationally, this is often the right trade for monitoring and alerting.

Cross-spectral methods generalize PSD to multiple channels. CSD estimates cross power spectral density P_{xy}(f), while COHERENCE normalizes it to the familiar magnitude-squared coherence:

C_{xy}(f) = \frac{|P_{xy}(f)|^2}{P_{xx}(f)P_{yy}(f)}

Values near 1 indicate strong linear relation at frequency f; values near 0 indicate weak linear relation. Coherence is widely used to distinguish real coupling from coincidental spectral peaks.

For nonstationary signals, time localization is required. STFT computes a Fourier transform on sliding windows:

X(m,\omega)=\sum_{n=-\infty}^{\infty}x[n]w[n-m]e^{-j\omega n}

where w is the window and m is time index of the frame. SPECTROGRAM usually reports |X(m,\omega)|^2 (or related modes such as magnitude/phase), producing a time-frequency energy map. ISTFT performs overlap-add reconstruction when analysis/synthesis conditions are consistent (windowing, hop size, overlap, and boundary conventions).

When timestamps are uneven, classical FFT assumptions do not hold. LOMBSCARGLE solves a frequency-wise least-squares problem against sinusoidal bases. Conceptually, for each angular frequency \omega, it fits

y(t) \approx a\cos(\omega t) + b\sin(\omega t)

and uses fit quality as spectral power. This avoids forcing irregular data onto a uniform grid before analysis.

Implementation assumptions matter as much as formulas:

  • Sampling and aliasing: meaningful spectra require sufficient sampling rate and anti-alias considerations.
  • Window choice: Hann/Tukey/boxcar and others alter leakage and amplitude behavior.
  • Segment settings: nperseg, noverlap, and nfft control resolution, variance, and interpolation density.
  • Detrending/scaling: options like constant/linear detrend and density vs spectrum scaling must match reporting intent.
  • Stationarity scope: PSD methods assume local stationarity; STFT/spectrogram methods relax this by short windows.

These calculators expose those controls directly, so engineering teams can align analysis settings with standards, acceptance criteria, and physics.

Practical Example Consider a packaging line with repeated bearing issues on a conveyor drive. The site has two accelerometers on the gearbox (input and output side) sampled at f_s=2{,}000 Hz, plus motor current data at the same rate. The team wants early-warning detection and root-cause confidence before scheduling downtime.

Step 1: Build the frequency axis and quick sanity transform. They run RFFT on short baseline captures and map bins with RFFTFREQ. This confirms that expected shaft orders and harmonics are observable and not buried by obvious clipping or acquisition errors.

Step 2: Establish baseline PSD with two estimators. They compute ENG_PERIODOGRAM to inspect high-resolution peaks and ENG_WELCH to create a stable monitoring baseline. In review meetings, Welch becomes the operational KPI source because it is less volatile run-to-run.

Step 3: Test channel coupling. A peak near a suspected defect frequency appears in both gearbox channels. They compute CSD and COHERENCE. High coherence in a narrow band supports a true mechanical path rather than independent sensor artifacts. Low coherence elsewhere helps deprioritize unrelated broadband noise.

Step 4: Check nonstationary behavior under load transitions. During startup and load changes, the team runs STFT and SPECTROGRAM. They observe that the problematic band intensifies only after a torque step, indicating a condition-dependent excitation. This timing clue is critical for maintenance planning and for reproducing the issue in controlled tests.

Step 5: Reconstruct filtered signal for time-domain validation. They isolate affected time-frequency regions and use ISTFT to reconstruct a filtered waveform. The reconstructed signal is fed into existing time-domain envelope and peak-to-peak checks already used in reliability dashboards.

Step 6: Extend analysis to telemetry gaps. Some overnight records contain missing packets from wireless gateways, making uniform sampling assumptions weak. For those windows, the team uses LOMBSCARGLE on timestamped subsets to verify that the same periodic component persists despite irregular spacing.

Step 7: Operationalize in Excel-based governance. Maintenance and operations teams track three decision metrics per asset: (1) Welch band power growth, (2) coherence at defect-related bands, and (3) persistence across both uniform and uneven-sampled methods. Work orders trigger only when all three exceed thresholds, reducing false positives and unnecessary interventions.

This workflow shows the value of combining rigorous signal-processing methods with spreadsheet-native execution. Engineers retain methodological control while operations stakeholders get transparent, repeatable criteria tied directly to cost and risk.

How to Choose Choose calculators by data characteristics first, then by diagnostic objective, then by reporting requirement. The matrix below covers the most common choices in this category.

Decision Need Recommended Function Use When Strengths Limits / Cautions
Fast one-sided transform of real data RFFT You need immediate frequency components from real-valued samples Very fast; minimal overhead; ideal for custom feature extraction Raw transform is not a PSD estimate; sensitive to windowing and record length
Frequency-bin axis for one-sided FFT RFFTFREQ You must map FFT bins to physical frequency units Simple, deterministic mapping from n and d Incorrect sample spacing gives incorrect engineering interpretation
Baseline PSD estimate with high detail ENG_PERIODOGRAM You want straightforward PSD with maximal nominal resolution Simple and transparent; useful for exploratory peak discovery High estimator variance; unstable for alert thresholds
Stable PSD for monitoring and thresholding ENG_WELCH You need robust repeatable PSD under noise Lower variance through averaging; strong default for operations Lower frequency resolution than periodogram; parameter tuning needed
Cross-spectrum between two channels CSD You need shared spectral content and phase-aware relation Quantifies common frequency content and phase structure Interpretation requires care if channels are weakly stationary or poorly synchronized
Normalized linear coupling by frequency COHERENCE You need confidence that shared peaks reflect true linear relation Scale-free 01 metric; excellent for validating coupling High coherence does not alone prove causality
Time-frequency decomposition for nonstationary signals STFT Frequency content changes over time Rich complex representation; supports phase-aware analysis Time-frequency resolution tradeoff depends on window/hop settings
Visual/array time-frequency power map SPECTROGRAM You need interpretable time-frequency energy tracking Easy pattern detection for bursts, chirps, transients Can hide phase detail depending on mode; parameter choices shape visibility
Reconstruct signal from STFT domain ISTFT You applied STFT-domain processing and need waveform output Enables overlap-add reconstruction and downstream time checks Requires parameter consistency with STFT and valid overlap/window assumptions
Spectral analysis for uneven timestamps LOMBSCARGLE Sampling is irregular or has gaps Avoids naive resampling; robust for irregular observations Frequency grid selection and normalization choices affect interpretation

A practical decision flow is:

graph TD
    A[Start: Define signal and objective] --> B{Uniform sampling?}
    B -- No --> L[Use LOMBSCARGLE]
    B -- Yes --> C{Need time-varying spectrum?}
    C -- Yes --> D[Use STFT or SPECTROGRAM]
    D --> E{Need reconstructed waveform?}
    E -- Yes --> F[Use ISTFT]
    E -- No --> G[Report time-frequency metrics]
    C -- No --> H{Single-channel or multi-channel?}
    H -- Single --> I{Exploration or monitoring?}
    I -- Exploration --> J[Use ENG_PERIODOGRAM]
    I -- Monitoring --> K[Use ENG_WELCH]
    H -- Multi --> M[Use CSD + COHERENCE]
    J --> N[Optional: RFFT + RFFTFREQ for custom features]
    K --> N
    M --> N

In most industrial settings, the best default stack is ENG_WELCH for baseline monitoring, COHERENCE for multi-sensor validation, and STFT/SPECTROGRAM when anomalies are episodic. ENG_PERIODOGRAM and RFFT are excellent tactical tools for rapid exploration, while LOMBSCARGLE is the right choice whenever timestamps are uneven.

For governance, teams should standardize parameter templates per asset class (sample rate, window type, segment length, overlap, scaling) and version-control those templates alongside threshold logic. That policy-level consistency makes cross-site comparisons meaningful and prevents hidden analysis drift over time. In dual-pillar terms, this is where scientific rigor and operational usability meet: advanced spectral methods rendered in a process stakeholders can execute, audit, and trust.

COHERENCE

Coherence is a frequency-domain measure of the linear relationship between two signals. It is the normalized cross power spectral density.

The magnitude squared coherence is defined as:

C_{xy}(f) = \frac{|P_{xy}(f)|^2}{P_{xx}(f) P_{yy}(f)}

Values range from 0 to 1, where 1 indicates perfect linear relationship and 0 indicates no relationship at that frequency.

Excel Usage

=COHERENCE(x, y, fs, window, nperseg, noverlap, nfft, spectral_detrend)
  • x (list[list], required): Time series of measurement values for first signal.
  • y (list[list], required): Time series of measurement values for second signal.
  • fs (float, optional, default: 1): Sampling frequency.
  • window (str, optional, default: “hann”): Desired window to use.
  • nperseg (int, optional, default: null): Length of each segment.
  • noverlap (int, optional, default: null): Number of points to overlap between segments.
  • nfft (int, optional, default: null): Length of the FFT used.
  • spectral_detrend (str, optional, default: “constant”): Specifies how to detrend each segment.

Returns (list[list]): A 2D array where the first row is frequencies and the second row is the magnitude squared coherence.

Example 1: Basic Coherence

Inputs:

x y
1 2 3 4 5 4 3 2 1 1.1 2.1 3.1 4.1 5.1 4.1 3.1 2.1 1.1

Excel formula:

=COHERENCE({1,2,3,4,5,4,3,2,1}, {1.1,2.1,3.1,4.1,5.1,4.1,3.1,2.1,1.1})

Expected output:

Result
0 0.111111 0.222222 0.333333 0.444444
1 1 1 1 1
Example 2: Coherence with linear detrending

Inputs:

x y spectral_detrend
1 3 5 7 9 11 13 15 2 4 6 8 10 12 14 16 linear

Excel formula:

=COHERENCE({1,3,5,7,9,11,13,15}, {2,4,6,8,10,12,14,16}, "linear")

Expected output:

Result
0 0.125 0.25 0.375 0.5
NaN NaN NaN NaN NaN
Example 3: Coherence with scalar-compatible single values

Inputs:

x y
2 2.5

Excel formula:

=COHERENCE(2, 2.5)

Expected output:

Result
0
NaN
Example 4: Coherence with explicit segment length

Inputs:

x y nperseg
0 1 0 -1 0 1 0 -1 0.1 0.9 0.2 -1.1 0.1 1 0 -0.9 4

Excel formula:

=COHERENCE({0,1,0,-1,0,1,0,-1}, {0.1,0.9,0.2,-1.1,0.1,1,0,-0.9}, 4)

Expected output:

Result
0 0.25 0.5
NaN 0.987256 NaN

Python Code

Show Code
import numpy as np
from scipy.signal import coherence as scipy_coherence

def coherence(x, y, fs=1, window='hann', nperseg=None, noverlap=None, nfft=None, spectral_detrend='constant'):
    """
    Estimate the magnitude squared coherence (Cxy) using Welch's method.

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.coherence.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        x (list[list]): Time series of measurement values for first signal.
        y (list[list]): Time series of measurement values for second signal.
        fs (float, optional): Sampling frequency. Default is 1.
        window (str, optional): Desired window to use. Default is 'hann'.
        nperseg (int, optional): Length of each segment. Default is None.
        noverlap (int, optional): Number of points to overlap between segments. Default is None.
        nfft (int, optional): Length of the FFT used. Default is None.
        spectral_detrend (str, optional): Specifies how to detrend each segment. Valid options: Constant, Linear, None. Default is 'constant'.

    Returns:
        list[list]: A 2D array where the first row is frequencies and the second row is the magnitude squared coherence.
    """
    try:
        def to_1d(v):
            if isinstance(v, list):
                if all(isinstance(row, list) for row in v):
                    return np.array([float(x) for row in v for x in row], dtype=float)
                return np.array([float(x) for x in v], dtype=float)
            return np.array([float(v)], dtype=float)

        x_arr = to_1d(x)
        y_arr = to_1d(y)

        detrend = spectral_detrend
        if detrend == "False":
            detrend = False

        f, cxy = scipy_coherence(
            x_arr, 
            y_arr, 
            fs=float(fs), 
            window=window, 
            nperseg=int(nperseg) if nperseg is not None else None, 
            noverlap=int(noverlap) if noverlap is not None else None, 
            nfft=int(nfft) if nfft is not None else None, 
            detrend=detrend
        )

        return [f.tolist(), cxy.tolist()]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Time series of measurement values for first signal.
Time series of measurement values for second signal.
Sampling frequency.
Desired window to use.
Length of each segment.
Number of points to overlap between segments.
Length of the FFT used.
Specifies how to detrend each segment.

CSD

The cross power spectral density (CSD) is a measure of the common frequency content between two signals. It is calculated by taking the Fourier transform of the cross-correlation between the two signals.

Like Welch’s method for power spectral density, this function estimates the CSD by dividing the signals into overlapping segments, computing the cross-periodogram for each segment pair, and averaging the results.

Excel Usage

=CSD(x, y, fs, window, nperseg, noverlap, nfft, spectral_detrend, return_onesided, spectral_scaling, avg_method)
  • x (list[list], required): Time series of measurement values for first signal.
  • y (list[list], required): Time series of measurement values for second signal.
  • fs (float, optional, default: 1): Sampling frequency.
  • window (str, optional, default: “hann”): Desired window to use.
  • nperseg (int, optional, default: null): Length of each segment.
  • noverlap (int, optional, default: null): Number of points to overlap between segments.
  • nfft (int, optional, default: null): Length of the FFT used.
  • spectral_detrend (str, optional, default: “constant”): Specifies how to detrend each segment.
  • return_onesided (bool, optional, default: true): If True, return a one-sided spectrum for real data.
  • spectral_scaling (str, optional, default: “density”): Selects between ‘density’ and ‘spectrum’.
  • avg_method (str, optional, default: “mean”): Method to use when averaging periodograms.

Returns (list[list]): A 2D array representing frequencies and cross spectral density.

Example 1: Basic CSD

Inputs:

x y
1 2 3 4 5 4 3 2 1 1.1 2.1 3.1 4.1 5.1 4.1 3.1 2.1 1.1

Excel formula:

=CSD({1,2,3,4,5,4,3,2,1}, {1.1,2.1,3.1,4.1,5.1,4.1,3.1,2.1,1.1})

Expected output:

Result
0 0.111111 0.222222 0.333333 0.444444
4.49611 10.5132 3.43142 0.263405 0.179396
0 5.26328e-16 -7.89492e-16 8.22387e-17 1.39806e-16
Example 2: CSD with median averaging

Inputs:

x y avg_method
1 2 1 2 1 2 1 2 2 1 2 1 2 1 2 1 median

Excel formula:

=CSD({1,2,1,2,1,2,1,2}, {2,1,2,1,2,1,2,1}, "median")

Expected output:

Result
0 0.125 0.25 0.375 0.5
0 -4.10865e-33 0 -0.666667 -1.33333
0 0 0 0 0
Example 3: CSD with spectrum scaling

Inputs:

x y spectral_scaling nperseg
0 1 0 -1 0 1 0 -1 0 1 0 -1 0 1 0 -1 spectrum 4

Excel formula:

=CSD({0,1,0,-1,0,1,0,-1}, {0,1,0,-1,0,1,0,-1}, "spectrum", 4)

Expected output:

Result
0 0.25 0.5
0 0.5 0
0 0 0
Example 4: CSD with scalar-compatible inputs

Inputs:

x y
3 3.2

Excel formula:

=CSD(3, 3.2)

Expected output:

Result
0
0
0

Python Code

Show Code
import numpy as np
from scipy.signal import csd as scipy_csd

def csd(x, y, fs=1, window='hann', nperseg=None, noverlap=None, nfft=None, spectral_detrend='constant', return_onesided=True, spectral_scaling='density', avg_method='mean'):
    """
    Estimate the cross power spectral density (Pxy) using Welch's method.

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.csd.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        x (list[list]): Time series of measurement values for first signal.
        y (list[list]): Time series of measurement values for second signal.
        fs (float, optional): Sampling frequency. Default is 1.
        window (str, optional): Desired window to use. Default is 'hann'.
        nperseg (int, optional): Length of each segment. Default is None.
        noverlap (int, optional): Number of points to overlap between segments. Default is None.
        nfft (int, optional): Length of the FFT used. Default is None.
        spectral_detrend (str, optional): Specifies how to detrend each segment. Valid options: Constant, Linear, None. Default is 'constant'.
        return_onesided (bool, optional): If True, return a one-sided spectrum for real data. Default is True.
        spectral_scaling (str, optional): Selects between 'density' and 'spectrum'. Valid options: Density, Spectrum. Default is 'density'.
        avg_method (str, optional): Method to use when averaging periodograms. Valid options: Mean, Median. Default is 'mean'.

    Returns:
        list[list]: A 2D array representing frequencies and cross spectral density.
    """
    try:
        def to_1d(v):
            if isinstance(v, list):
                if all(isinstance(row, list) for row in v):
                    return np.array([float(x) for row in v for x in row], dtype=float)
                return np.array([float(x) for x in v], dtype=float)
            return np.array([float(v)], dtype=float)

        x_arr = to_1d(x)
        y_arr = to_1d(y)

        detrend = spectral_detrend
        if detrend == "False":
            detrend = False

        f, pxy = scipy_csd(
            x_arr, 
            y_arr, 
            fs=float(fs), 
            window=window, 
            nperseg=int(nperseg) if nperseg is not None else None, 
            noverlap=int(noverlap) if noverlap is not None else None, 
            nfft=int(nfft) if nfft is not None else None, 
            detrend=detrend, 
            return_onesided=bool(return_onesided), 
              scaling=spectral_scaling,
            average=avg_method
        )

        return [f.tolist(), pxy.real.tolist(), pxy.imag.tolist()]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Time series of measurement values for first signal.
Time series of measurement values for second signal.
Sampling frequency.
Desired window to use.
Length of each segment.
Number of points to overlap between segments.
Length of the FFT used.
Specifies how to detrend each segment.
If True, return a one-sided spectrum for real data.
Selects between 'density' and 'spectrum'.
Method to use when averaging periodograms.

ENG_PERIODOGRAM

The periodogram is an estimate of the spectral density of a signal. It is calculated as the squared magnitude of the discrete Fourier transform (DFT) of the signal, normalized by the sampling frequency.

P(f) = \frac{\Delta t}{N} \left| \sum_{n=0}^{N-1} x_n e^{-i 2 \pi f n \Delta t} \right|^2

where x_n are the signal samples and N is the number of samples.

Excel Usage

=ENG_PERIODOGRAM(x, fs, window, nfft, spectral_detrend, return_onesided, spectral_scaling)
  • x (list[list], required): Time series of measurement values (Excel range).
  • fs (float, optional, default: 1): Sampling frequency of the x time series.
  • window (str, optional, default: “boxcar”): Desired window to use.
  • nfft (int, optional, default: null): Length of the FFT used.
  • spectral_detrend (str, optional, default: “constant”): Specifies how to detrend each segment.
  • return_onesided (bool, optional, default: true): If True, return a one-sided spectrum for real data.
  • spectral_scaling (str, optional, default: “density”): Selects between ‘density’ and ‘spectrum’.

Returns (list[list]): A 2D array where the first row is frequencies and the second row is the power spectral density.

Example 1: Basic PSD

Inputs:

x fs
1 2 3 4 5 4 3 2 1 1

Excel formula:

=ENG_PERIODOGRAM({1,2,3,4,5,4,3,2,1}, 1)

Expected output:

Result
0 0.111111 0.222222 0.333333 0.444444
8.76512e-32 15.2752 0.0178125 0.222222 0.0403322
Example 2: Periodogram with linear detrending

Inputs:

x spectral_detrend
1 3 5 7 9 11 13 15 linear

Excel formula:

=ENG_PERIODOGRAM({1,3,5,7,9,11,13,15}, "linear")

Expected output:

Result
0 0.125 0.25 0.375 0.5
0 0 0 0 0
Example 3: Periodogram with spectrum scaling

Inputs:

x spectral_scaling nfft
0 1 0 -1 0 1 0 -1 spectrum 8

Excel formula:

=ENG_PERIODOGRAM({0,1,0,-1,0,1,0,-1}, "spectrum", 8)

Expected output:

Result
0 0.125 0.25 0.375 0.5
0 0 0.5 0 0
Example 4: Periodogram with scalar-compatible input

Inputs:

x
5

Excel formula:

=ENG_PERIODOGRAM(5)

Expected output:

Result
0
0

Python Code

Show Code
import numpy as np
from scipy.signal import periodogram as scipy_periodogram

def eng_periodogram(x, fs=1, window='boxcar', nfft=None, spectral_detrend='constant', return_onesided=True, spectral_scaling='density'):
    """
    Estimate power spectral density using a periodogram.

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.periodogram.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        x (list[list]): Time series of measurement values (Excel range).
        fs (float, optional): Sampling frequency of the x time series. Default is 1.
        window (str, optional): Desired window to use. Default is 'boxcar'.
        nfft (int, optional): Length of the FFT used. Default is None.
        spectral_detrend (str, optional): Specifies how to detrend each segment. Valid options: Constant, Linear, None. Default is 'constant'.
        return_onesided (bool, optional): If True, return a one-sided spectrum for real data. Default is True.
        spectral_scaling (str, optional): Selects between 'density' and 'spectrum'. Valid options: Density, Spectrum. Default is 'density'.

    Returns:
        list[list]: A 2D array where the first row is frequencies and the second row is the power spectral density.
    """
    try:
        def to_1d(v):
            if isinstance(v, list):
                if all(isinstance(row, list) for row in v):
                    return np.array([float(x) for row in v for x in row], dtype=float)
                return np.array([float(x) for x in v], dtype=float)
            return np.array([float(v)], dtype=float)

        x_arr = to_1d(x)

        detrend = spectral_detrend
        if detrend == "False":
            detrend = False

        f, pxx = scipy_periodogram(
            x_arr, 
            fs=float(fs), 
            window=window, 
            nfft=int(nfft) if nfft is not None else None, 
            detrend=detrend, 
            return_onesided=bool(return_onesided), 
              scaling=spectral_scaling
        )

        return [f.tolist(), pxx.tolist()]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Time series of measurement values (Excel range).
Sampling frequency of the x time series.
Desired window to use.
Length of the FFT used.
Specifies how to detrend each segment.
If True, return a one-sided spectrum for real data.
Selects between 'density' and 'spectrum'.

ENG_WELCH

Welch’s method (also known as the periodogram averaging method) computes an estimate of the power spectral density by dividing the data into overlapping segments, computing a modified periodogram for each segment, and averaging these periodograms.

This method reduces noise in the estimated power spectra in exchange for reducing the frequency resolution. It is more robust than the standard periodogram for signals with significant noise.

Excel Usage

=ENG_WELCH(x, fs, window, nperseg, noverlap, nfft, spectral_detrend, return_onesided, spectral_scaling, avg_method)
  • x (list[list], required): Time series of measurement values (Excel range).
  • fs (float, optional, default: 1): Sampling frequency of the x time series.
  • window (str, optional, default: “hann”): Desired window to use.
  • nperseg (int, optional, default: null): Length of each segment.
  • noverlap (int, optional, default: null): Number of points to overlap between segments.
  • nfft (int, optional, default: null): Length of the FFT used.
  • spectral_detrend (str, optional, default: “constant”): Specifies how to detrend each segment.
  • return_onesided (bool, optional, default: true): If True, return a one-sided spectrum for real data.
  • spectral_scaling (str, optional, default: “density”): Selects between ‘density’ and ‘spectrum’.
  • avg_method (str, optional, default: “mean”): Method to use when averaging periodograms.

Returns (list[list]): A 2D array where the first row is frequencies and the second row is the power spectral density.

Example 1: Welch default (Hann window)

Inputs:

x
1 2 3 4 5 4 3 2 1 0 1 2 3 4 5 4 3 2 1

Excel formula:

=ENG_WELCH({1,2,3,4,5,4,3,2,1,0,1,2,3,4,5,4,3,2,1})

Expected output:

Result
0 0.0526316 0.105263 0.157895 0.210526 0.263158 0.315789 0.368421 0.421053 0.473684
0.37173 11.0677 27.5414 4.97818 0.0016883 0.323246 0.522246 0.0457904 0.00507076 0.185125
Example 2: Welch with median averaging

Inputs:

x avg_method nperseg
0 1 0 -1 0 1 0 -1 0 1 median 4

Excel formula:

=ENG_WELCH({0,1,0,-1,0,1,0,-1,0,1}, "median", 4)

Expected output:

Result
0 0.25 0.5
0 1.6 0
Example 3: Welch with spectrum scaling

Inputs:

x spectral_scaling nperseg noverlap
1 2 1 2 1 2 1 2 spectrum 4 2

Excel formula:

=ENG_WELCH({1,2,1,2,1,2,1,2}, "spectrum", 4, 2)

Expected output:

Result
0 0.25 0.5
0 0.125 0.25
Example 4: Welch with scalar-compatible input

Inputs:

x
4

Excel formula:

=ENG_WELCH(4)

Expected output:

Result
0
0

Python Code

Show Code
import numpy as np
from scipy.signal import welch as scipy_welch

def eng_welch(x, fs=1, window='hann', nperseg=None, noverlap=None, nfft=None, spectral_detrend='constant', return_onesided=True, spectral_scaling='density', avg_method='mean'):
    """
    Estimate power spectral density using Welch's method.

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.welch.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        x (list[list]): Time series of measurement values (Excel range).
        fs (float, optional): Sampling frequency of the x time series. Default is 1.
        window (str, optional): Desired window to use. Default is 'hann'.
        nperseg (int, optional): Length of each segment. Default is None.
        noverlap (int, optional): Number of points to overlap between segments. Default is None.
        nfft (int, optional): Length of the FFT used. Default is None.
        spectral_detrend (str, optional): Specifies how to detrend each segment. Valid options: Constant, Linear, None. Default is 'constant'.
        return_onesided (bool, optional): If True, return a one-sided spectrum for real data. Default is True.
        spectral_scaling (str, optional): Selects between 'density' and 'spectrum'. Valid options: Density, Spectrum. Default is 'density'.
        avg_method (str, optional): Method to use when averaging periodograms. Valid options: Mean, Median. Default is 'mean'.

    Returns:
        list[list]: A 2D array where the first row is frequencies and the second row is the power spectral density.
    """
    try:
        def to_1d(v):
            if isinstance(v, list):
                if all(isinstance(row, list) for row in v):
                    return np.array([float(x) for row in v for x in row], dtype=float)
                return np.array([float(x) for x in v], dtype=float)
            return np.array([float(v)], dtype=float)

        x_arr = to_1d(x)

        detrend = spectral_detrend
        if detrend == "False":
            detrend = False

        f, pxx = scipy_welch(
            x_arr, 
            fs=float(fs), 
            window=window, 
            nperseg=int(nperseg) if nperseg is not None else None, 
            noverlap=int(noverlap) if noverlap is not None else None, 
            nfft=int(nfft) if nfft is not None else None, 
            detrend=detrend, 
            return_onesided=bool(return_onesided), 
              scaling=spectral_scaling,
            average=avg_method
        )

        return [f.tolist(), pxx.tolist()]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Time series of measurement values (Excel range).
Sampling frequency of the x time series.
Desired window to use.
Length of each segment.
Number of points to overlap between segments.
Length of the FFT used.
Specifies how to detrend each segment.
If True, return a one-sided spectrum for real data.
Selects between 'density' and 'spectrum'.
Method to use when averaging periodograms.

ISTFT

The Inverse Short-Time Fourier Transform (ISTFT) reconstructs a time-domain signal from its STFT representation.

Faithful reconstruction requires that the STFT parameters (window, overlap, etc.) match those used in the forward transform and that the Constant OverLap Add (COLA) constraint is satisfied.

Excel Usage

=ISTFT(zxx_grid, fs, window, nperseg, noverlap, input_onesided, boundary)
  • zxx_grid (list[list], required): The STFT grid as returned by the stft function (Excel range).
  • fs (float, optional, default: 1): Sampling frequency.
  • window (str, optional, default: “hann”): Desired window to use.
  • nperseg (int, optional, default: null): Number of data points per segment.
  • noverlap (int, optional, default: null): Number of points to overlap between segments.
  • input_onesided (bool, optional, default: true): If True, interpret input as one-sided.
  • boundary (bool, optional, default: true): If True, input signal was extended at boundaries.

Returns (list[list]): A 2D array where the first row is segment times and the second row is the reconstructed time-domain signal.

Example 1: STFT/ISTFT roundtrip

Inputs:

zxx_grid fs
0 0.5 1
0 (Re) 1 1
0 (Im) 0 0

Excel formula:

=ISTFT({"",0,0.5;"0 (Re)",1,1;"0 (Im)",0,0}, 1)

Expected output:

Result
0
0.5
Example 2: ISTFT with two frequency bins

Inputs:

zxx_grid fs
0 0.5 2
0 (Re) 1 1
0 (Im) 0 0
0.5 (Re) 0 0
0.5 (Im) 0 0

Excel formula:

=ISTFT({"",0,0.5;"0 (Re)",1,1;"0 (Im)",0,0;"0.5 (Re)",0,0;"0.5 (Im)",0,0}, 2)

Expected output:

Result
0
0.5
Example 3: ISTFT with explicit segment and overlap

Inputs:

zxx_grid fs nperseg noverlap
0 1 2 4 2
0 (Re) 1 0
0 (Im) 0 0
0.5 (Re) 0.5 0.5
0.5 (Im) 0 0

Excel formula:

=ISTFT({"",0,1;"0 (Re)",1,0;"0 (Im)",0,0;"0.5 (Re)",0.5,0.5;"0.5 (Im)",0,0}, 2, 4, 2)

Expected output:

Result
0 0.5
0 0.5
Example 4: ISTFT without boundary extension

Inputs:

zxx_grid fs boundary
0 0.5 1 false
0 (Re) 0.5 0.5
0 (Im) 0 0

Excel formula:

=ISTFT({"",0,0.5;"0 (Re)",0.5,0.5;"0 (Im)",0,0}, 1, FALSE)

Expected output:

Result
0 1 2
0 0.25 0.25

Python Code

Show Code
import numpy as np
from scipy.signal import istft as scipy_istft

def istft(zxx_grid, fs=1, window='hann', nperseg=None, noverlap=None, input_onesided=True, boundary=True):
    """
    Perform the Inverse Short-Time Fourier Transform (ISTFT).

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.istft.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        zxx_grid (list[list]): The STFT grid as returned by the stft function (Excel range).
        fs (float, optional): Sampling frequency. Default is 1.
        window (str, optional): Desired window to use. Default is 'hann'.
        nperseg (int, optional): Number of data points per segment. Default is None.
        noverlap (int, optional): Number of points to overlap between segments. Default is None.
        input_onesided (bool, optional): If True, interpret input as one-sided. Default is True.
        boundary (bool, optional): If True, input signal was extended at boundaries. Default is True.

    Returns:
        list[list]: A 2D array where the first row is segment times and the second row is the reconstructed time-domain signal.
    """
    try:
      if not isinstance(zxx_grid, list) or not all(isinstance(row, list) for row in zxx_grid):
        return "Error: zxx_grid must be a 2D list"
      if len(zxx_grid) < 3:
        return "Error: zxx_grid must include header and at least one complex row pair"
      if (len(zxx_grid) - 1) % 2 != 0:
        return "Error: zxx_grid must contain pairs of real and imaginary rows"

      row_length = len(zxx_grid[0])
      if row_length < 2:
        return "Error: zxx_grid must include at least one time column"
      if any(len(row) != row_length for row in zxx_grid):
        return "Error: zxx_grid rows must all have the same length"

      zxx_rows = []
      for i in range(1, len(zxx_grid), 2):
          re_row = [float(x) for x in zxx_grid[i][1:]]
          im_row = [float(x) for x in zxx_grid[i+1][1:]]
          complex_row = [complex(r, im) for r, im in zip(re_row, im_row)]
          zxx_rows.append(complex_row)

      zxx_arr = np.array(zxx_rows)

      # Infer a reasonable nperseg from the given STFT matrix if not explicitly provided.
      # For a one-sided (default) STFT, the number of frequency bins is nperseg//2 + 1.
      # For a two-sided STFT, the number of frequency bins equals nperseg.
      if nperseg is None:
        bins = zxx_arr.shape[0]
        if bool(input_onesided):
          nperseg_val = max(2, 2 * (bins - 1))
        else:
          nperseg_val = max(1, bins)
      else:
        nperseg_val = int(nperseg)

      noverlap_val = None
      if noverlap is not None:
        noverlap_val = int(noverlap)
        if noverlap_val >= nperseg_val:
          return "Error: noverlap must be less than nperseg"

      # Ensure the STFT matrix has the expected number of frequency bins for the chosen nperseg.
      expected_bins = (nperseg_val // 2 + 1) if bool(input_onesided) else nperseg_val
      if zxx_arr.shape[0] < expected_bins:
        pad_rows = expected_bins - zxx_arr.shape[0]
        zxx_arr = np.vstack([zxx_arr, np.zeros((pad_rows, zxx_arr.shape[1]), dtype=complex)])
      elif zxx_arr.shape[0] > expected_bins:
        return "Error: zxx_grid has more frequency bins than expected for the given nperseg"

      t, x = scipy_istft(
          zxx_arr,
          fs=float(fs),
          window=window,
          nperseg=nperseg_val,
          noverlap=noverlap_val,
          input_onesided=bool(input_onesided),
          boundary=bool(boundary),
      )

      t_list = list(t)
      x_list = list(x)
      max_len = max(len(t_list), len(x_list))
      t_list += [""] * (max_len - len(t_list))
      x_list += [""] * (max_len - len(x_list))

      return [t_list, x_list]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

The STFT grid as returned by the stft function (Excel range).
Sampling frequency.
Desired window to use.
Number of data points per segment.
Number of points to overlap between segments.
If True, interpret input as one-sided.
If True, input signal was extended at boundaries.

LOMBSCARGLE

The Lomb-Scargle periodogram estimates spectral power for data sampled at uneven time intervals. It computes a least-squares fit of sinusoidal components across a set of angular frequencies, making it useful when standard FFT assumptions of uniform sampling are not met.

For observations (x_i, y_i) and angular frequency \omega, the method estimates power by fitting sinusoidal terms and evaluating the fit quality over the supplied frequency grid.

Excel Usage

=LOMBSCARGLE(x, y, freqs, precenter, normalize)
  • x (list[list], required): Sample times (Excel range).
  • y (list[list], required): Observed signal values (Excel range).
  • freqs (list[list], required): Angular frequency grid (radians per unit time) (Excel range).
  • precenter (bool, optional, default: false): If True, subtract the mean of y before computing the periodogram.
  • normalize (bool, optional, default: false): If True, normalize periodogram values by the residual variance.

Returns (list[list]): A 2D array where the first row is angular frequencies and the second row is Lomb-Scargle power values.

Example 1: Unevenly sampled signal periodogram

Inputs:

x y freqs
0 0.8 1.9 3.1 4.7 6 0 0.7 1 0.2 -0.8 -0.3 0.5 1 1.5 2

Excel formula:

=LOMBSCARGLE({0,0.8,1.9,3.1,4.7,6}, {0,0.7,1,0.2,-0.8,-0.3}, {0.5,1,1.5,2})

Expected output:

Result
0.5 1 1.5 2
0.662122 1.1068 0.156344 0.0138262
Example 2: Periodogram with precentering

Inputs:

x y freqs precenter
0 1.3 2 3.8 5.2 1.2 1.4 0.2 -0.6 -0.3 0.4 0.9 1.4 true

Excel formula:

=LOMBSCARGLE({0,1.3,2,3.8,5.2}, {1.2,1.4,0.2,-0.6,-0.3}, {0.4,0.9,1.4}, TRUE)

Expected output:

Result
0.4 0.9 1.4
1.24138 1.44547 0.278972
Example 3: Periodogram with normalization

Inputs:

x y freqs normalize
0 1 2 4 7 0.2 0.9 0.1 -0.5 -0.1 0.3 0.7 1.1 true

Excel formula:

=LOMBSCARGLE({0,1,2,4,7}, {0.2,0.9,0.1,-0.5,-0.1}, {0.3,0.7,1.1}, TRUE)

Expected output:

Result
0.3 0.7 1.1
0.403186 0.748432 0.589561
Example 4: Single angular frequency input

Inputs:

x y freqs
0 1 2 3 1 0.5 -0.2 -0.8 1.2

Excel formula:

=LOMBSCARGLE({0,1,2,3}, {1,0.5,-0.2,-0.8}, 1.2)

Expected output:

Result
1.2
0.924347

Python Code

Show Code
import numpy as np
from scipy.signal import lombscargle as scipy_lombscargle

def lombscargle(x, y, freqs, precenter=False, normalize=False):
    """
    Estimate a Lomb-Scargle periodogram for unevenly sampled data.

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.lombscargle.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        x (list[list]): Sample times (Excel range).
        y (list[list]): Observed signal values (Excel range).
        freqs (list[list]): Angular frequency grid (radians per unit time) (Excel range).
        precenter (bool, optional): If True, subtract the mean of y before computing the periodogram. Default is False.
        normalize (bool, optional): If True, normalize periodogram values by the residual variance. Default is False.

    Returns:
        list[list]: A 2D array where the first row is angular frequencies and the second row is Lomb-Scargle power values.
    """
    try:
        def to_1d(v):
            if isinstance(v, list):
                if all(isinstance(row, list) for row in v):
                    return np.array([float(x) for row in v for x in row], dtype=float)
                return np.array([float(x) for x in v], dtype=float)
            return np.array([float(v)], dtype=float)

        x_arr = to_1d(x)
        y_arr = to_1d(y)
        freqs_arr = to_1d(freqs)

        if x_arr.size != y_arr.size:
            return "Error: x and y must contain the same number of elements"
        if freqs_arr.size == 0:
            return "Error: freqs must contain at least one value"

        pgram = scipy_lombscargle(
            x_arr,
            y_arr,
            freqs_arr,
            precenter=bool(precenter),
            normalize=bool(normalize)
        )

        return [freqs_arr.tolist(), pgram.tolist()]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Sample times (Excel range).
Observed signal values (Excel range).
Angular frequency grid (radians per unit time) (Excel range).
If True, subtract the mean of y before computing the periodogram.
If True, normalize periodogram values by the residual variance.

RFFT

The real FFT computes the discrete Fourier transform (DFT) of a real-valued signal and returns only the nonnegative-frequency terms. This reduces redundant output compared with the full complex FFT because negative-frequency components are implied by conjugate symmetry.

For a real sequence x_n of length N, the transform is:

X_k = \sum_{n=0}^{N-1} x_n e^{-i 2\pi kn/N}, \quad k = 0, \dots, \lfloor N/2 \rfloor

Excel Usage

=RFFT(a, n, axis, norm)
  • a (list[list], required): Real-valued signal samples (Excel range).
  • n (int, optional, default: null): Length of the transformed axis of the output.
  • axis (int, optional, default: -1): Axis over which to compute the transform.
  • norm (str, optional, default: null): Normalization mode.

Returns (list[list]): A 2D array where the first row is the real part and the second row is the imaginary part of the one-sided FFT output.

Example 1: One-sided FFT of simple real signal

Inputs:

a
0 1 0 -1

Excel formula:

=RFFT({0,1,0,-1})

Expected output:

Result
0 0 0
0 -2 0
Example 2: One-sided FFT with explicit transform length

Inputs:

a n
1 2 3 8

Excel formula:

=RFFT({1,2,3}, 8)

Expected output:

Result
6 2.41421 -2 -0.414214 2
0 -4.41421 -2 1.58579 0
Example 3: One-sided FFT with orthonormal scaling

Inputs:

a norm
2 0 -2 0 ortho

Excel formula:

=RFFT({2,0,-2,0}, "ortho")

Expected output:

Result
0 2 0
0 0 0
Example 4: One-sided FFT for single scalar input

Inputs:

a
5

Excel formula:

=RFFT(5)

Expected output:

Result
5
0

Python Code

Show Code
import numpy as np
from numpy.fft import rfft as numpy_rfft

def rfft(a, n=None, axis=-1, norm=None):
    """
    Compute the one-dimensional discrete Fourier transform for real input.

    See: https://numpy.org/doc/stable/reference/generated/numpy.fft.rfft.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        a (list[list]): Real-valued signal samples (Excel range).
        n (int, optional): Length of the transformed axis of the output. Default is None.
        axis (int, optional): Axis over which to compute the transform. Default is -1.
        norm (str, optional): Normalization mode. Valid options: Backward, Ortho, Forward. Default is None.

    Returns:
        list[list]: A 2D array where the first row is the real part and the second row is the imaginary part of the one-sided FFT output.
    """
    try:
        def to_ndarray(v):
            if isinstance(v, list):
                if all(isinstance(row, list) for row in v):
                    return np.array([[float(x) for x in row] for row in v], dtype=float)
                return np.array([float(x) for x in v], dtype=float)
            return np.array([float(v)], dtype=float)

        a_arr = to_ndarray(a)
        n_val = int(n) if n is not None else None
        axis_val = int(axis)
        norm_val = norm if norm is not None and str(norm) != "" else None

        result = numpy_rfft(a_arr, n=n_val, axis=axis_val, norm=norm_val)
        flat_result = np.ravel(result)

        return [flat_result.real.tolist(), flat_result.imag.tolist()]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Real-valued signal samples (Excel range).
Length of the transformed axis of the output.
Axis over which to compute the transform.
Normalization mode.

RFFTFREQ

This function returns the discrete frequency-bin centers corresponding to the output of a one-sided real FFT. It provides the nonnegative frequency axis used to interpret amplitudes from real-input transforms.

For transform length n and sample spacing d, the bins are:

f_k = \frac{k}{d n}, \quad k = 0, 1, \dots, \left\lfloor\frac{n}{2}\right\rfloor

Excel Usage

=RFFTFREQ(n, d)
  • n (int, required): Window length.
  • d (float, optional, default: 1): Sample spacing (inverse of sampling rate).

Returns (list[list]): A single-row 2D array of nonnegative sample frequencies.

Example 1: Frequency bins for even length

Inputs:

n
8

Excel formula:

=RFFTFREQ(8)

Expected output:

Result
0 0.125 0.25 0.375 0.5
Example 2: Frequency bins for odd length

Inputs:

n
7

Excel formula:

=RFFTFREQ(7)

Expected output:

Result
0 0.142857 0.285714 0.428571
Example 3: Frequency bins with custom sample spacing

Inputs:

n d
8 0.1

Excel formula:

=RFFTFREQ(8, 0.1)

Expected output:

Result
0 1.25 2.5 3.75 5
Example 4: Frequency bins for longer transform

Inputs:

n d
16 0.5

Excel formula:

=RFFTFREQ(16, 0.5)

Expected output:

Result
0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1

Python Code

Show Code
from numpy.fft import rfftfreq as numpy_rfftfreq

def rfftfreq(n, d=1):
    """
    Return sample frequencies for one-sided real FFT bins.

    See: https://numpy.org/doc/stable/reference/generated/numpy.fft.rfftfreq.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        n (int): Window length.
        d (float, optional): Sample spacing (inverse of sampling rate). Default is 1.

    Returns:
        list[list]: A single-row 2D array of nonnegative sample frequencies.
    """
    try:
        n_val = int(n)
        d_val = float(d)

        if n_val <= 0:
            return "Error: n must be a positive integer"
        if d_val <= 0:
            return "Error: d must be positive"

        freqs = numpy_rfftfreq(n_val, d=d_val)
        return [freqs.tolist()]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Window length.
Sample spacing (inverse of sampling rate).

SPECTROGRAM

A spectrogram is a visual representation of the spectrum of frequencies of a signal as it varies with time. When applied to an audio signal, spectrograms are sometimes called sonographs, voiceprints, or waterfalls.

The spectrogram is computed by taking the squared magnitude of the Short-Time Fourier Transform (STFT) of the signal. It provides a time-frequency distribution of the signal’s energy.

Excel Usage

=SPECTROGRAM(x, fs, window, nperseg, noverlap, nfft, spectral_detrend, return_onesided, spectral_scaling, spectrogram_mode)
  • x (list[list], required): Time series of measurement values.
  • fs (float, optional, default: 1): Sampling frequency.
  • window (str, optional, default: “tukey”): Desired window to use.
  • nperseg (int, optional, default: null): Length of each segment.
  • noverlap (int, optional, default: null): Number of points to overlap between segments.
  • nfft (int, optional, default: null): Length of the FFT used.
  • spectral_detrend (str, optional, default: “constant”): Specifies how to detrend each segment.
  • return_onesided (bool, optional, default: true): If True, return a one-sided spectrum for real data.
  • spectral_scaling (str, optional, default: “density”): Selects between power spectral density (‘density’) and power spectrum (‘spectrum’).
  • spectrogram_mode (str, optional, default: “psd”): Defines what kind of return values are expected.

Returns (list[list]): A 2D array representing the spectrogram grid.

Example 1: Basic Spectrogram

Inputs:

x
1 2 3 4 5 4 3 2 1 0 1 2 3 4 5 4 3 2 1

Excel formula:

=SPECTROGRAM({1,2,3,4,5,4,3,2,1,0,1,2,3,4,5,4,3,2,1})

Expected output:

Result
9.5
0 0.582152
0.0526316 4.91559
0.105263 31.2987
0.157895 0.354778
0.210526 0.598176
0.263158 0.841238
0.315789 0.268336
0.368421 0.0406904
0.421053 0.0463173
0.473684 0.20166
Example 2: Spectrogram in magnitude mode

Inputs:

x spectrogram_mode nperseg
0 1 0 -1 0 1 0 -1 0 1 magnitude 4

Excel formula:

=SPECTROGRAM({0,1,0,-1,0,1,0,-1,0,1}, "magnitude", 4)

Expected output:

Result
2 6
0 0 0
0.25 1.1547 1.1547
0.5 0 0
Example 3: Spectrogram in phase mode

Inputs:

x spectrogram_mode nperseg noverlap
1 2 1 2 1 2 1 2 phase 4 2

Excel formula:

=SPECTROGRAM({1,2,1,2,1,2,1,2}, "phase", 4, 2)

Expected output:

Result
2 4 6
0 0 0 0
0.25 0 0 0
0.5 3.14159 3.14159 3.14159
Example 4: Spectrogram with scalar-compatible input

Inputs:

x
2

Excel formula:

=SPECTROGRAM(2)

Expected output:

Result
0.5
0 0

Python Code

Show Code
import numpy as np
from scipy.signal import spectrogram as scipy_spectrogram

def spectrogram(x, fs=1, window='tukey', nperseg=None, noverlap=None, nfft=None, spectral_detrend='constant', return_onesided=True, spectral_scaling='density', spectrogram_mode='psd'):
    """
    Compute a spectrogram with consecutive Fourier transforms.

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.spectrogram.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        x (list[list]): Time series of measurement values.
        fs (float, optional): Sampling frequency. Default is 1.
        window (str, optional): Desired window to use. Default is 'tukey'.
        nperseg (int, optional): Length of each segment. Default is None.
        noverlap (int, optional): Number of points to overlap between segments. Default is None.
        nfft (int, optional): Length of the FFT used. Default is None.
        spectral_detrend (str, optional): Specifies how to detrend each segment. Valid options: Constant, Linear, None. Default is 'constant'.
        return_onesided (bool, optional): If True, return a one-sided spectrum for real data. Default is True.
        spectral_scaling (str, optional): Selects between power spectral density ('density') and power spectrum ('spectrum'). Valid options: Density, Spectrum. Default is 'density'.
        spectrogram_mode (str, optional): Defines what kind of return values are expected. Valid options: PSD, Magnitude, Angle, Phase. Default is 'psd'.

    Returns:
        list[list]: A 2D array representing the spectrogram grid.
    """
    try:
        def to_1d(v):
            if isinstance(v, list):
                if all(isinstance(row, list) for row in v):
                    return np.array([float(x) for row in v for x in row], dtype=float)
                return np.array([float(x) for x in v], dtype=float)
            return np.array([float(v)], dtype=float)

        x_arr = to_1d(x)

        detrend = spectral_detrend
        if detrend == "False":
            detrend = False

        win = window
        if win == "tukey":
            win = ('tukey', 0.25)

        f, t, sxx = scipy_spectrogram(
            x_arr, 
            fs=float(fs), 
            window=win, 
            nperseg=int(nperseg) if nperseg is not None else None, 
            noverlap=int(noverlap) if noverlap is not None else None, 
            nfft=int(nfft) if nfft is not None else None, 
            detrend=detrend, 
            return_onesided=bool(return_onesided), 
              scaling=spectral_scaling,
            mode=spectrogram_mode
        )

        output = []
        header = [""] + t.tolist()
        output.append(header)
        for i, freq in enumerate(f):
            row = [float(freq)] + sxx[i, :].tolist()
            output.append(row)

        return output
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Time series of measurement values.
Sampling frequency.
Desired window to use.
Length of each segment.
Number of points to overlap between segments.
Length of the FFT used.
Specifies how to detrend each segment.
If True, return a one-sided spectrum for real data.
Selects between power spectral density ('density') and power spectrum ('spectrum').
Defines what kind of return values are expected.

STFT

The Short-Time Fourier Transform (STFT) is used to quantify the change of a nonstationary signal’s frequency and phase content over time.

It is computed by dividing a longer time signal into shorter segments of equal length and then computing the Fourier transform separately on each shorter segment.

Excel Usage

=STFT(x, fs, window, nperseg, noverlap, nfft, spectral_detrend, return_onesided, boundary, padded)
  • x (list[list], required): Time series of measurement values.
  • fs (float, optional, default: 1): Sampling frequency.
  • window (str, optional, default: “hann”): Desired window to use.
  • nperseg (int, optional, default: 256): Length of each segment.
  • noverlap (int, optional, default: null): Number of points to overlap between segments.
  • nfft (int, optional, default: null): Length of the FFT used.
  • spectral_detrend (str, optional, default: “False”): Specifies how to detrend each segment.
  • return_onesided (bool, optional, default: true): If True, return a one-sided spectrum.
  • boundary (str, optional, default: “zeros”): Specifies whether the input signal is extended at both ends.
  • padded (bool, optional, default: true): If True, zero-pad signal to fit segments.

Returns (list[list]): A 2D array representing the STFT grid (interleaved real and imaginary).

Example 1: Basic STFT

Inputs:

x
1 2 3 4 5 4 3 2 1

Excel formula:

=STFT({1,2,3,4,5,4,3,2,1})

Expected output:

Result
0 5 10
0.0 (Re) 1.64381 3.21239 0.143807
0.0 (Im) 0 0 0
0.1111111111111111 (Re) -0.694229 -1.86154 0.0557706
0.1111111111111111 (Im) 0.95196 -0.828119 -0.123841
0.2222222222222222 (Re) -0.0772516 0.154503 -0.0772516
0.2222222222222222 (Im) -0.317934 0.400537 -0.0826035
0.3333333333333333 (Re) -0.0719035 0.143807 -0.0719035
0.3333333333333333 (Im) -0.018854 -0.0156369 0.034491
0.4444444444444444 (Re) 0.0214811 -0.0429621 0.0214811
0.4444444444444444 (Im) 0.0291447 -0.0703818 0.0412371
Example 2: STFT with constant detrending

Inputs:

x spectral_detrend nperseg
0 1 0 -1 0 1 0 -1 constant 4

Excel formula:

=STFT({0,1,0,-1,0,1,0,-1}, "constant", 4)

Expected output:

Result
0 2 4 6 8
0.0 (Re) 0 0 0 0 0
0.0 (Im) 0 0 0 0 0
0.25 (Re) 0.125 0 0 0 -0.125
0.25 (Im) 0.25 -0.5 0.5 -0.5 0.25
0.5 (Re) -0.25 0 0 0 0.25
0.5 (Im) 0 0 0 0 0
Example 3: STFT without boundary extension and padding

Inputs:

x boundary padded nperseg noverlap
1 2 1 2 1 2 1 2 None false 4 2

Excel formula:

=STFT({1,2,1,2,1,2,1,2}, "None", FALSE, 4, 2)

Expected output:

Result
2 4 6
0.0 (Re) 1.5 1.5 1.5
0.0 (Im) 0 0 0
0.25 (Re) -0.5 -0.5 -0.5
0.25 (Im) 0 0 0
0.5 (Re) -0.5 -0.5 -0.5
0.5 (Im) 0 0 0
Example 4: STFT with scalar-compatible input

Inputs:

x
3

Excel formula:

=STFT(3)

Expected output:

Result
0
0.0 (Re) 3
0.0 (Im) 0

Python Code

Show Code
import numpy as np
from scipy.signal import stft as scipy_stft

def stft(x, fs=1, window='hann', nperseg=256, noverlap=None, nfft=None, spectral_detrend='False', return_onesided=True, boundary='zeros', padded=True):
    """
    Compute the Short Time Fourier Transform (STFT).

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.stft.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        x (list[list]): Time series of measurement values.
        fs (float, optional): Sampling frequency. Default is 1.
        window (str, optional): Desired window to use. Default is 'hann'.
        nperseg (int, optional): Length of each segment. Default is 256.
        noverlap (int, optional): Number of points to overlap between segments. Default is None.
        nfft (int, optional): Length of the FFT used. Default is None.
        spectral_detrend (str, optional): Specifies how to detrend each segment. Valid options: Constant, Linear, None. Default is 'False'.
        return_onesided (bool, optional): If True, return a one-sided spectrum. Default is True.
        boundary (str, optional): Specifies whether the input signal is extended at both ends. Valid options: Zeros, Even, Odd, Constant, None. Default is 'zeros'.
        padded (bool, optional): If True, zero-pad signal to fit segments. Default is True.

    Returns:
        list[list]: A 2D array representing the STFT grid (interleaved real and imaginary).
    """
    try:
        def to_1d(v):
            if isinstance(v, list):
                if all(isinstance(row, list) for row in v):
                    return np.array([float(x) for row in v for x in row], dtype=float)
                return np.array([float(x) for x in v], dtype=float)
            return np.array([float(v)], dtype=float)

        x_arr = to_1d(x)

        detrend = spectral_detrend
        if detrend == "False":
            detrend = False

        bound = boundary
        if bound == "None":
            bound = None

        f, t, zxx = scipy_stft(
            x_arr, 
            fs=float(fs), 
            window=window, 
            nperseg=int(nperseg), 
            noverlap=int(noverlap) if noverlap is not None else None, 
            nfft=int(nfft) if nfft is not None else None, 
            detrend=detrend, 
            return_onesided=bool(return_onesided), 
            boundary=bound,
            padded=bool(padded)
        )

        output = []
        header = [""] + t.tolist()
        output.append(header)
        for i, freq in enumerate(f):
            row_real = [f"{freq} (Re)"] + zxx[i, :].real.tolist()
            row_imag = [f"{freq} (Im)"] + zxx[i, :].imag.tolist()
            output.append(row_real)
            output.append(row_imag)

        return output
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Time series of measurement values.
Sampling frequency.
Desired window to use.
Length of each segment.
Number of points to overlap between segments.
Length of the FFT used.
Specifies how to detrend each segment.
If True, return a one-sided spectrum.
Specifies whether the input signal is extended at both ends.
If True, zero-pad signal to fit segments.