User manual › Reading results — scopes, charts, stored values, and what a run tells you
kind: manual#manual#results#scope#chart#variables#diagnostics

Reading results#

A run produces signals. This page covers the blocks that show them, the chart window, the values the command window keeps, and what a run's messages mean.

The blocks that show a signal#

Sinks are the blocks that consume a signal rather than producing one. The ones you will reach for most:

BlockWhat it gives you
ScopeA plot against time, filling in as the run advances. The usual way to watch a signal.
Floating ScopeA scope with no input link — it is pointed at a signal instead, which is useful for a quick look without redrawing anything.
DisplayThe current value as a number, rather than a curve.
XY GraphOne signal against another instead of against time.
Signal RecorderSaves what it receives as a time series in the variables space, so the run's numbers can be worked with in the command window afterwards.
To FileWrites the signal to a CSV file on disk, for use elsewhere.
RecordA scope that also keeps what it plots: the traces are drawn live, and the same samples are saved as time series in the variables space.
Stop SimulationEnds the run when its input says so, rather than at the stop time.
TerminatorDeliberately discards a signal, for an output you do not want.

Each of these has its own page under Block catalog — every library block, with a sample of it running.

The chart window#

A scope's plot can be opened as a chart of its own, and the chart can be exported: its Export Chart button writes PNG, JPEG, PDF, SVG or EPS, and from the command window a chart handle's exportDataCSV(path) writes the plotted numbers in CSV form, when what you want is the data rather than the picture.

While a run is going, the chart keeps refitting its view to follow the new samples. After you zoom or pan (the wheel, a left-drag box, a middle-drag, or the zoom buttons), it stops refitting and leaves the view where you put it. New samples are still drawn. Reset Zoom, or a middle-button double click, fits the view to the data again and turns following back on. Starting the next run does the same.

A scope also keeps faded traces of the runs before the current one, so you can see what an edit to the model changed. The run just before is drawn at 25% of its colour, older runs fainter, and all of them under the current run. They are not data: they are not in the legend or the line list, not in the CSV export, and not part of what the view fits. Reset Zoom and the refit while a run is going always frame the current run, whether the old ones are shown or not. The Chart menu in the chart window's title bar sets them:

Menu rowWhat it does
Show Previous RunsA switch that shows or hides the faded traces. Hidden runs are still recorded, so turning this back on shows the runs made meanwhile. On by default.
Previous Runs KeptHow many earlier runs are kept, 1 to 10. 3 by default.
Previous Runs: ClearForgets the runs kept so far.

The same menu's Buffer Size (Sample) field sets how many samples the plot keeps. It takes a positive whole number, applied when you press Return, leave the field or close the menu, and refuses anything else. The axis titles are typed into the X Axis Label and Y Axis Label fields at the top of the X Label and Y Label menus, and the plot follows as you type — the titles on the plot itself cannot be typed into.

X Label, Y Label and Chart work as a menu bar: click one to open it and again to close it, and while one is open, moving the pointer onto another opens that one instead.

Both settings are saved with the scope. The traces themselves are not: a project opens with none. An undo or redo rebuilds the diagram, which clears a scope's plot, and its previous runs go with it.

You can also plot without involving the diagram at all. plot() in the command window opens a chart of its own — it does not touch the block diagram.

plot speaks MATLAB#

plot takes the arguments MATLAB's plot takes, and reads them the same way:

You writeYou get
plot(y)one line per column of y, against the row index 1..n
plot(x, y)y against x
plot(x, y, 'r--o')with a line spec: colour, line style and marker
plot(x1, y1, x2, y2)several pairs on one chart, each with its own spec
plot(..., 'LineWidth', 2)trailing 'Name', value pairs, applied to every line
plot(s)a recorded time series against its own time base

A line spec is one colour (r g b c m y k w), one line style (- -- : -.) and one marker (o s d x + * .), in any order, in one quoted word. A marker with no line style draws the markers alone, which is MATLAB's rule too. The five properties this chart carries are LineWidth, LineStyle, Color, Marker and MarkerSize.

semilogx, semilogy and loglog are the same call with one or both axes on a base-10 logarithmic scale.

A chart handle lets you look at what was drawn — c.path(i) binds line i and .info prints its point count and bounds, which is how you check that a matrix was read the way you meant:

$ ICoreBlocks --console "c = plot([1 2; 3 4; 5 6]); p = c.path(1); p.info"
p: name='', points=3, bounds=[x: 1..3, y: 2..6]

That is the second column — [2 4 6] — against 1..3, and there is a first line beside it. A vector plots the same way:

$ ICoreBlocks --console "c = plot(0:0.5:2, [0 0.5 1 1.5 2].^2, 'r--o'); p = c.path(0); p.info"
p: name='', points=5, bounds=[x: 0..2, y: 0..4]

