Generated reference › Fourier Synchrosqueezed Transform — Control Systems/Time Frequency
kind: generated#block#control-systems-time-frequency

Fourier Synchrosqueezed Transform — Control Systems/Time Frequency

Control_Systems/Time_Frequency/Fourier_Synchrosqueezed_Transform · 1 input / 4 output port(s) at insert · exports to Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Description#

The block's own DESCRIPTION_HTML, rendered verbatim — the same text the config dialog's info panel and the library navigator show. Fix a wrong sentence in the block's .cpp (R-D9), never here.

Fourier Synchrosqueezed Transform

Control Systems / Time Frequency

The synchrosqueezed short-time transform of a stream – MATLAB's fsst, one column per sample. The last L samples are transformed twice, with the window and with the window's derivative; the ratio gives every bin its own instantaneous frequency and the bin's contribution is moved there:

ν[m] = m − Im(D[m] ÷ X[m]),   S[round(ν) mod L] += X[m]·(−1)m

so a ridge an ordinary spectrogram spreads over several bins comes back on one. The same rows, summed over a band and scaled, reconstruct the signal – that is MATLAB's ifsst, and it is the fourth output.

Ports

  • u – the sampled signal. Scalar: one channel and its own window.
  • Mag – |S|, the synchrosqueezed column, [L/2+1, 1], one-sided with DC first and Nyquist last. Row m is the normalized frequency m/L in cycles per sample, or m·fs/L in hertz for a signal sampled at fs. This is the map to plot, and the map Time-Frequency Ridges reads.
  • Re – the real part of that same column, [L/2+1, 1]: the synchrosqueezed transform is complex, and a wire carries a real number, so its two halves ride two ports.
  • Im – its imaginary part, same size, same rows.
  • xr – [1, 1], the signal reconstructed from the rows inside Reconstruction Band – the sample at the published column's own time, which is L/2 − 1 samples behind the input (see Frames and timing). Over the whole band it is the input itself, delayed by that much.

