Mercurial > octave
changeset 26241:2d80a065ce9a
Add functions for calculating value of function handle over windows of data (bug #48774).
author | Juan Pablo Carbajal <ajuanpi+dev@gmail.com> |
---|---|
date | Sat, 15 Dec 2018 07:29:27 -0800 |
parents | a957e0da8613 |
children | 2730917a7979 |
files | scripts/signal/module.mk scripts/signal/movfun.m scripts/signal/movmin.m scripts/signal/movslice.m scripts/signal/private/parse_movargs.m |
diffstat | 5 files changed, 632 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/scripts/signal/module.mk Sat Dec 15 13:44:39 2018 +0100 +++ b/scripts/signal/module.mk Sat Dec 15 07:29:27 2018 -0800 @@ -3,6 +3,7 @@ %reldir%/private %canon_reldir%_PRIVATE_FCN_FILES = \ + %reldir%/private/parse_moveargs.m \ %reldir%/private/rectangle_lw.m \ %reldir%/private/rectangle_sw.m \ %reldir%/private/triangle_lw.m \ @@ -30,6 +31,9 @@ %reldir%/hanning.m \ %reldir%/hurst.m \ %reldir%/ifftshift.m \ + %reldir%/movfun.m \ + %reldir%/movmin.m \ + %reldir%/movslice.m \ %reldir%/periodogram.m \ %reldir%/sinc.m \ %reldir%/sinetone.m \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/signal/movfun.m Sat Dec 15 07:29:27 2018 -0800 @@ -0,0 +1,513 @@ +## Copyright (C) 2018 Juan Pablo Carbajal +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see <http://www.gnu.org/licenses/>. + +## Author: Juan Pablo Carbajal <ajuanpi+dev@gmail.com> +## Created: 2018-08-09 + +## -*- texinfo -*- +## @defun {@var{y} =} movfun (@var{fun}, @var{x}, @var{wlen}) +## @defunx {@var{y} =} movfun (@dots{}, @var{property}, @var{value}) +## Apply function @var{fun} to a moving window on @var{x} +## +## If @var{wlen} is a scalar, the function @var{fun} is applied to a moving +## window of length @var{wlen}. In this case @var{wlen} must be an odd number. +## If @var{wlen} is an array with 2 elements, the function is applied to a +## moving window @code{-wlen(1):wlen(2)} which always includes the middle point. +## +## The function works along the first non-singleton dimension unless the property +## @asis{'dim'} is set. +## +## The input @var{x} is reshaped into a @var{wlen}-by-n matrix and @var{fun} +## is called o this matrix. Therefore @var{fun} must take a single array input +## argument and apply the computation column-wise. +## +## The output of @var{fun} can a 1-dimensional array of length @var{ndim}. +## In this case, the output of @var{fun} when applied to a column vector must +## be a @strong{row} array of length @var{ndim}. +## When applied to a 2-dimensional array with @var{n} columns, there are two +## formats that work: +## @var{fun} can return an @var{n}-by-@var{ndim} array. +## See @code{demo ('movfun', 5)} for an example of this case. +## Alternatively, @var{fun} can return a row array of length +## @code{@var{ndim} * @var{n}}, where the output of @var{fun} applied to the +## i-th column should be in the values @code{i:@var{n}:(@var{ndim}*@var{n})}. +## This is useful when concatenating functions into arrays, or when using +## @command{nthargout}. +## ## See @code{demo ('movfun', 6)} for an example of this case. +## +## If the input @var{x} is a @var{N}-by-@var{M} array, and @var{fun} returns +## @var{K} values per input column, then the output @var{y} is a +## @var{N}-by-@var{M}-by-@var{K} array. All singleton dimensions are squeezed. +## Thus, if @var{M} is 1, then @var{y} is @var{N}-by-@var{K}. If in addition +## @var{K} is also 1, @var{y} has the same size as the input @var{x}. +## +## The results on the first and last @var{wlen} elements (the boundaries) is +## controlled by the boundary conditions property @asis{'Endpoints'}. Possible +## values are: +## +## @table @asis +## +## @item 'open' +## (Default) The function is applied to shorter windows at the extrema of the array, +## e.g. for a window of length 3, @code{@var{x}(1) = @var{fun} (@var{x}(1:2))}, and +## @code{@var{x}(end) = @var{fun} (@var{x}(end-1:end))} +## +## @item 'periodic' +## The window is wraped around so that +## @code{@var{x}(1) = @var{fun} ([@var{x}(end-@var{k}:end) @var{x}(1:@var{k})])}, where +## @var{k} is the radius of the window (for symmetric windows, +## @code{@var{k} = (@var{wlen} - 1) / 2)} +## +## @item 'zero' +## The array is pre- and post-padded with zeros to exactly contain the window. +## +## @item 'same' +## The resulting array @var{y} has the same values as @var{x} at the boundaries. +## +## @item 'discard' +## The resulting array @var{y} is NaN at the boundaries. +## +## @end table +## +## Note that for some of these values, the window size at the boundaries is not +## the same as in the middle part, and @var{fun} needs to work on these cases. +## +## Other optional properties are: +## +## @table @asis +## +## @item 'nancond' +## Indicating whether @code{nan} or @code{NA} values should be omitted (value: +## @asis{'omitnan'}), or included (value: @asis{'includenan'}) in the arguments +## passed to @var{func}. The default is @asis{'omitnan'}. +## +## @item 'outdim' +## A row array that allow for the selection of output dimensions. This is useful +## only when @var{fun} returns an array dimension of @var{ndim}. The default is +## to return all output dimensions. +## +## @end table +## +## @strong{Programming tip}: the property 'outdim' is implemented to save memory +## in case the output is highly dimensional or a wrapper to the base function +## that selects the desired outputs is too costly. The easiest way to select +## output dimension if memory is not an issue is to filter the result of +## @command{movfun}. If code complexity is not an issue one can create a +## wrapper, e.g. using anonymous functions. For example, if @code{basefunc} is +## the function returning a @var{K}-dimensional row output and we wish dimension +## @var{D}, we could pass the following wrapper to @command{movfun}: +## @example +## @code{@var{fun} = @@(x)basefunc (x)(:,size(x,2) * (@var{D}-1) + (1:size(x,2)))} +## @end example +## +## @seealso{movslice, prepad, postpad, permute, reshape, reshapemod} +## @end defun + +function Y = movfun (func, X, wlen, varargin) + persistent dispatch; + + valid_bc = {'open', 'periodic', 'same', 'zero', 'discard'}; + if (isempty (dispatch)) + dispatch = struct (); + for k = valid_bc + cmd = sprintf ('dispatch.%s = @%s_bc;', k{1}, k{1}); + eval (cmd); + endfor + endif + + # Parse input arguments + parser = inputParser (); + parser.FunctionName = 'movfun'; + parser.addParamValue ('Endpoints', 'open', ... + @(x)any (ismember (tolower (x), valid_bc))); + parser.addParamValue ('dim', [], ... + @(x) isempty(x) || (isscalar (x) && x > 0 && x <= ndims(X))); + parser.addParamValue ('nancond', 'omitnan', ... + @(x) any (ismember (x, {'omitnan', 'includenan'}))); + parser.addParamValue ('outdim', [], ... + @(x) isempty(x) || (isvector (x) && all (x > 0))); + + parser.parse(varargin{:}); + bc = parser.Results.Endpoints; % boundary condition + dim = parser.Results.dim; % dimension to be used as input + nancond = parser.Results.nancond; % whether nan are ignored or not + outdim = parser.Results.outdim; % selected output dimension of func + clear parser + #### End parse input arguments + + # If dim was not provided search the first non-singleton dimension + szX = size (X); + if (isempty (dim)) + dim = find (szX > 1, 1, 'first'); + endif + + # Window length is not a vector wlen = [wpre wpos] + if (isscalar (wlen)) + # Check for proper window length + # TODO: Matlab accepts even windows + if (mod (wlen, 2) == 0) + error ('Octave:invalid-input-arg', 'Window must be of odd length'); + endif + if (wlen == 1) + error ('Octave:invalid-input-arg', 'Window must be larger than 1'); + endif + endif + + # Check that array is longer that wlen at dim. At least one full window must + # fit. Function max is used to include the case when wlen is an array. + # TODO: consider bc to decide what to do here + if (max (wlen) > szX(dim)) + error ('Octave:invalid-input-arg', ... + 'window length (%d) must be shorter than length along dim (%d=%d)', ... + wlen, dim, szX(dim)); + endif + + # Move the desired dim to the 1st dimension + dims = length (szX); % number of dimensions + dperm = [dim 1:(dim-1) (dim+1):dims]; % permutation of dimensions + X = permute (X, dperm); % permute dim to first dimensions + ncols = prod (szX(dperm(2:end))); % rest dimensions as single column + N = szX(dperm(1)); % length of dim + X = reshape (X, N, ncols); % reshape input + + # obtain function for boundary conditions + bcfunc = dispatch.(tolower (bc)); + + # obtain slicer + [slc, C, Cpre, Cpos, win] = movslice (N, wlen); + + # Check that outdim makes sense + tmp = func (zeros (length (win), 1)); % output for window + noutdim = length (tmp); % number of output dimensions + if (!isempty (outdim)) + if (max (outdim) > noutdim) + error ('Octave:invalid-input-arg', ... + 'Selected output dimension (%d) is larger than output dimensions (%d)', ... + max (outdim), noutdim); + endif + else + outdim = 1:noutdim; + endif + soutdim = length (outdim); % length of selected output dimensions + # If noutdim is not one then modify function to handle multiple outputs + if (noutdim > 1) + func_ = @(x) reshape (func (x), size (x, 2), noutdim)(:,outdim); + else + func_ = func; + endif + + # apply processing to each column + # Is it faster with cellfun? Do not think so + # It could be parallel + Y = zeros (N, ncols, soutdim); + for i = 1:ncols + Y(:,i,:) = movfun_oncol (func_, X(:,i), wlen, bcfunc, ... + slc, C, Cpre, Cpos, win, soutdim); + endfor + + # Restore shape + Y = reshape (Y, [szX(dperm), soutdim]); + Y = permute (Y, [dperm, dims+1]); + Y = squeeze (Y); + +endfunction + +function y = movfun_oncol (func, x, wlen, bcfunc, I, C, Cpre, Cpos, win, odim) + N = length (x); + y = NA (N, odim); + # process center part + y(C,:) = func (x(I)); + + # process boundaries + if !isempty (Cpre) % don't process zero length bkw-window + y(Cpre,:) = bcfunc (func, x, Cpre, win, wlen, odim); + endif + if !isempty (Cpos) % don't process zero length fwd-window + y(Cpos,:) = bcfunc (func, x, Cpos, win, wlen, odim); + endif +endfunction + +function y = open_bc (func, x, idxp, win, wlen, odim) + N = length (x); + idx = idxp + win; + tf = !((idx < 1) | (idx > N)); % idx inside boundaries + + n = length (idxp); + y = zeros (n, odim); + for i = 1:n + k = idx(tf(:,i),i); + y(i,:) = func (x(k)); + endfor +endfunction + +function y = periodic_bc (func, x, idxp, win) + N = length (x); + idx = idxp + win; + tf = idx < 1; + idx(tf) = N + idx(tf); + tf = idx > N; + idx(tf) = idx(tf) - N; + y = func (x(idx)); +endfunction + +function y = same_bc (func, x, idxp, win) + idx = idxp + win; + idx(idx < 1) = 1; + N = length (x); + idx(idx > N) = N; + y = func (x(idx)); +endfunction + +function y = zero_bc (func, x, idxp, win, wlen) + if isscalar (wlen) + wlen = [wlen wlen]; + endif + N = length (x); + if (min (idxp) == 1) + x = prepad (x, N + wlen(1)); + idx = idxp + win + wlen(1); + elseif (max (idxp) == N) + x = postpad (x, N + wlen(2)); + idx = idxp + win; + endif + y = func (x(idx)); +endfunction + +function y = discard_bc (func, x, idxp, win, wlen, odim) + y = nan (length (idxp), odim); +endfunction + +%!demo +%! t = 2 * pi * linspace (0,1,100).'; +%! x = sin (3 * t); +%! xn = x + 0.1 * randn (size (x)); +%! x_o = movfun (@mean, xn, 5, 'Endpoints', 'open'); +%! x_p = movfun (@mean, xn, 5, 'Endpoints', 'periodic'); +%! x_f = movfun (@mean, xn, 5, 'Endpoints', 'same'); +%! x_z = movfun (@mean, xn, 5, 'Endpoints', 'zero'); +%! x_d = movfun (@mean, xn, 5, 'Endpoints', 'discard'); +%! +%! figure (), clf +%! h = plot ( +%! t, xn, 'o;noisy signal;', +%! t, x, '-;true;', +%! t, x_o, '-;open;', +%! t, x_p, '-;periodic;', +%! t, x_f, '-;same;', +%! t, x_z, '-;zero;', +%! t, x_d, '-;discard;'); +%! set (h(1), 'markerfacecolor', 'auto'); +%! set (h(2:end), 'linewidth', 3); +%! axis tight +%! xlabel ('time') +%! ylabel ('signal') +%! ##### +%! # Moving mean of noisy sinusoidal function with different boundary +%! # conditions. + +%!demo +%! t = 2 * pi * linspace (0,1,100).'; +%! x = sin (3 * t); +%! xn = x + 0.1 * randn (size (x)); +%! nw = 5; +%! x_ = zeros (size(x,1), nw); +%! w = 3 + (1:nw) * 4; +%! for i=1:nw +%! x_(:,i) = movfun (@mean, xn, w(i), 'Endpoints', 'periodic'); +%! endfor +%! +%! figure (), clf +%! h = plot ( +%! t, xn, 'o', +%! t, x, '-', +%! t, x_, '-'); +%! set (h(1), 'markerfacecolor', 'auto'); +%! set (h(2:end), 'linewidth', 3); +%! axis tight +%! xlabel ('time') +%! ylabel ('signal') +%! legend (h, {'noisy', 'true', strsplit(num2str(w)){:}}); +%! ##### +%! # Moving mean of noisy sinusoidal function with periodic boundary conditions +%! # using windows of different lengths. + +%!demo +%! t = linspace (0,1,100).'; +%! X = exp(-(t - [0.1:0.3:1]).^2/2/0.1^2); +%! Y = movfun (@max, X, 15); +%! figure (), clf +%! h = plot ( +%! t, X, '-', +%! t, Y, '--'); +%! axis tight +%! xlabel ('time') +%! ylabel ('signal') +%! ##### +%! # Moving max of different Gaussian functions. Illustrates the application +%! # on inputs with several columns. + +%!demo +%! t = linspace (0,1-1e-2,100).'; +%! w = 2 * pi * 3; +%! x = sin (w * t); +%! y = cos (w * t); +%! y_ = movfun (@diff, x, [1 0], 'Endpoints', 'periodic'); +%! # Is the same as y_ = x(2:end) - x(1:end-1); +%! dt = t(2) - t(1); +%! y_ = y_ / w / dt; +%! figure (), clf +%! h = plot (t, x, '-', +%! t, y, '-', +%! t, y_, ':'); +%! set (h, 'linewidth', 3); +%! axis tight +%! xlabel ('time') +%! ylabel ('signal') +%! legend (h, {'sin', 'cos', 'bkw diff'}); +%! ##### +%! # Backward difference of sinusoidal function with periodic boundary +%! # conditions. Illustrates the use of asymmetric windows. + +%!demo +%! N = 1e3; +%! wlen = 99; +%! x = linspace (-1, 1, N).'; +%! pp = [-2 0 1 0]; +%! y = polyval (pp, x); +%! yn = y + 0.1 * (abs (y) + 0.5) .* exp (randn (N, 1)); +%! +%! st = movfun (@(y)statistics(y).', yn, wlen); +%! figure (), clf +%! h = plot (x, y, '-', +%! x, yn, '.', +%! x, st(:,[3 6]), '-', +%! x, st(:,6) + [-1 1].*st(:,7), '-', +%! x, st(:,[1 2 4 5]), '-'); +%! set (h([1 3:4]), 'linewidth', 3); % mean +%! set (h(5:end), 'color', 'k'); +%! axis tight +%! xlabel ('x') +%! ylabel ('y') +%! legend (h, {'noiseless', 'noisy', 'mean', 'median'}) +%! ##### +%! # Moving window statistics. The plot highlights mean and median. black lines +%! # show minimum, first quartile, third quartile, and maximum. +%! # This illustrates the use of functions with multidimensional output. + +%!demo +%! N = 1e2; +%! wlen = 9; +%! x = linspace (-1, 1, N).'; +%! pp = [-2 0 1 0]; +%! y = polyval (pp, x); +%! y(:,2) = y + 0.1 * (abs (y) + 0.5) .* exp (randn (N, 1)); +%! y(:,1) = -y(:,1) + 0.1 * randn (N, 1); +%! +%! func = @(y)[min(y) max(y)]; +%! st = movfun (func, y, wlen); +%! figure (), clf +%! h = plot (x, y, 'o', +%! x, squeeze (st(:,1,:)), '-', +%! x, squeeze (st(:,2,:)), '-'); +%! axis tight +%! set (h(3:4), 'color', get(h(1), 'color')); +%! set (h(5:6), 'color', get(h(2), 'color')); +%! xlabel ('x') +%! ylabel ('y') +%! legend (h(1:2), {'data1', 'data2'}) +%! ##### +%! # Moving window bounds. +%! # This illustrates the use of functions with flat multidimensional output. + +%!error(movfun(@min, [0;0], 1)) % wlen == 1 +%!error(movfun(@min, [0;0], 2)) % odd wlen +%!error(movfun(@min, [0;0], 3)) % wlen larger than data +%!error(movfun(@min, [0;0;0], [1 4])) % wlen larger than data +%!error(movfun(@min, [0;0;0], [4 1])) % wlen larger than data + +%!test +%! X = (1:10).' + [-3, 0, 4]; +%! ctrfun = @(x)x(2,:); +%! valid_bc = {'same', 'periodic', 'zero'}; +%! for bc = valid_bc +%! assert (movfun(ctrfun, X, 3, 'Endpoints', bc{1}), X); +%! endfor +%! X_ = X; X_([1 end],:) = nan; +%! assert (movfun(ctrfun, X, 3, 'Endpoints', 'discard'), X_); +%! X_ = X; X_([1 end],:) = X([2 end],:); +%! assert (movfun(ctrfun, X, 3, 'Endpoints', 'open'), X_); + +%!test % dim == 2 same as transpose +%! X = randi (10, 3); +%! ctrfun = @(x)x(2,:); +%! valid_bc = {'same', 'periodic', 'zero'}; +%! for bc = valid_bc +%! assert (movfun(ctrfun, X.', 3, 'Endpoints', bc{1}, 'dim', 2), X.'); +%! endfor +%! X_ = X; X_([1 end],:) = nan; +%! assert (movfun(ctrfun, X.', 3, 'Endpoints', 'discard', 'dim', 2), X_.'); +%! X_ = X; X_([1 end],:) = X([2 end],:); +%! assert (movfun(ctrfun, X.', 3, 'Endpoints', 'open', 'dim', 2), X_.'); + +%!test +%! X = randi (10, 3, 10, 2); +%! Y = movfun (@(x)x(2,:), X, 3, 'Endpoints', 'same', 'dim', 2); +%! assert (X, Y); + +%!test # bad zero_bc +%! X = ones (10, 1); +%! Y = X; Y(1:2) = Y([end end-1]) = [0.6;0.8]; +%! assert (movfun (@mean, X, 5, 'Endpoints', 'zero'), Y); + +## Asymmetric windows +%!shared X,wlen,wlen0b,wlen0f,ctrfun,Xd,UNO,UNOd0b,UNOd0f +%! X = (1:10).' + [-3, 0, 4]; +%! wlen = [2 1]; +%! wlen0b = [0 2]; +%! wlen0f = [2 0]; +%! ctrfun = @(x)x(wlen(1)+1,:); +%! Xd = X; Xd([1:2 end],:) = nan; +%! UNO = ones (7,1); +%! UNOd0b = UNOd0f = UNO; +%! UNOd0b(end-1:end,:) = nan; +%! UNOd0f(1:2,:) = nan; + +%!assert (movfun(ctrfun, X, wlen, 'Endpoints', 'same'), X); +%!assert (movfun(ctrfun, X, wlen, 'Endpoints', 'discard'), Xd); +%!assert (movfun(ctrfun, X, wlen, 'Endpoints', 'periodic'), X); +%!assert (movfun(ctrfun, X, wlen, 'Endpoints', 'zero'), X); +%!error movfun(ctrfun, X, wlen, 'Endpoints', 'open'); % for shorter x, indexing fails + +%!assert (movfun(@min, UNO, wlen0b, 'Endpoints', 'same'), UNO); +%!assert (movfun(@min, UNO, wlen0f, 'Endpoints', 'same'), UNO); + +%!assert (movfun(@min, UNO, wlen, 'Endpoints', 'open'), UNO); +%!assert (movfun(@min, UNO, wlen0b, 'Endpoints', 'open'), UNO); +%!assert (movfun(@min, UNO, wlen0f, 'Endpoints', 'open'), UNO); + +%!assert (movfun(@min, UNO, wlen0b, 'Endpoints', 'discard'), UNOd0b); +%!assert (movfun(@min, UNO, wlen0f, 'Endpoints', 'discard'), UNOd0f); + +%!assert (movfun(@min, UNO, wlen0b, 'Endpoints', 'periodic'), UNO); +%!assert (movfun(@min, UNO, wlen0f, 'Endpoints', 'periodic'), UNO); + +%!assert (movfun(@max, UNO, wlen0b, 'Endpoints', 'zero'), UNO); +%!assert (movfun(@max, UNO, wlen0f, 'Endpoints', 'zero'), UNO); + +## Multidimensional output +%!error(movfun(@(x)[min(x) max(x)], (1:10).', 3, 'Outdim', 3)) % outdim > dim +%!assert(size(movfun(@(x)[min(x) max(x)], (1:10).', 3)), [10 2]) +%!assert(size(movfun(@(x)[min(x) max(x)], cumsum(ones(10,5),2), 3)), [10 5 2]) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/signal/movmin.m Sat Dec 15 07:29:27 2018 -0800 @@ -0,0 +1,37 @@ +## Copyright (C) 2018 Juan Pablo Carbajal +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see <http://www.gnu.org/licenses/>. + +## Author: Juan Pablo Carbajal <ajuanpi+dev@gmail.com> +## Created: 2018-08-13 + +## -*- texinfo -*- +## @defun {@var{y} =} movmin (@var{x}, @var{wlen}) +## @defunx {@var{y} =} movmin (@dots{}, @var{dim}) +## @defunx {@var{y} =} movmin (@dots{}, @var{nanstr}) +## @defunx {@var{y} =} movmin (@dots{}, @var{property}, @var{value}) +## Minimum of @var{x} over sliding window of length @var{wlen}. +## +## This is equivalent to +## @code{movfun (@@min, @var{x}, @var{wlen}, args@{:@}} +## where @code{args = @{'dim', @var{dim}, 'nancond', @var{nanstr}, @dots{}@}} +## +## Property @asis{'SamplePoints'} are not implemented yet +## +## @seealso{movfun} +## @end defun + +function y = movmin (x, wlen, varargin) + y = movfun (@min, x, wlen, parse_movargs(varargin{:}){:}); +endfunction
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/signal/movslice.m Sat Dec 15 07:29:27 2018 -0800 @@ -0,0 +1,42 @@ +## Copyright (C) 2018 Juan Pablo Carbajal +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see <http://www.gnu.org/licenses/>. + +## Author: Juan Pablo Carbajal <ajuanpi+dev@gmail.com> +## Created: 2018-12-08 + +## -*- texinfo -*- +## @defun {@var{slc} =} movslice (@var{N}, @var{wlen}) +## @defunx {[@dots{} @var{c} @var{pre} @var{pos} @var{w}] =} movslice (@dots{}) +## Generate indices to slice a 1D array of length @var{N} in windows @var{wlen} +## +## TODO +## @seealso{movfun, reshapemod} +## @end defun + +function [I, C, Cpre, Cpos, win] = movslice (N, wlen) + # TODO asymmetric window + if (isscalar (wlen)) + hwlen = (wlen - 1) / 2; + wlen = [hwlen hwlen]; + endif + + Cpre = 1:wlen(1); % centers that can't fit the pre-window + Cnf = N - wlen(2) + 1; % first center that can't fit the post-window + Cpos = Cnf:N; % centers that can't fit post-window + C = (wlen(1) + 1):(Cnf - 1); + win = (-wlen(1):wlen(2)).'; + I = C + win; +endfunction +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/signal/private/parse_movargs.m Sat Dec 15 07:29:27 2018 -0800 @@ -0,0 +1,36 @@ +## Copyright (C) 2018 Juan Pablo Carbajal +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see <http://www.gnu.org/licenses/>. + +## Author: Juan Pablo Carbajal <ajuanpi+dev@gmail.com> +## Created: 2018-08-13 + +## -*- texinfo -*- +## @defun {@var{y} =} parse_movargs (@var{x}) +## Parse arguments in compatibility with other software. +## +## Undocumented private function +## @end defun + +function args = parse_movargs (varargin) + if (numel (varargin) == 1) # scalar was given --> dim or nancond + if (ischar (varargin{1})) # --> nancond + args = {'nancond', varargin{1}}; + else # --> dim + args = {'dim', varargin{1}}; + endif + else # Name,Value pairs + args = varargin; + endif +endfunction