Sampled trajectories

Main author: Simon Rohou

SampledTraj<T> stores time/value pairs \((t_i,x_i)\) with distinct, ordered timestamps. Between samples, the trajectory is linearly interpolated:

\[x(t)=x_i+\frac{t-t_i}{t_{i+1}-t_i}(x_{i+1}-x_i),\qquad t\in[t_i,t_{i+1}].\]

The codomain T can be a real, a vector or a matrix. In both C++ and Python, the value type of SampledTraj can be inferred from the constructor arguments.

Construction

Create a sampled trajectory with set(value,time) or from a map of samples. Assigning a value at an existing timestamp replaces the previous one.

depth = SampledTraj_Scalar()
depth.set(2., 0.)
depth.set(3., 1.)
depth.set(4., 2.)

position = SampledTraj_Vector()
position.set(Vector([0,1]), 0.)
position.set([1,2], 1.)  # equivalent shorter notation
position.set([2,3], 2.)

Evaluation and properties

x(t) interpolates at a scalar time. x([a,b]) returns the interval hull of the interpolant over \([a,b]\). tdomain() and codomain() give the temporal domain and the hull of the values. nb_samples() counts timestamps, whereas size() gives the codomain dimension.

x = SampledTraj({0.: 2., 1.: 4., 2.: 3.})
value = x(0.5)                # 3.
range_x = x([0.5,1.5])        # [3,4]
sample_count = x.nb_samples() # 3
output_size = x.size()        # 1

Accessing samples and components

Iteration returns the stored pairs \((t_i,x_i)\) in time order. For a vector trajectory, x[i] extracts a scalar component and subvector(i,j) extracts consecutive components. In C++, as_scalar_trajs() returns all scalar components.

for ti, xi in x:
  print(ti, xi)
p = SampledTraj({0.: Vector([0.,1.,2.]),
                 1.: Vector([1.,2.,3.])})
x = p[0]
yz = p.subvector(1,2)

Sampling

sampled(dt) adds samples on a regular temporal grid, retaining original samples by default. Set keep_original_values=False to keep only the requested grid. sampled_as(reference) evaluates the trajectory at the timestamps of another reference trajectory. In C++, same_sampling() checks whether two trajectories share the same timestamps.

x = SampledTraj_Scalar({0.:0, 1.:2, 2.:0})
merged = x.sampled(0.6)         # original samples also retained
regular = x.sampled(0.6, False) # regular grid and final time only
clock = SampledTraj_Scalar({0.:0, 0.5:0, 1.5:0, 2.:0})
aligned = x.sampled_as(clock, False) # x evaluated at clock's timestamps

Changing the time domain

truncate_tdomain([a,b]) restricts the domain and inserts interpolated values at its endpoints. shift_tdomain(dt) translates timestamps. stretch_tdomain([a,b]) rescales them to a new temporal domain.

shifted = SampledTraj({0.:0., 1.:1., 2.:0.})
shifted.truncate_tdomain([0.5,1.5]) # retains interpolated endpoints
shifted.shift_tdomain(10.)          # time domain now [10.5,11.5]
shifted.stretch_tdomain([0,1])      # rescale time, not values

See also: Operations on trajectories and Trajectories serialization.