Nothing
# pulstran.R
# Copyright (C) 2019 Geert van Boxtel <gjmvanboxtel@gmail.com>
# Matlab/Octave signal package:
# Copyright (C) 2000 Paul Kienzle <pkienzle@users.sf.net>
#
# 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; see the file COPYING. If not, see
# <https://www.gnu.org/licenses/>.
#
# 20191126 Geert van Boxtel First version for v0.1.0
#------------------------------------------------------------------------------
#' Pulse train
#'
#' Generate a train of pulses based on samples of a continuous function.
#'
#' Generate the signal \code{y <- sum(func(t + d, ...))} for each \code{d}. If
#' \code{d} is a matrix of two columns, the first column is the delay \code{d}
#' and the second column is the amplitude \code{a}, and \code{y <- sum(a *
#' func(t + d))} for each \code{d, a}. Clearly, \code{func} must be a function
#' which accepts a vector of times. Any extra arguments needed for the function
#' must be tagged on the end.
#'
#' If instead of a function name you supply a pulse shape sampled at frequency
#' \code{fs} (default 1 Hz), an interpolated version of the pulse is added at
#' each delay \code{d}. The interpolation stays within the the time range of the
#' delayed pulse. The interpolation method defaults to linear, but it can be any
#' interpolation method accepted by the function \code{interp1}
#'
#' @param t Time values at which \code{func} is evaluated, specified as a
#' vector.
#' @param d Offset removed from the values of the array \code{t}, specified as a
#' real vector, matrix, or array. You can apply an optional gain factor to
#' each delayed evaluation by specifying \code{d} as a two-column matrix, with
#' offset defined in column 1 and associated gain in column 2. If you specify
#' \code{d} as a vector, the values are interpreted as delays only.
#' @param func Continuous function used to generate a pulse train based on its
#' samples, specified as 'rectpuls', 'gauspuls', 'tripuls', or a function
#' handle. If you use \code{func} as a function handle, you can pass the
#' function parameters as follows:\cr \code{y <- pulstran(t, d, 'gauspuls',
#' 10e3, bw = 0.5)}.\cr This creates a pulse train using a 10 kHz Gaussian
#' pulse with 50\% bandwidth. Alternatively, \code{func} can be a prototype
#' function, specified as a vector. The interval of the function \code{0} to
#' \code{(length(p) - 1) / fs}, and its samples are identically zero outside
#' this interval. By default, linear interpolation is used for generating
#' delays.
#' @param fs Sample rate in Hz, specified as a real scalar.
#' @param method Interpolation method, specified as one of the following
#' options:
#' \describe{
#' \item{"linear" (default)}{Linear interpolation. The interpolated value at a
#' query point is based on linear interpolation of the values at neighboring
#' grid points in each respective dimension. This is the default interpolation
#' method.}
#' \item{"nearest"}{Nearest neighbor interpolation. The interpolated value at a
#' query point is the value at the nearest sample grid point.}
#' \item{"cubic"}{Shape-preserving piecewise cubic interpolation. The
#' interpolated value at a query point is based on a shape-preserving
#' piecewise cubic interpolation of the values at neighboring grid points.}
#' \item{"spline"}{Spline interpolation using not-a-knot end conditions. The
#' interpolated value at a query point is based on a cubic interpolation of
#' the values at neighboring grid points in each respective dimension.}
#' }
#' Interpolation is performed by the function \code{'interp1'} function in the
#' library \code{'pracma'}, and any interpolation method accepted by the
#' function \code{'interp1'} can be specified here.
#'
#' @param ... Further arguments passed to \code{func}.
#'
#' @return Pulse train generated by the function, returned as a vector.
#'
#' @examples
#'
#' ## periodic rectangular pulse
#' t <- seq(0, 60, 1/1e3)
#' d <- cbind(seq(0, 60, 2), sin(2 * pi * 0.05 * seq(0, 60, 2)))
#' y <- pulstran(t, d, 'rectpuls')
#' plot(t, y, type = "l", xlab = "Time (s)", ylab = "Waveform",
#' main = "Periodic rectangular pulse")
#'
#' ## assymetric sawtooth waveform
#' fs <- 1e3
#' t <- seq(0, 1, 1/fs)
#' d <- seq(0, 1, 1/3)
#' x <- tripuls(t, 0.2, -1)
#' y <- pulstran(t, d, x, fs)
#' plot(t, y, type = "l", xlab = "Time (s)", ylab = "Waveform",
#' main = "Asymmetric sawtooth waveform")
#'
#' ## Periodic Gaussian waveform
#' fs <- 1e7
#' tc <- 0.00025
#' t <- seq(-tc, tc, 1/fs)
#' x <- gauspuls(t, 10e3, 0.5)
#' plot(t, x, type="l", xlab = "Time (s)", ylab = "Waveform",
#' main = "Gaussian pulse")
#' ts <- seq(0, 0.025, 1/50e3)
#' d <- cbind(seq(0, 0.025, 1/1e3), sin(2 * pi * 0.1 * (0:25)))
#' y <- pulstran(ts, d, x, fs)
#' plot(ts, y, type = "l", xlab = "Time (s)", ylab = "Waveform",
#' main = "Gaussian pulse train")
#'
#' # Custom pulse trains
#' fnx <- function(x, fn) sin(2 * pi * fn * x) * exp(-fn * abs(x))
#' ffs <- 1000
#' tp <- seq(0, 1, 1/ffs)
#' pp <- fnx(tp, 30)
#' plot(tp, pp, type = "l",xlab = 'Time (s)', ylab = 'Waveform',
#' main = "Custom pulse")
#' fs <- 2e3
#' t <- seq(0, 1.2, 1/fs)
#' d <- seq(0, 1, 1/3)
#' dd <- cbind(d, 4^-d)
#' z <- pulstran(t, dd, pp, ffs)
#' plot(t, z, type = "l", xlab = "Time (s)", ylab = "Waveform",
#' main = "Custom pulse train")
#'
#' @author Sylvain Pelissier, \email{sylvain.pelissier@@gmail.com}.\cr
#' Conversion to R by Geert van Boxtel \email{G.J.M.vanBoxtel@@gmail.com}.
#'
#' @export
pulstran <- function(t, d, func, fs = 1,
method = c("linear", "nearest", "cubic", "spline"), ...) {
if (missing(t) || length(t) <= 0)
stop("t must be an array")
y <- rep(0L, length(t))
if (missing(d) || length(d) <= 0) return(y)
if (is.vector(d) || (is.array(d) && length(dim(d)) == 1)) {
a <- rep(1L, length(d))
} else if ((is.matrix(d) || is.array(d)) && ncol(d) == 2) {
a <- d[, 2]
d <- d[, 1]
} else stop("invalid value specified for d")
if (is.character(func)) {
for (i in seq_along(d)) {
y <- y + a[i] * do.call(func, list(t - d[i], ...))
}
} else {
method <- match.arg(method)
span <- (length(func) - 1) / fs
t_pulse <- (0:(length(func) - 1)) / fs
for (i in seq_along(d)) {
dt <- t - d[i]
idx <- which(dt >= 0 & dt <= span)
if (length(idx) > 0) {
y[idx] <- y[idx] + a[i] *
pracma::interp1(t_pulse, func, dt[idx], method)
}
}
}
y
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.