Three things differ from MATLAB, and each says so when you hit it:

  • Four marker shapes, not twelve: circle, square, diamond and cross (x, + and * all draw the cross; . is a small circle). MATLAB's ^ v > < p h are refused by name rather than rounded to the nearest shape you did not ask for.

    `` $ ICoreBlocks --console "plot([1 2 3], 'Marker', '^')" plot(): Marker must be one of o s d x + * . or 'none' (this chart draws four shapes), got '^' ``

  • MarkerSize is in pixels, where MATLAB's is in points — this chart has no point size to give it.
  • plot([]) is refused rather than opening empty axes. At a prompt a blank window is a puzzle and a sentence is not.

c.holdOn(m) adds another matrix to an existing chart, and it reads a matrix the chart's way — column 0 is x, every later column a line, which is the shape a recorded signal lands in. plot deliberately no longer does; that is the one place the two disagree, and it is why a series still plots against its own time.

The current figure#

plot draws into the current figure, the way MATLAB's does: the first draw opens a window ("Figure 1"), and every draw after it replaces what that window holds unless hold on is set. figure opens the next window and makes it current; close ends the current one (close all ends every one); clf empties it and keeps the window. The verbs that dress a figure act on the current one and take both of MATLAB's spellings — the call, xlabel("t"), and the command syntax, hold on — and each is also a member on a chart handle, c.title("x").

$ ICoreBlocks --console "f = figure, plot([1 2 3]), hold on, plot([3 2 1]), c = gcf, c.path(1), hold off, plot([1 2 3]), c.path(1)"
f = Figure 1
1 line(s) plotted
hold on
1 line(s) plotted onto the current chart
c = Figure 1
line path c[1]
hold off
chart redrawn, 1 line(s) plotted
getLinePathByIndex(): c has no line path at index 1

A hold-off draw resets what MATLAB's newplot resets: the lines, any placed text, the title, the limits and the log scales. A hold-on draw keeps them, and re-applies a limit set by hand:

$ ICoreBlocks --console "plot([1 2 3], [10 20 30]), xlim, xlim([0 5]), hold on, plot([1 2 3], [15 15 15]), xlim, axis tight, axis"
chart created, 1 line(s) plotted
[1, 3]
xlim set to [0, 5]
hold on
1 line(s) plotted onto the current chart
[0, 5]
axis tight
[1, 3, 10, 30]

The furniture, with what each answers:

You writeYou get
xlabel(t) ylabel(t) title(t)the label or title set; a hold-off draw clears the title
xlim([a b]) ylim([a b]) axis([x0 x1 y0 y1])the range set by hand; bare xlim / axis print the current one
xlim auto axis autoback to autoscaling (which pads y by 20%, x not at all)
axis tight xlim tightranges fitted to the data
axis equalone data unit the same number of pixels on both rulers
axis off / axis onrulers hidden or shown
grid on / grid off / grid minorthe grid, which this chart draws by default
legend("a", "b") / legend / legend offlines named in drawing order; unnamed lines are data1, data2, …
text(x, y, str)a string placed at the data point, following zoom and pan
subplot(m, n, p) / subplot(mnp)the window tiled m by n; tile p (row by row) is the current axes
tiledlayout(m, n) / nexttilethe same grid, filled in order
gcf / gcathe current figure / current axes, bindable: a = gca
saveas(gcf, path) exportgraphics(gcf, path) print("-dpng", path)the figure written to a file, png/jpg/bmp/pdf/svg by extension
$ ICoreBlocks --console "subplot(2, 1, 1), bar([1 2 3]), title(\"bars\"), a = subplot(2, 1, 2), stem([1 3 2]), a.path(1), saveas(gcf, \"fig.png\")"
Figure 1 tile 1 of 2 x 1
1 line(s) plotted
title set to 'bars'
a = Figure 1 tile 2 of 2 x 1
2 line(s) plotted
line path a[1]
Figure 1 written to fig.png

Two things differ from MATLAB here, and each says so at the prompt: axis square, axis image and their kin shape the plot box, which on this chart follows its window, so they are refused by name (axis equal and axis tight are the two shapes it has); and legend's 'Location' is refused, because the legend sits where the toolkit places it. annotation is refused too — it places in figure-normalised coordinates this chart does not expose — and text is the form it has.

The model response verbs draw there too#

step, impulse, ramp, pzmap, polezero, rootlocus and the console's four-argument bode/nyquist sweeps address the current figure like every other verb on this page — so hold on adds a response to what the axes already hold, a second call redraws rather than opening a second window, and close, clf, title and xlim all reach them. They opened a window of their own until 2026-09-06, and the difference a reader notices is the plot furniture:

$ ICoreBlocks --console "G = tf([1 2],[1 3 2]); pzmap(G); sgrid; c = gca; p = c.path(0); p.info"
p: name='Poles (2)', points=2, bounds=[x: -2..-1, y: 0..0]

sgrid and zgrid draw constant-damping and constant-frequency loci over whatever the current axes already hold, which is what MATLAB's do. Before the change the map went to a window and the grid to the figure, so the two never met; now pzmap(G); sgrid grids the map, and c.path(2) onward are the loci that were added to it.

The other chart kinds#

