Generated reference › Cubic Spline — Control Systems/Curve Fitting
kind: generated#block#control-systems-curve-fitting

Cubic Spline — Control Systems/Curve Fitting

Control_Systems/Curve_Fitting/Cubic_Spline · 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.

Cubic Spline

Control Systems / Curve Fitting

Evaluates a piecewise cubic at its input: the spline is built once, from the parameters, and each sample costs finding the interval the input falls in and evaluating that interval's cubic y = a·t³ + b·t² + c·t + d in the local coordinate t = u − xₖ. The three ways of building it are the Curve Fitting Toolbox's: through the data (csapi/csape), near the data (csaps), or taken straight from a piecewise coefficient matrix you already have (a ppform, as fnval reads one).

Ports

  • u – the abscissa the spline is read at, of any size [m,n]. The spline is applied to each entry independently; this is one curve, not m×n of them.
  • y – the spline's value there, of the same size [m,n]. The block never reshapes a signal.

Parameters

  • Breakpoints – the abscissae x, a row or column vector of 2 to 64 strictly increasing values. A repeated breakpoint is a zero-width interval and a decreasing one describes no axis at all; both are refused with a message rather than divided by.
  • Data Values – the ordinates y, a vector of the same length. Read in the interpolating and smoothing types and ignored in ppform, where the pieces are given directly.
  • Spline Type – where the cubics come from:
    • interpolating – the spline passes exactly through every data point. This is csapi/csape, and the default.
    • smoothing – the spline balances closeness to the data against its own curvature, controlled by Smoothing Parameter. This is csaps, and it always carries natural end conditions, so End Conditions is ignored.
    • ppform – no fitting at all: Piecewise Coefficients is the table of cubics and the block only evaluates it, which is what fnval does with a ppform. Data Values, End Conditions, End Slopes and Smoothing Parameter are all ignored.
  • End Conditions – the two conditions an interpolating cubic spline needs beyond the data, used by the interpolating type only:
    • not-a-knot – the third derivative is continuous across the second and second-to-last breakpoints, so the first two pieces are one cubic and the last two are another. This is what MATLAB's spline and csapi do, and the default here. It needs at least 4 breakpoints.
    • natural – the second derivative is zero at both ends (csape(…, 'variational')). This is the one Simulink's lookup tables call "Cubic spline" – see Notes.
    • clamped – the first derivative takes the values given by End Slopes at the two ends (csape(…, 'complete', …)).
  • End Slopes – the two derivatives [d₀ dₙ] the clamped end condition imposes, at the first and last breakpoint. Ignored by the other two end conditions.
  • Smoothing Parameter – p in (0, 1], the csaps parameter: the fit minimises p·Σ(yₖ−f(xₖ))² + (1−p)·∫f″². p = 1 interpolates (and is then exactly the natural interpolating spline); a smaller p follows the data less and bends less; p near zero approaches the straight-line least-squares fit. MATLAB picks a default p from the data when you do not give one and this block does not reproduce that choice – it asks. Used by the smoothing type only.
  • Piecewise Coefficients – the cubics themselves for the ppform type: one row per interval (so one fewer than there are breakpoints) and four columns in descending powers of the local coordinate, [a b c d] for a·t³ + b·t² + c·t + d with t measured from that row's own breakpoint. This is exactly the layout of a MATLAB ppform's coefs field, so a spline built in MATLAB can be pasted in unchanged. Ignored by the other two types.
  • Extrapolation – what the block answers outside the breakpoints:
    • spline – the first and last cubics are continued beyond the ends, which is what fnval and ppval do. Cubics grow fast: a little way outside the data this is a large number, and that is the honest answer rather than a wrong one.
    • clip – the value at the nearest end breakpoint is held, which is what a Simulink lookup table does under ExtrapMethod "Clip".
  • 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 spline is structural and is inlined into the generated arithmetic rather than exposed as a tunable parameter: the number of pieces decides how many branches the emitted core contains, which no runtime parameter can change. Re-export after changing any parameter. No target solves anything – the linear system was solved at export time, and what is emitted is a chain of comparisons and one nested multiply-add per branch.

