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:
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.)
SampledTraj<double> depth;
depth.set(2., 0.);
depth.set(3., 1.);
depth.set(4., 2.);
SampledTraj<Vector> position;
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
SampledTraj<double> x({{0.,2.}, {1.,4.}, {2.,3.}});
double value = x(0.5); // 3.
Interval range = x({0.5,1.5}); // [3,4]
auto sample_count = x.nb_samples(); // 3
auto 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)
for(const auto& [ti, xi] : x)
std::cout << ti << " -> " << xi << std::endl;
SampledTraj<Vector> p({{0.,{0,1,2}}, {1.,{1,2,3}}});
auto x = p[0];
auto 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
SampledTraj<double> x({{0.,0}, {1.,2}, {2.,0}});
auto merged = x.sampled(0.6); // original samples also retained
auto regular = x.sampled(0.6, false); // regular grid and final time only
SampledTraj<double> clock({{0.,0}, {0.5,0}, {1.5,0}, {2.,0}});
auto aligned = x.sampled_as(clock); // 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
SampledTraj<double> x({{0.,0.}, {1.,1.}, {2.,0.}});
x.truncate_tdomain({0.5,1.5}); // retains interpolated endpoints
x.shift_tdomain(10.); // time domain now [10.5,11.5]
x.stretch_tdomain({0,1}); // rescale time, not values
See also: Operations on trajectories and Trajectories serialization.