bar, barh, stem, stairs, scatter, histogram, area, errorbar and fill take MATLAB's arguments — one data group, a line spec, 'Name', value pairs — and draw into the current figure like plot. This chart has one series kind, the line path, so each of them is MATLAB's geometry drawn as line paths: a bar is its outline, a stem is a stem path plus a marker-only tip path, an area is the closed outline of its band. The numbers are exactly MATLAB's — the same x positions, widths and heights — without the fill, and c.path(i) counts what a call drew:

$ ICoreBlocks --console "c = bar([1 2; 3 4]); p = c.path(1); p.info"
p: name='', points=8, bounds=[x: 1..2.4, y: 0..4]

$ ICoreBlocks --console "c = stem([1 2 3]); p = c.path(0); p.info, p = c.path(1); p.info"
p: name='', points=9, bounds=[x: 1..3, y: 0..3]
p: name='', points=3, bounds=[x: 1..3, y: 1..3]

$ ICoreBlocks --console "c = histogram([1 2 2 3 3 3]); p = c.path(0); p.info"
p: name='', points=8, bounds=[x: 0.5..3.5, y: 0..3]
You writeYou get
bar(y) bar(x, y) bar(y, width) bar(..., 'stacked')one outline per column, grouped side by side (0.8 of the spacing) or stacked
barh(...)the same, sideways
stem(y) stem(x, y)two paths per column: the stems, then the tips
stairs(y) stairs(x, y)one path per column, held flat until the next x
scatter(x, y) scatter(x, y, sz) scatter(x, y, sz, [r g b])markers alone; sz is MATLAB's area in points², scaled so 36 is this chart's 11-pixel circle
histogram(x) histogram(x, nbins) histogram(x, edges)the outline of the bins, by MATLAB's own automatic rule; 'BinWidth', 'BinEdges', 'NumBins', 'Normalization'
area(y) area(x, y)one closed outline per column, the columns stacked as MATLAB stacks them
errorbar(y, err) errorbar(x, y, err) errorbar(x, y, neg, pos)the data line with a whisker at every point
fill(x, y, c)a closed polygon's outline in colour c

scatter draws one size and one colour per call — a path carries one of each — so a size per point (sz as a vector) is refused rather than drawn at some average; and pie is refused by name, this chart having neither the rulers nor the wedges for one (bar(y) draws the same numbers as bars). area() with no argument, or with a subsystem, is still the canvas decoration of that name: the two grammars do not overlap, and the argument decides.

Values the command window keeps#

The command window has a variables space: results you name are kept there for the rest of the session, so a value can be computed once and reused.

A recorded signal is a time series — time stamps against one column per channel. It deliberately prints as a summary rather than as its samples, because a run of any length would otherwise fill the window:

$ ICoreBlocks --console "G = tf([1],[1 1]); y = step(G, 5, 6); y"
y = Time Series (6 samples, t = 0 .. 5)  # Time Series

To get at the numbers, ask for the two halves — .time() and .values():

$ ICoreBlocks --console "G = tf([1],[1 1]); y = step(G, 5, 6); y.time()"
[[0], [1], [2], [3], [4], [5]]  # Matrix of Double

$ ICoreBlocks --console "G = tf([1],[1 1]); y = step(G, 5, 6); y.values()"
[[0], [0.632121], [0.864665], [0.950213], [0.981684], [0.993262]]  # Matrix of Double

Those values are 1 − e⁻ᵗ at each second, which is the step response of 1/(s+1) — a result you can check by hand, and a good way to confirm the application is doing what you think before trusting it on a model you cannot check.

Asking for something a value does not have tells you what it does have:

$ ICoreBlocks --console "G = tf([1],[1 1]); y = step(G, 5, 6); y.toMatrix()"
error: a time series has no member 'toMatrix' (try .time() or .values())

clearVariablesSpace empties the variables space when you want to start clean.

What a run's messages mean#

The diagnostics panel collects what a run reports. Two messages are worth recognising:

  • "Unable to retrieve config …" — a block asked for one of its own parameters and did not get it. In practice this means a parameter the block needs was never set, and the run stops rather than continuing on a value nobody chose. Open that block's configuration dialog and check the parameter named in the message.
  • A run that ends immediately — most often a Stop Simulation block whose input was already nonzero at the first step, or a stop time that is not later than the start time. Note that any nonzero entry fires that block, so a vector signal only one of whose elements is nonzero will still stop the run. Both causes are visible in the model configuration panel and on the diagram.

If a result looks wrong rather than absent, the first thing to check is sample rate rather than the block itself — Running a simulation — solver settings, sampling, starting and stopping covers why a signal sampled more slowly than it changes produces a response that looks stepped or delayed.

Real runs#

Every transcript on this page is a real run rather than an illustration. Binary ICoreBlocks.app, built 2026-09-25 23:58 (source at approximately commit 1dac24c5), re-run 2026-09-26; one process per line, HOME=<scratch> ICoreBlocks --console "<line>", with the startup lines removed and paths under that scratch home shortened to …. Re-run any line above to check this page against the program.