Generated reference › Savitzky Golay Filter — Control Systems/Signal Smoothing
kind: generated#block#control-systems-signal-smoothing

Savitzky Golay Filter — Control Systems/Signal Smoothing

Control_Systems/Signal_Smoothing/Savitzky_Golay_Filter · 1 input / 1 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.

Savitzky-Golay Filter

Control Systems / Signal Smoothing

Smooths a stream by fitting a least-squares polynomial of degree p to the last N samples and reporting the value that polynomial takes at one chosen point of the window. Unlike a moving average of the same length it preserves the height and width of a peak, which is what it is usually reached for.

The value of a least-squares fit at a fixed point of its window is a linear function of that window, so the block is one FIR filter: the small normal-equation system is solved once from the parameters, and the coefficient row it produces is applied to a shift register. That row is the same one MATLAB's sgolay(p, N) produces.

Ports

  • u – the noisy signal. Scalar – see Notes.
  • y – the fitted value. Same size as the input, so scalar.

Parameters

  • Frame Length – N, the number of samples the fit sees. An odd whole number from 3 to 101, and it must be greater than Polynomial Order. Odd because the centre of an even window falls between two samples.
  • Polynomial Order – p, the degree fitted. A whole number from 0 to 6, and less than Frame Length. Order 0 makes the block a plain moving average; higher orders follow curvature at the cost of rejecting less noise.
  • Evaluation Point – where in the window the fitted polynomial is read:
    • center – the middle of the window. This is what sgolayfilt does for its interior samples, and it is the default. It delays the signal by (N−1)/2 samples, because the middle of the last N samples is that far in the past.
    • endpoint – the newest sample. No delay, and noisier: an endpoint fit is the least constrained point of a least-squares polynomial.
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text.

The N coefficients are structural and are inlined into the arithmetic at export time rather than exposed as tunable parameters: they follow from all three settings, and changing any of them changes how many multiplies the core contains, which no runtime parameter can do. Re-export after changing them.

The three HDL targets are genuine synthesizable Q16.16: a shift register and a fixed multiply-accumulate, with the products accumulated at full width and shifted back once at the end rather than per term. No solve reaches the hardware – it happened at export time.

Simulink bridge

No equivalent (Support::None). The Signal Processing Toolbox ships no Simulink library at all, and sgolay and sgolayfilt are MATLAB functions. 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.

Notes

  • Stateful, and discrete by nature (setDiscreteOnlyBlock(true)): the window advances once per sample.
  • center delays the signal and endpoint does not. This is the block's one real choice and no setting avoids it: a centred fit needs samples on both sides of the point it reports, and the only samples a stream has on the far side are ones already past. Compare a centred output against the input and it will look shifted – it is, by exactly (N−1)/2 samples.
  • The window is zero-prefilled, and the zeros count. The first N−1 outputs of a run are a startup transient. MATLAB's sgolayfilt switches to the endpoint rows of the same matrix over the first and last (N−1)/2 samples instead; a streaming block cannot do the second of those, so it does neither. The convention matches Moving Median.
  • Verified against MATLAB. At frame 7 and order 2 the coefficient row matches sgolay(2,7)'s middle row to 1.1e−16 and its last row to 6.7e−16 in R2026a.
  • Order 0 is a moving average, exactly: the least-squares constant over a window is its mean.
  • Scalar only. One channel and its own history; wire one block per channel.
  • No state space. Linear in the input, but its output depends on N past samples through a fixed FIR rather than through an A/B/C/D pair seeded here, so it carries none and model reduction correctly declines to merge it.

Code facts#

FactValue
registered typeControl_Systems/Signal_Smoothing/Savitzky_Golay_Filter
familyControl_Systems/Signal_Smoothing
solver environment classICoreBlock_0_Control_Systems_1_Signal_Smoothing_2_Savitzky_Golay_Filter
sourcesrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Signal_Smoothing/Savitzky_Golay_Filter/ICoreBlock_0_Control_Systems_1_Signal_Smoothing_2_Savitzky_Golay_Filter.cpp
headersrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Signal_Smoothing/Savitzky_Golay_Filter/ICoreBlock_0_Control_Systems_1_Signal_Smoothing_2_Savitzky_Golay_Filter.h
default size on canvas134 × 72 px
ports at insert1 in, 1 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoubleu
2outICoreDoubley

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
Frame Length7—
Polynomial Order2—
Evaluation Pointcenter%~%endpoint~~center—

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): no Simulink equivalent: sgolay() and sgolayfilt() are MATLAB functions and the Signal Processing Toolbox ships no Simulink library at all, while nothing in the Simulink standard library or in DSP System Toolbox fits a least-squares polynomial over a sliding window. Reported rather than dropped, and it carries no parity testbench

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).

Savitzky-Golay Filter -- least-squares polynomial smoothing over the last N samples ONE COEFFICIENT ROW, TEN IDENTICAL DOT PRODUCTS. The value a least-squares polynomial takes at a FIXED point of its window is a LINEAR functional of that window, so the fit is never recomputed per sample: a small normal-equation system is solved once when the configuration is read, and the row it produces is inlined by every target as a fixed multiply-accumulate over a shift register. The emitted core solves nothing and branches nowhere.

MEASURED AGAINST MATLAB R2026a rather than asserted: at frame 7 and order 2 the row derived here matches sgolay(2,7)'s middle row to 1.1e-16 (centre) and its last row to 6.7e-16 (endpoint). The probe is recorded in the notes of the toolbox-blocks plan, Family B.

⚠ THE REGRESSION AXIS IS SCALED TO ROUGHLY [-1, 1] BEFORE THE SOLVE, and that is not cosmetic. Written on the raw sample index, the normal-equation matrix for a frame of 101 and an order of 6 carries entries around 1e26 and the solve loses digits to nothing but the choice of units. Scaling cannot change the answer -- the fit is evaluated exactly at the point the axis is centred on, where the polynomial's value is its constant term whatever the axis is worth per sample -- so it is a free improvement.

⚠ THE WINDOW IS ZERO-PREFILLED AND THE ZEROS COUNT, the same convention Moving Median carries (measured against Simulink). The first N-1 outputs of a run are a startup transient. MATLAB's sgolayfilt() instead switches to the endpoint rows of the same matrix over the first and last (N-1)/2 samples, which a streaming block cannot do at the end of a signal it has not seen.

Sample results#

Savitzky Golay Filter — Step: 0 -> 1 at t = 1 sSavitzky Golay Filter — Step: 0 -> 1 at t = 1 s00.51012345t (s)in ICoreDouble-Out-0out ICoreDouble-Out-0

The same rig also ran:

StimulusWhat it isOutput range
impulseImpulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)-0.09524 … 0.3333
rampRamp: slope 1 from t = 0-0.009524 … 5.5
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias-0.9993 … 0.9989
tableRepeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample-1.095 … 1.714

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

Category dynamic · sample time 0.1 · 60 steps · commit 6db3032c0 · produced by docsSample --out <folder> --blocks Detrend Savitzky_Golay_Filter Hampel_Filter Envelope_Detector --steps 60 · data docs/generated/samples/Control_Systems__Signal_Smoothing__Savitzky_Golay_Filter.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).