The three HDL targets are genuine synthesizable Q16.16, which is possible only because each piece is evaluated in its own local coordinate t = u − xₖ: t stays inside one interval, so t³ stays small, where the same curve written as a polynomial in u would cube the whole abscissa and overflow the format on ordinary data.

Simulink bridge

No equivalent (Support::None). The Curve Fitting Toolbox ships no Simulink library at all: csapi, csape, csaps and fnval are MATLAB functions. Simulink's nearest block is the 1-D Lookup Table in its "Cubic spline" interpolation mode, and that is a different curve – measured, it is the natural spline, not MATLAB's not-a-knot one – while its parameter set has nothing to carry an end condition, a smoothing parameter or a ppform. That block already has an ICore counterpart of its own, 1-D Lookup Table, so mapping this one onto the same library path would also make an import ambiguous. This block therefore has no parity testbench; code export verification still covers it across all ten languages.

Notes

  • Algebraic, with no state. The output depends only on the current input, so the same input twice gives the same output twice.
  • Measured against R2026a, not asserted. On x = [0 0.7 1.9 2.4 4.1 5] with y = [1.3 -0.4 2.1 0.5 -1.2 0.9] the coefficients built here match csapi to 7.1e−15, csape's natural and clamped forms to 1.8e−15, and csaps at p = 0.6 and p = 0.95 to 7.2e−16 and 6.7e−16.
  • Simulink's "Cubic spline" and MATLAB's are different splines. A 1-D Lookup Table set to "Cubic spline" over that same data reproduces the natural spline here to the six digits the probe printed, and differs from csapi by up to 0.47 at points inside the table. If you are reproducing a Simulink model, choose natural and clip; if you are reproducing a MATLAB script, keep not-a-knot and spline.
  • not-a-knot needs four breakpoints, because the condition it imposes is stated at the second and the second-to-last of them. With two or three, use natural or clamped, which are well posed there.
  • p = 1 is the natural interpolating spline, exactly – the smoothing form is written so the two constructions meet rather than merely resemble each other, which is what makes the parameter readable as "how much smoothing" instead of "which algorithm".
  • Only the piecewise-polynomial form is read. The B-form MATLAB's spapi and spap2 produce is a different representation and is not accepted here; convert it with fn2fm(sp, 'pp') first.
  • No state space. A piecewise cubic is not linear in the input, so the block carries none and model reduction correctly declines to merge it.

Code facts#

FactValue
registered typeControl_Systems/Curve_Fitting/Cubic_Spline
familyControl_Systems/Curve_Fitting
solver environment classICoreBlock_0_Control_Systems_1_Curve_Fitting_2_Cubic_Spline
sourcesrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Curve_Fitting/Cubic_Spline/ICoreBlock_0_Control_Systems_1_Curve_Fitting_2_Cubic_Spline.cpp
headersrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Curve_Fitting/Cubic_Spline/ICoreBlock_0_Control_Systems_1_Curve_Fitting_2_Cubic_Spline.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
Breakpoints[0 1 2 3 4]—
Data Values[0 1 0.5 1.5 1]—
Spline Typeinterpolating%~%smoothing%~%ppform~~interpolating—
End Conditionsnot-a-knot%~%natural%~%clamped~~not-a-knot—
End Slopes[0 0]—
Smoothing Parameter0.9—
Piecewise Coefficients[0 0 1 0; 0 0 1 1; 0 0 1 2; 0 0 1 3]—
Extrapolationspline%~%clip~~spline—

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: csapi, csape, csaps and fnval are MATLAB functions and the Curve Fitting Toolbox ships no Simulink library at all. The nearest Simulink block is the 1-D Lookup Table in its "Cubic spline" mode, which was MEASURED against R2026a and computes a DIFFERENT curve - the natural spline rather than MATLAB's not-a-knot one, differing by up to 0.47 inside the table - and whose parameters carry no end condition, no smoothing parameter and no ppform. That library path already belongs to ICore's own 1-D Lookup Table, so a second entry on it would also make an import ambiguous. 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).