Parameters

  • Window Length – L, the window and the transform length: an even whole number from 4 to 20. Even because a one-sided spectrum needs a Nyquist bin, and because the transform's phase shift onto the window centre is then exactly (−1)m. Bounded above because the four transforms are unrolled at export: a column costs 4·L² inlined products.
  • Window – the analysis window, the symmetric shapes MATLAB's kaiser(), hann() and hamming() return:
    • Kaiser – fsst's own default, shaped by Kaiser Beta.
    • Hann – two end taps of exactly zero.
    • Hamming – end taps of 0.08.
    The window's derivative comes from a not-a-knot cubic spline through its taps, which is what MATLAB differentiates, and it decides every instantaneous frequency the block computes.
  • Kaiser Beta – β, the Kaiser shape parameter, a scalar of zero or more (MATLAB's fsst uses 10). Read only by the Kaiser window; a larger value means a wider main lobe and lower side lobes.
  • Reconstruction Band – the rows xr is summed over, as [low high] in normalized frequency (cycles per sample, 0 to 0.5), so the default [0 0.5] is the whole spectrum whatever L is. Each edge snaps to the nearest row – MATLAB's rule – and a tie keeps the lower one. The band is read by xr only; Mag, Re and Im always carry every row.
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.

Frames and timing

fsst fixes its overlap at L − 1 samples, so it has one column per input sample and pads the record with L/2 zeros in front. Column k therefore reads samples k − L/2 to k + L/2 − 1, and the newest column a stream can finish at sample n is k = n − L/2 + 1: the four outputs run L/2 − 1 samples behind the input, and they are all zero until the register first fills at sample L − 1. The leading zeros of the register are exactly MATLAB's leading zero padding, so the columns agree from the first one published.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. The window, its spline derivative and the transform's basis are fixed by the configuration and inlined as numbers, so no generated core computes a sine, a cosine or a Bessel function; the reassignment's rounding is a bounded count and its range reduction a bounded halving loop, so no core needs a floor either (PLC Structured Text has none). All four settings are structural – re-export after changing one.

The three HDL targets carry the transform in real arithmetic and are therefore simulation-only: the instantaneous frequency is a division by the transform's own squared magnitude, which leaves Q16.16 in both directions. The window is held in the port's fixed-point format, so quantization happens only at the port boundary.

⚠ The destination row is a ROUNDING, so it is a discrete answer. A bin whose instantaneous frequency sits within a quantum of a half-row boundary can be reassigned one row apart in fixed point, and that row then differs by the whole of that bin's contribution. It is arithmetic and not a code-generation defect – the same algorithm on different numbers – and it is why a fixed-point comparison of this block should be read as a spread rather than a single figure. Feeding it a narrowband signal, whose bins all agree about one frequency, keeps every decision far from its boundary.

Simulink bridge

None (Support::None). fsst is a Signal Processing Toolbox function and that toolbox ships no Simulink library, so there is no path a diagram could name. The bridge reports this block rather than dropping it silently, and it therefore has no parity testbench; code export verification still covers it across all ten languages. Sampling Time (s) → SampleTime, as on every block, has nothing to cross to for the same reason.

Notes

  • Stateful, and discrete by nature: a register of the last L samples and the column published from it.
  • The reassignment runs over the whole two-sided spectrum before the one-sided half is taken, exactly as fsst does, so a bin from the negative half whose frequency wraps into the positive one still contributes. That is also why the rows are not doubled.
  • Verified against R2026a: the whole complex matrix to 1.96e-14 on a scale of 10.68, the full-band reconstruction to 8.9e-16 and a band reconstruction to 4.4e-16 (the source banner carries what was checked).
  • Not reproduced: a window vector given tap by tap, an odd window length, a complex input (fsst then returns a centred two-sided spectrum), and ifsst's reconstruction along a ridge with NumFrequencyBins – that one needs a ridge index per column, which is a second input this block does not have.
  • It is not Spectrogram. Spectrogram publishes |X| every hop samples and holds in between; this block publishes a column every sample and moves each bin's energy to where its instantaneous frequency says it belongs, which is what makes a ridge sharp enough to follow.
  • No state space. The instantaneous frequency is a ratio of two transforms, so the block is not linear in its input and model reduction correctly declines to merge it.

Code facts#

FactValue
registered typeControl_Systems/Time_Frequency/Fourier_Synchrosqueezed_Transform
familyControl_Systems/Time_Frequency
solver environment classICoreBlock_0_Control_Systems_1_Time_Frequency_2_Fourier_Synchrosqueezed_Transform
sourcesrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Time_Frequency/Fourier_Synchrosqueezed_Transform/ICoreBlock_0_Control_Systems_1_Time_Frequency_2_Fourier_Synchrosqueezed_Transform.cpp
headersrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Time_Frequency/Fourier_Synchrosqueezed_Transform/ICoreBlock_0_Control_Systems_1_Time_Frequency_2_Fourier_Synchrosqueezed_Transform.h
default size on canvas170 × 100 px
ports at insert1 in, 4 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoubleu
2outICoreDoubleMag
3outICoreDoubleRe
4outICoreDoubleIm
5outICoreDoublexr

Ports the constructor creates. A block whose port list changes with its configuration adds or removes ports at load time; the count above is the one a freshly inserted block has.

Configuration variables#

Config variableDefaultSimulink parameter
Window Length16—
WindowFS::WIN_KAISER%~%FS::WIN_HANN%~%FS::WIN_HAMMING~~FS::WIN_…—
Kaiser Beta10—
Reconstruction Band[0 0.5]—

Every block also carries Sampling Time (s) from ICoreBlockSolverEnvironment: zero or less inherits the solver's rate, a positive value runs the block at that period.

supportSupport::None
Simulink path—
port-count rulePortsParam::None
SampleTime parameteryes

Caveat (shown to the user): fsst is a Signal Processing Toolbox function, not a Simulink library block -- that toolbox ships no Simulink library at all -- so there is no path a diagram could name; the block is reported rather than dropped when a model crosses

Catalog contract: src/ICoreBlocks/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h

Description vs code#

The lists agree. check_block_descriptions.py finds no disagreement between the description's Ports, Parameters, Code export and Simulink bridge lists and the code's.

The verdict above is tools/docs/check_block_descriptions.py (P7.1), which compares LISTS. It cannot read a sentence: "stateless" on a block with a state, an initial-value semantic the recursion does not implement, a "not synthesizable" caveat the HDL banner contradicts. That is the agent audit (P7.3) on BLOCK_DESCRIPTION_AUDIT.md, and this tool's green is not a substitute for one.

File banner (developer view)#

The top comment of the block's .cpp — the maths, the realization and the export strategy, addressed to whoever changes it. It must not contradict the description above (P7.5).

Fourier Synchrosqueezed Transform -- MATLAB's fsst, one column per sample, and its inverse A windowed transform of the last L samples is taken twice, once with the window and once with the window's derivative. Their ratio gives every bin its own instantaneous frequency, and each bin's contribution is MOVED to the row nearest that frequency:

X[m] = SUM(n) w[n] * fr[n] * e^(-i2*pi*m*n/L) D[m] = SUM(n) w'[n] * fr[n] * e^(-i2*pi*m*n/L) w' in bins: the derivative * L/(2*pi) nu = m - Im(D[m]/X[m]) S[mod(round(nu), L)] += X[m] * (-1)^m

so a ridge an ordinary spectrogram smears over several bins comes back on one. The one-sided rows 0 .. L/2 are published, and the same rows, summed over a band and scaled, reconstruct the signal -- MATLAB's ifsst.

MEASURED AGAINST R2026a BEFORE A LINE WAS WRITTEN, on the 24-sample record x = [1.2 0.7 2.5 0.3 0.12 0.8 1.1 -0.22 0.45 1.7 0.35 0.62 0.09 -1.4 0.66 0.2 -0.9 1.05 0.4 -0.55 0.8 -0.3 1.15 0.05] at L = 16, and every convention was checked rather than assumed:

  • fsst fixes noverlap at L-1, so there is ONE COLUMN PER SAMPLE, and it pads the record with

L/2 zeros in front and L/2-1 behind (even L). Column k therefore reads x[k-L/2 .. k+L/2-1] and the newest column a stream can complete at sample n is k = n - L/2 + 1: a latency of L/2 - 1. Verified by taking fsst of the first 16 samples alone -- its first nine columns equal the 24-sample record's to 0.0 -- so a register starting at zero reproduces the leading zero padding exactly.

  • The whole transform is INDEPENDENT OF THE SAMPLING RATE: fsst(x, 100, w) minus

fsst(x, 1, w) is exactly 0.0 in both parts, and so is the reconstruction, because the rate cancels between the window derivative's Fs/(2*pi), the frequency vector's m*Fs/L and the reassignment's (nf-1)/(fmax-fmin). The block therefore takes no rate at all and works in bins: row m is the normalized frequency m/L, or m*Fs/L in hertz for a signal sampled at Fs.

  • The window derivative is a NOT-A-KNOT CUBIC SPLINE derivative at the knots (MATLAB's dtwin

differentiates spline(1:L, w)), not a finite difference; reproduced to 2.0e-15 relative.

  • The whole 9x24 complex matrix is reproduced to 1.96e-14 on a scale of 10.68 with

kaiser(16,10) and to 2.07e-14 on 12.30 with hann(16).

  • ifsst of the FULL band returns the signal: ifsst(fsst(x,1,w), w) equals x to 8.9e-16. Its

scale is 1/(w[L/2-1]*L) and every row but DC and Nyquist is counted twice; a band reconstruction matches MATLAB to 4.4e-16, with the band edges snapped to the NEAREST row.

Support::None: fsst is a Signal Processing Toolbox FUNCTION and that toolbox ships no Simulink library, so there is no counterpart to bridge to or run a parity testbench against.

Sample results#

Fourier Synchrosqueezed Transform — Step: 0 -> 1 at t = 1 sFourier Synchrosqueezed Transform — Step: 0 -> 1 at t = 1 s051015012345t (s)in ICoreDouble-Out-0out ICoreDouble-Out-0 [9x1] entry 0out ICoreDouble-Out-1 [9x1] entry 0out ICoreDouble-Out-2 [9x1] entry 0

The same rig also ran:

StimulusWhat it isOutput range
impulseImpulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)0 … 0.5818
rampRamp: slope 1 from t = 00 … 81.46
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias0 … 15.35
tableRepeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample0 … 3.868

Plotted: step — Step: 0 -> 1 at t = 1 s

Category dynamic · sample time 0.1 · 60 steps · commit 875fdbf564cf31a145283edf6a75dc1d64d1090a · produced by docsSample --out <folder> --blocks Wigner_Ville_Distribution Cross_Wigner_Ville_Distribution Inverse_STFT Fourier_Synchrosqueezed_Transform Time_Frequency_Ridges Frequency_Domain_Filter_Identification Fill_Gaps EOM_6DOF_ECEF_Quaternion EOM_6DOF_Custom_Variable_Mass_ECEF_Quaternion EOM_6DOF_Simple_Variable_Mass_ECEF_Quaternion ECI_To_ECEF_Rotation_Matrix ECI_Position_To_LLA LLA_To_ECI_Position ECI_Position_To_AER --steps 60 · data docs/generated/samples/Control_Systems__Time_Frequency__Fourier_Synchrosqueezed_Transform.json · the SVG is generated from those numbers by tools/docs/plot_svg.py, so it is a run and not a drawing (R-D10).