Smooth — Control Systems/Signal Smoothing
Control_Systems/Signal_Smoothing/Smooth · 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.
Smooth
Control Systems / Signal Smoothing
Smooths a stream by fitting a local regression to the last Span
samples and emitting its value at the centre of that window. This is
MATLAB's smooth on a stream, and it carries the same three
uniformly-spaced methods: a centred moving average, lowess (a
local straight line) and loess (a local parabola), the last two weighted
so that samples near the centre count for more than samples near the edge.
Writing t for a sample's offset from the centre and h = (Span−1)/2, the weighted pair solve w = row 0 of (V′WV)−1V′W with tri-cube weights W(t) = (1 − (|t|/h)3)3 and a local basis V = [1, t] or [1, t, t2]. Row 0 is the whole answer because the basis at t = 0 is [1, 0, 0]: the fitted value at the centre is the constant term of the local fit.
Ports
- u – the sampled signal being smoothed. Scalar: one channel and its own window – see Notes.
- y – the smoothed value, scalar, lagging the input by (Span−1)/2 samples. Its size is the input's and does not depend on any setting.
Parameters
- Smoothing Method – which local fit is made. Three values, named
as MATLAB names them:
- moving – the unweighted average of all Span samples. The
same numbers as Machine Learning / Feature Engineering / Rolling
Statistics in its Mean mode, shifted by (Span−1)/2
samples; reach for that block when a trailing mean with no lag is what is
wanted, and for this one when the answer has to line up with
smooth. - lowess – a tri-cube-weighted straight line. The default, as it is MATLAB's. It follows a ramp exactly where a moving average of the same length lags it.
- loess – a tri-cube-weighted parabola. It preserves a peak's height where lowess flattens it, at the price of rejecting less noise.
- moving – the unweighted average of all Span samples. The
same numbers as Machine Learning / Feature Engineering / Rolling
Statistics in its Mean mode, shifted by (Span−1)/2
samples; reach for that block when a trailing mean with no lag is what is
wanted, and for this one when the answer has to line up with
- Span – how many samples the fit sees. A whole number from 1 to
101. An even value is silently rounded up to the next odd one, exactly as
smoothdoes, because a centred window has to have a centre. Bounded above because the dot product is unrolled at export. - 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 weight row is structural and is inlined into the arithmetic at export time rather than exposed as a tunable parameter: it follows from both settings, and changing either 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 one fixed multiply-accumulate, with the products accumulated at full width and shifted back once at the end rather than per term. No fit is solved in hardware – it happened at export time.
Simulink bridge
None (Support::None). smooth is a Curve
Fitting Toolbox function, and that toolbox ships no Simulink library at
all, 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. No configuration of it crosses either, including "Sampling Time (s)",
which has no counterpart to be written to.
Notes
- Stateful, and discrete by nature
(
setDiscreteOnlyBlock(true)): the window advances once per sample. - One solve, then arithmetic. A fit evaluated at one point is a linear function of the window, so the small weighted system is solved once when the configuration loads and every sample afterwards is a single dot product. Nothing inverts a matrix per sample, here or in any exported core.
- The output lags (Span−1)/2 samples, because the centre of the
last Span samples is that far in the past and a stream cannot see the
future half of a centred window. Savitzky-Golay Filter offers an endpoint
evaluation that avoids the lag;
smoothhas no such option, so neither does this block. - The two extreme samples of the span carry weight exactly zero under lowess and loess, because the tri-cube weight is normalised by h and vanishes at ±h. A span of 7 therefore fits five points. This is MATLAB's behaviour, not an approximation of it, and it is why an effective smoothing length is two shorter than the span looks. Under moving all Span samples count.
- Verified against MATLAB. Over spans 5, 7, 9 and 11 and all three
methods, the row derived here reproduces
smooth's interior impulse response to 4.4e−16 and its interior output on a 20-sample record to 1.3e−15, in R2026a. - The window is zero-prefilled, and the zeros count. The first Span−1 outputs of a run are a startup transient in which the fit sees samples that were never measured. MATLAB has no such phase because it is handed the whole vector at once and shrinks its window at the ends; a stream cannot. The convention matches Moving Median, Detrend, Hampel Filter and Savitzky-Golay Filter.
- Three of MATLAB's six methods are deliberately absent.
sgolayis the same local polynomial with the weights left at 1, and is Savitzky-Golay Filter in this family.rlowessandrloessadd a bisquare reweighting computed from the fit's own residuals, so their weights are a function of the data rather than of the configuration – there is no row to inline, and every generated core would have to carry five iterations of a weighted solve per sample. - Scalar only. One channel and its own history; wire one block per channel. A shared window would mix them.
- Smoothing is not filtering the noise away. A short span follows the input closely and rejects little; a long one rejects more and lags more, and under lowess it also flattens any feature narrower than the span.
- No state space. The block is linear in its input, but its output depends on Span past samples through a fixed row rather than through an A/B/C/D pair, so it carries none and model reduction correctly declines to merge it.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Signal_Smoothing/Smooth |
| family | Control_Systems/Signal_Smoothing |
| solver environment class | ICoreBlock_0_Control_Systems_1_Signal_Smoothing_2_Smooth |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Signal_Smoothing/Smooth/ICoreBlock_0_Control_Systems_1_Signal_Smoothing_2_Smooth.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Signal_Smoothing/Smooth/ICoreBlock_0_Control_Systems_1_Signal_Smoothing_2_Smooth.h |
| default size on canvas | 130 × 72 px |
| ports at insert | 1 in, 1 out |
| code generators implemented | Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text |
Ports#
| # | Direction | Signal type | Description label |
|---|---|---|---|
| 1 | in | ICoreDouble | u |
| 2 | out | ICoreDouble | y |
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 variable | Default | Simulink parameter |
|---|---|---|
Smoothing Method | moving%~%lowess%~%loess~~lowess | — |
Span | 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.
Simulink bridge#
| support | Support::None |
| Simulink path | — |
| port-count rule | PortsParam::None |
SampleTime parameter | yes |
Caveat (shown to the user): smoothing a running window by local regression is a Curve Fitting Toolbox FUNCTION (smooth), 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).
Smooth -- MATLAB's smooth() on a stream: local regression over the last Span samples Three methods, one shape. Each is a weighted least-squares fit over the window, evaluated at the window's CENTRE, and a fit evaluated at one point is a LINEAR functional of the window -- so the whole thing collapses to one row of numbers:
moving w(k) = 1/Span for every k (a centred moving average) lowess local LINE with tri-cube weights (degree 1) loess local PARABOLA with tri-cube weights (degree 2)
For the weighted pair, writing t for a sample's offset from the centre and halfw = (Span-1)/2,
W(t) = (1 - (|t| / halfw)^3)^3 the tri-cube weight V = [1, t] or [1, t, t^2] the local design matrix w = row 0 of (V'WV)^-1 V'W evaluate the fit at t = 0
and row 0 is the whole answer because the basis at t = 0 is [1, 0, 0]: the fitted value AT the centre is the constant term of the local fit and nothing else.
⚠ THREE DETAILS TAKEN OFF smooth.m's unifloess PATH RATHER THAN OFF THE TEXTBOOK (R2026a):
- The two EXTREME samples of the span carry weight zero and are dropped. dmax is halfw, so
the tri-cube at +/-halfw is exactly 0. A span of 7 fits FIVE points, not seven; only the 'moving' method uses all of them.
- MATLAB's own
weightthere is (1 - (d/dmax)^3)^1.5 -- the SQUARE ROOT of the tri-cube --because it scales the design matrix AND the response and the two meet in a projection (alpha = Q(halfw,:)Q' . weight). Squaring it back gives the tri-cube, which is what is solved with here. Same smoother; the ordinary spelling of it.
- An even Span is silently forced ODD, span = 2*floor(span/2)+1, exactly as MATLAB does.
MEASURED against R2026a, twice and two different ways, rather than transcribed:
- the INTERIOR IMPULSE RESPONSE of smooth(), read off a unit impulse buried in 201 zeros,
matches the row derived here to 4.4e-16 worst over spans 5, 7, 9, 11 x all three methods;
- the INTERIOR OUTPUT of smooth() on the 20-sample record
[1.2 -0.7 2.5 0.3 -1.9 0.8 1.1 -2.2 0.45 1.7 -0.35 0.9 2.1 -1.4 0.25 0.6 -0.8 1.55 0.05 -1.1] matches this row applied to the same window to 1.3e-15 worst over the same grid.
⚠ THE ROW IS STORED REVERSED, wbuf[i] = w[Span-1-i], because the running window is kept NEWEST-FIRST and the row is written in window order. Every one of these rows happens to be symmetric, so the reversal changes no number today; it is written out rather than relied on, since a method added later need not be.
Sample results#
The same rig also ran:
| Stimulus | What it is | Output range |
|---|---|---|
impulse | Impulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair) | 0 … 0.4274 |
ramp | Ramp: slope 1 from t = 0 | 0 … 5.7 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | -0.9886 … 0.9882 |
table | Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample | -1.143 … 2 |
Plotted: step — Step: 0 -> 1 at t = 1 s
Category dynamic · sample time 0.1 · 60 steps · commit 3c100aff6f27235305db4ad4d572f32e342718ad · produced by docsSample --out <folder> --blocks Smooth Exclude_Data --steps 60 · data docs/generated/samples/Control_Systems__Signal_Smoothing__Smooth.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).