block TimeTable "Generate a (possibly discontinuous) signal by linear interpolation in a table"
parameter Real table[:,2] = fill(0, 0, 2) "Table matrix (time = first column; e.g., table=[0, 0; 1, 1; 2, 4])"
annotation (Dialog(groupImage = "modelica://Modelica/Resources/Images/Blocks/Sources/TimeTable.png"));
parameter Modelica.SIunits.Time timeScale(min = Modelica.Constants.eps) = 1 "Time scale of first table column"
annotation (Evaluate = true);
extends Interfaces.SignalSource;
parameter Modelica.SIunits.Time shiftTime = startTime "Shift time of first table column";
protected
Real a "Interpolation coefficient a of actual interval (y=a*x+b)";
Real b "Interpolation coefficient b of actual interval (y=a*x+b)";
Integer last(start = 1) "Last used lower grid index";
discrete SIunits.Time nextEvent(start = 0, fixed = true) "Next event instant";
discrete Real nextEventScaled(start = 0, fixed = true) "Next scaled event instant";
Real timeScaled "Scaled time";
function getInterpolationCoefficients "Determine interpolation coefficients and next time event"
extends Modelica.Icons.Function;
input Real table[:,2] "Table for interpolation";
input Real offset "y-offset";
input Real startTimeScaled "Scaled time-offset";
input Real timeScaled "Actual scaled time instant";
input Integer last "Last used lower grid index";
input Real TimeEps "Relative epsilon to check for identical time instants";
input Real shiftTimeScaled "Time shift";
output Real a "Interpolation coefficient a (y=a*x + b)";
output Real b "Interpolation coefficient b (y=a*x + b)";
output Real nextEventScaled "Next scaled event instant";
output Integer next "New lower grid index";
protected
Integer columns = 2 "Column to be interpolated";
Integer ncol = 2 "Number of columns to be interpolated";
Integer nrow = size(table, 1) "Number of table rows";
Integer next0;
Real tp;
Real dt;
algorithm
next := last;
nextEventScaled := timeScaled - TimeEps * abs(timeScaled);
tp := timeScaled + TimeEps * abs(timeScaled);
if tp < startTimeScaled then
nextEventScaled := startTimeScaled;
a := 0;
b := offset;
elseif nrow < 2 then
a := 0;
b := offset + table[1,columns];
else
tp := tp - shiftTimeScaled;
while next < nrow and table[next,1] <= tp loop
next := next + 1;
end while;
if next < nrow then
nextEventScaled := shiftTimeScaled + table[next,1];
end if;
if next == 1 then
next := 2;
end if;
next0 := next - 1;
dt := table[next,1] - table[next0,1];
if dt <= TimeEps * abs(table[next,1]) then
a := 0;
b := offset + table[next,columns];
else
a := (table[next,columns] - table[next0,columns]) / dt;
b := offset + table[next0,columns] - a * table[next0,1];
end if;
end if;
b := b - a * shiftTimeScaled;
end getInterpolationCoefficients;
algorithm
if noEvent(1 < size(table, 1)) then
assert(not (0 < table[1,1] or table[1,1] < 0), "The first point in time has to be set to 0, but is table[1,1] = " + String(table[1,1]));
end if;
when {pre(nextEvent) <= time, initial()} then
(a,b,nextEventScaled,last) := getInterpolationCoefficients(table, offset, startTime / timeScale, timeScaled, last, 100 * Modelica.Constants.eps, shiftTime / timeScale);
nextEvent := nextEventScaled * timeScale;
end when;
equation
assert(0 < size(table, 1), "No table values defined.");
y = a * timeScaled + b;
timeScaled = time / timeScale;
annotation (
Icon(
coordinateSystem(
preserveAspectRatio = true,
extent = {
{-100, -100},
{100, 100}}),
graphics = {
Line(
points = {
{-80, 68},
{-80, -80}},
color = {192, 192, 192}),
Polygon(
points = {
{-80, 90},
{-88, 68},
{-72, 68},
{-80, 90}},
lineColor = {192, 192, 192},
fillColor = {192, 192, 192},
fillPattern = FillPattern.Solid),
Line(
points = {
{-90, -70},
{82, -70}},
color = {192, 192, 192}),
Polygon(
points = {
{90, -70},
{68, -62},
{68, -78},
{90, -70}},
lineColor = {192, 192, 192},
fillColor = {192, 192, 192},
fillPattern = FillPattern.Solid),
Rectangle(
extent = {
{-48, 70},
{2, -50}},
lineColor = {255, 255, 255},
fillColor = {192, 192, 192},
fillPattern = FillPattern.Solid),
Line(points = {
{-48, -50},
{-48, 70},
{52, 70},
{52, -50},
{-48, -50},
{-48, -20},
{52, -20},
{52, 10},
{-48, 10},
{-48, 40},
{52, 40},
{52, 70},
{2, 70},
{2, -51}}),
Text(
extent = {
{-150, -150},
{150, -110}},
textString = "offset=%offset")}),
Diagram(
coordinateSystem(
preserveAspectRatio = true,
extent = {
{-100, -100},
{100, 100}}),
graphics = {
Polygon(
points = {
{-80, 90},
{-85, 68},
{-74, 68},
{-80, 90}},
lineColor = {95, 95, 95},
fillColor = {95, 95, 95},
fillPattern = FillPattern.Solid),
Line(
points = {
{-80, 68},
{-80, -80}},
color = {95, 95, 95}),
Line(
points = {
{-90, -70},
{82, -70}},
color = {95, 95, 95}),
Polygon(
points = {
{88, -70},
{68, -65},
{68, -74},
{88, -70}},
lineColor = {95, 95, 95},
fillColor = {95, 95, 95},
fillPattern = FillPattern.Solid),
Rectangle(
extent = {
{-20, 90},
{30, -30}},
lineColor = {255, 255, 255},
fillColor = {192, 192, 192},
fillPattern = FillPattern.Solid),
Line(points = {
{-20, -30},
{-20, 90},
{80, 90},
{80, -30},
{-20, -30},
{-20, 0},
{80, 0},
{80, 30},
{-20, 30},
{-20, 60},
{80, 60},
{80, 90},
{30, 90},
{30, -31}}),
Text(
extent = {
{-70, -42},
{-32, -54}},
textString = "offset"),
Polygon(
points = {
{-31, -30},
{-33, -40},
{-28, -40},
{-31, -30}},
lineColor = {95, 95, 95},
fillColor = {95, 95, 95},
fillPattern = FillPattern.Solid),
Polygon(
points = {
{-31, -70},
{-34, -60},
{-29, -60},
{-31, -70},
{-31, -70}},
lineColor = {95, 95, 95},
fillColor = {95, 95, 95},
fillPattern = FillPattern.Solid),
Line(
points = {
{-31, -32},
{-31, -70}},
color = {95, 95, 95}),
Line(
points = {
{-20, -30},
{-20, -70}},
color = {95, 95, 95}),
Text(
extent = {
{-38, -73},
{8, -83}},
textString = "startTime"),
Line(
points = {
{-20, -30},
{-80, -30}},
color = {95, 95, 95}),
Text(
extent = {
{-76, 93},
{-44, 75}},
textString = "y"),
Text(
extent = {
{66, -78},
{90, -88}},
textString = "time"),
Text(
extent = {
{-15, 83},
{24, 68}},
textString = "time"),
Text(
extent = {
{33, 83},
{76, 67}},
textString = "y")}),
Documentation(
info = "<html>\n<p>\nThis block generates an output signal by <strong>linear interpolation</strong> in\na table. The time points and function values are stored in a matrix\n<strong>table[i,j]</strong>, where the first column table[:,1] contains the\ntime points and the second column contains the data to be interpolated.\nThe table interpolation has the following properties:\n</p>\n<ul>\n<li>The interpolation interval is found by a linear search where the interval used in the\n last call is used as start interval.</li>\n<li>The time points need to be <strong>monotonically increasing</strong>.</li>\n<li><strong>Discontinuities</strong> are allowed, by providing the same\n time point twice in the table.</li>\n<li>Values <strong>outside</strong> of the table range, are computed by\n <strong>extrapolation</strong> through the last or first two points of the\n table.</li>\n<li>If the table has only <strong>one row</strong>, no interpolation is performed and\n the function value is just returned independently of the actual time instant.</li>\n<li>Via parameters <strong>shiftTime</strong> and <strong>offset</strong> the curve defined\n by the table can be shifted both in time and in the ordinate value.\n The time instants stored in the table are therefore <strong>relative</strong>\n to <strong>shiftTime</strong>.</li>\n<li>If time < startTime, no interpolation is performed and the offset\n is used as ordinate value for the output.</li>\n<li>If the table has more than one row, the first point in time <strong>always</strong> has to be set to <strong>0</strong>, e.g.,\n <strong>table=[1,1;2,2]</strong> is <strong>illegal</strong>. If you want to\n shift the time table in time use the <strong>shiftTime</strong> parameter instead.</li>\n<li>The table is implemented in a numerically sound way by\n generating <strong>time events</strong> at interval boundaries.\n This generates continuously differentiable values for the integrator.</li>\n<li>Via parameter <strong>timeScale</strong> the first column of the table array can\n be scaled, e.g., if the table array is given in hours (instead of seconds)\n <strong>timeScale</strong> shall be set to 3600.</li>\n</ul>\n<p>\nExample:\n</p>\n<pre>\n table = [0, 0;\n 1, 0;\n 1, 1;\n 2, 4;\n 3, 9;\n 4, 16];\nIf, e.g., time = 1.0, the output y = 0.0 (before event), 1.0 (after event)\n e.g., time = 1.5, the output y = 2.5,\n e.g., time = 2.0, the output y = 4.0,\n e.g., time = 5.0, the output y = 23.0 (i.e., extrapolation).\n</pre>\n\n<p>\n<img src=\"modelica://Modelica/Resources/Images/Blocks/Sources/TimeTable.png\"\n alt=\"TimeTable.png\">\n</p>\n\n</html>",
revisions = "<html>\n<h4>Release Notes</h4>\n<ul>\n<li><em>Oct. 21, 2002</em>\n by Christian Schweiger:<br>\n Corrected interface from\n<pre>\n parameter Real table[:, :]=[0, 0; 1, 1; 2, 4];\n</pre>\n to\n<pre>\n parameter Real table[:, <strong>2</strong>]=[0, 0; 1, 1; 2, 4];\n</pre>\n </li>\n<li><em>Nov. 7, 1999</em>\n by <a href=\"http://www.robotic.dlr.de/Martin.Otter/\">Martin Otter</a>:<br>\n Realized.</li>\n</ul>\n</html>"));
end TimeTable;