View Raw SPL
/*****************************************************************************
* *
* LEVELDURATION.SPL Copyright (C) 2023 DSP Development Corporation *
* All Rights Reserved *
* *
* Author: Randy Race *
* *
* Synopsis: Returns XY series where a level is crossed for a duration *
* *
* Revisions: 7 Jun 2023 RRR Creation *
* *
*****************************************************************************/
#include
#if @HELP_LEVELDURATION
LEVELDURATION
Purpose: Returns an XY series of the locations where a series crosses
a level and remains above the level for a specified duration.
Syntax: LEVELDURATION(series, level, duration, edgeout, tol)
(r, f) = LEVELDURATION(series, level, duration, edgeout, tol)
series - A series, the input series.
level - Optional. A real, the rising and falling
crossing threshold. Defaults to 0.0.
duration - Optional. A real, the time duration required
between crossings.
edgeout - Optional. An integer, the output value alignment.
The output value will be placed to the left or right
of the actual crossing point as specified below:
0: Left if input edge rising, right if falling
(default).
1: Right on rising, left on falling.
2: Right whether rising or falling.
3: Left whether rising or falling.
4: Linearly interpolate the X crossing values if
necessary.
tol - Optional. A real, the level crossing tolerance.
Defaults to EPS.
Returns: An XY series where the X values are the rising edges of where
the series remains above LEVEL for DURATION. The Y values are
the same as LEVEL.
(r, f) = LEVELDURATION(series, level, duration, edgeout, tol)
returns two XY series, the rising (starting) edges and the falling
(ending) edges.
Example:
W1: gsin(10000, 1/1000, 0.3)
W2: levelduration(w1)
W3: W1;overp(w2,lred);setplotstyle(1,2);setsym(8, 2)
W1 contains the source series.
W2 returns a 3 point XY series of the rising edges
where W1 crosses 0.0 and remains above 0.0 for 1 second.
W3 displays the original series with the rising edges
overplotted as red up arrows.
Example:
W1: gsin(10000, 1/1000, 0.3)
W2: levelduration(w1, 0.0, 1.0)
W3: W1;overp(w2,lred);setplotstyle(1,2);setsym(8, 2)
Same as above except the LEVEL and DURATION parameters are
explicit.
Example:
W1: gsin(10000, 1/1000, 0.3)
W2: (r, f) = levelduration(w1,0.5,1.0);r;overplot(f,lgreen);setsym(8, 1);setsym(9,2)
W3: W1;overp(r,lred);overp(f,lgreen);setplotstyle(1,2);setsym(8,2);setplotstyle(1,3);setsym(9,3)
W1 contains the source series.
W2 returns two 3 point XY series where R contains the rising
(beginning) edges and F contains the falling (ending) edges
where W1 crosses 0.5 and remains above 0.5 for 1 second.
W3 displays the original series with the rising edges
overplotted as red up arrows and the falling edges are
overplotted as green down arrows.
Example:
W1: gsweep(10000, 1/10000, 1, 10.3)
W2: (r, f) = levelduration(w1,0.5,0.04);r;overplot(f,lgreen);setsym(8, 1);setsym(9,2)
W3: W1;overp(r,lred);overp(f,lgreen);setplotstyle(1,2);setsym(8,2);setplotstyle(1,3);setsym(9,3)
W1 contains a swept sinewave from 1 Hz to 10.3 Hz.
W2 returns two 3 point XY series where R contains the rising
(beginning) edges and F contains the falling (ending) edges
where W1 crosses 0.5 and remains above 0.5 for 0.04 seconds.
W3 displays the original series with the rising edges
overplotted as red up arrows and the falling edges are
overplotted as green down arrows. The plot shows 4 intervals
meet the condition of crossing 0.5 for at least 0.04 seconds.
Note the resulting rising edges are to the left of the actual
crossing point and the falling edges are to the right of the
crossing point.
Example:
W1: gsweep(10000, 1/10000, 1.0, 10.3)
W2: (r, f) = levelduration(w1,0.5,0.04,4);r;overplot(f,lgreen);setsym(8, 1);setsym(9,2)
W3: W1;overp(r,lred);overp(f,lgreen);setplotstyle(1,2);setsym(8,2);setplotstyle(1,3);setsym(9,3)
Same as above except the rising and crossing edges are linearly
interpolated to the actual crossing location.
Example:
W1: gsweep(10000, 1/10000, 1.0, 10.3)
W2: (r, f) = levelduration(w1,0.5,0.04,4);r;overplot(f,lgreen);setsym(8, 1);setsym(9,2)
W3: W1;overp(r,lred);overp(f,lgreen);setplotstyle(1,2);setsym(8,2);setplotstyle(1,3);setsym(9,3)
W4: integ(gnorm(1000, 1/1000))
W5: xy(xvals(r), xylookup(W4, xvals(r)));points;setsym(8)
W6: xy(xvals(f), xylookup(W4, xvals(f)));points;setsym(9)
w7: W4;overp(w5, lred);overp(w6, lgreen)
Same as above except the rising and crossing edges are used to
mark the same locations of an associated series in W4.
W7 shows the rising and falling edges overplotted on the series
in W4 showing the four intervals of W1 that cross 0.5 for
at least 0.04 seconds. Note the source series in W1 and the
associated series in W4 have different sample rates.
Remarks:
LEVELDURATION returns an XY series where the input crosses
LEVEL and remains above LEVEL for DURATION. The Y values of
the returned XY series are set to LEVEL.
The EDGEOUT parameter determines the precise location of
the level crossing. By default, if the input series crosses
LEVEL between samples, the rising edge is the sample to the
left of the crossing and the falling edge is the sample to the
right of the crossing. Set EDGEOUT to 4 to linearly interpolate
the crossing locations. See LEVELCROSS for more details.
TOL is used to handle cases where the series reaches LEVEL
exactly but does not cross it. The default value for TOL
is EPS("double").
See Also:
Levelcross
Xylookup
#endif
/* find locations that cross level and remain above level for duration */
levelduration(s, level, duration, edgeout, tol)
{
local xr, xf, idx, xyxr = {}, xyxf = {};
if (argc < 5)
{
if (argc < 4)
{
if (argc < 3)
{
if (argc < 2)
{
if (argc < 1) error(sprintf("%s - input series required", __FUNC__));
level = 0.0;
}
duration = 1.0;
}
edgeout = 0;
}
tol = eps();
}
if (edgeout == 4)
{
/* rising x locations, interpolated */
xr = xvals(levelcross(s, level + tol, 1, 4));
/* falling x locations, interpolated */
xf = xvals(levelcross(s, level + tol, 2, 4));
}
else
{
/* rising x locations, non-interpolated */
lev = levelcross(s, level + tol, 1, edgeout);
xr = xvals(lev);
xr = xr[find(lev == 1)];
/* falling x locations, non-interpolated */
lev = levelcross(s, level + tol, 2, edgeout);
xf = xvals(lev);
xf = xf[find(lev == 1)];
}
if (length(xf) > 0 && length(xr) > 0)
{
/* force first falling x after first rising x */
if (xf[1] < xr[1])
{
xf = extract(xf, 2, length(xr));
}
/* indices of X that meet or exceed duration */
idx = find((xf - xr) >= duration);
/* rising series */
xyxr = xy(xr[idx], ones(length(idx), 1) * level);
/* falling series */
xyxf = xy(xf[idx], ones(length(idx), 1) * level);
}
return(xyxr, xyxf);
}