Cubic Spline -- csapi / csape / csaps / fnval as one block THE SOLVE HAPPENS ONCE, WHEN THE CONFIGURATION IS READ. A cubic spline is a table of cubics, one per interval, and which cubics they are follows from the breakpoints and the data alone. So the small linear system is solved at configuration load and every one of the ten targets inlines the resulting numbers: the emitted core compares, subtracts and multiplies, and solves nothing. That is what lets a construction as heavy as a smoothing spline reach a PLC and three hardware description languages at all.

⚠ EVERY CONSTRUCTION IS MEASURED AGAINST R2026a. On x = [0 0.7 1.9 2.4 4.1 5] with y = [1.3 -0.4 2.1 0.5 -1.2 0.9] -- irregular spacing, asymmetric data -- the coefficients derived here match, entry for entry:

csapi(x, y) (not-a-knot) 7.1e-15 csape(x, y, 'variational') (natural) 1.8e-15 csape(x, y, 'complete', d) (clamped) 1.8e-15 csaps(x, y, 0.6) 7.2e-16 csaps(x, y, 0.95) 6.7e-16

Measured by compiling the block's OWN text for the solve outside the app and diffing the coefficients it produces against those five MATLAB calls, so the figures are this file's and not a prototype's.

The smoothing form is the Reinsch one: with rho = (1-p)/p, solve (R + rho*Q'Q) m = Q'y for the interior second derivatives and take f = y - rho*Q*m as the values the spline actually passes through. p = 1 gives rho = 0, i.e. the natural INTERPOLATING spline, so the two modes meet rather than merely resemble each other.

⚠ SIMULINK'S "CUBIC SPLINE" IS THE NATURAL ONE AND MATLAB'S IS NOT, WHICH IS WHY THIS BLOCK ASKS. A 1-D Lookup Table with InterpMethod "Cubic spline" over the data above reproduces the NATURAL end conditions (agreeing to the six digits the probe printed) and differs from csapi by up to 0.47 at points INSIDE the table -- not only at the ends, where a reader might expect an end condition to matter, because the end conditions propagate inwards. Two products, one name, two different curves. That measurement is also why "End Conditions" is a first-class parameter here rather than a hardcoded not-a-knot: a user reproducing a MATLAB script wants one and a user reproducing a Simulink model wants the other, and neither can tell from the answer alone which one they got.

HDL IS GENUINE Q16.16, and the local coordinate is what makes that safe. Each piece is evaluated in t = u - x_i, which never leaves the width of one interval, so the cubed term stays small where a polynomial written in u would raise the whole abscissa to the third

Sample results#

Cubic Spline — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sampleCubic Spline — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample-20-100-2-10123inputoutput
tin ICoreDouble-Out-0out ICoreDouble-Out-0
0-2-24.5
0.40.50.9688
0.8-2-24.5
1.20.50.9688
1.6-2-24.5
20.50.9688
2.4-2-24.5
2.80.50.9688
3.2-2-24.5
3.60.50.9688
4-2-24.5
4.40.50.9688
4.8-2-24.5
5.20.50.9688

Every 4th of 60 samples, from the table stimulus.

The same rig also ran:

StimulusWhat it isOutput range
impulseImpulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)0 … 1
rampRamp: slope 1 from t = 0-20.2 … 1.732
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias-7 … 1.066
stepStep: 0 -> 1 at t = 1 s0 … 1

Plotted: table — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample

Category static · sample time 0.1 · 60 steps · commit 3c100aff6f27235305db4ad4d572f32e342718ad · produced by docsSample --out <folder> --blocks Cubic_Spline --steps 60 · data docs/generated/samples/Control_Systems__Curve_Fitting__Cubic_Spline.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).