CTRE Phoenix 6 C++ 25.4.0
Loading...
Searching...
No Matches
DynamicMotionMagicTorqueCurrentFOC.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) Cross The Road Electronics.  All rights reserved.
3 * License information can be found in CTRE_LICENSE.txt
4 * For support and suggestions contact support@ctr-electronics.com or file
5 * an issue tracker at https://github.com/CrossTheRoadElec/Phoenix-Releases
6 */
7#pragma once
8
11#include <sstream>
12
13#include <units/frequency.h>
14#include <units/time.h>
15#include <units/angle.h>
16#include <units/angular_velocity.h>
17#include <units/angular_acceleration.h>
18#include <units/angular_jerk.h>
19#include <units/current.h>
20
21namespace ctre {
22namespace phoenix6 {
23namespace controls {
24
25/**
26 * Requires Phoenix Pro and CANivore;
27 * Requests Motion Magic® to target a final position using a motion profile.
28 * This dynamic request allows runtime changes to Cruise Velocity, Acceleration,
29 * and Jerk. Users can optionally provide a torque current feedforward. This
30 * control requires use of a CANivore.
31 *
32 * Motion Magic® produces a motion profile in real-time while attempting to honor the specified Cruise
33 * Velocity, Acceleration, and (optional) Jerk. This control mode does not use the Expo_kV or Expo_kA
34 * configs.
35 *
36 * Target position can be changed on-the-fly and Motion Magic® will do its best to adjust the profile. This
37 * control mode is based on torque current, so relevant closed-loop gains will use Amperes for the numerator.
38 */
40{
41 ctre::phoenix::StatusCode SendRequest(const char *network, uint32_t deviceHash, std::shared_ptr<ControlRequest> &req) const override
42 {
43 if (req.get() != this)
44 {
45 auto const reqCast = dynamic_cast<DynamicMotionMagicTorqueCurrentFOC *>(req.get());
46 if (reqCast != nullptr)
47 {
48 *reqCast = *this;
49 }
50 else
51 {
52 req = std::make_shared<DynamicMotionMagicTorqueCurrentFOC>(*this);
53 }
54 }
55
57 }
58
59public:
60 /**
61 * \brief Position to drive toward in rotations.
62 *
63 * - Units: rotations
64 *
65 */
66 units::angle::turn_t Position;
67 /**
68 * \brief Cruise velocity for profiling. The signage does not matter as the
69 * device will use the absolute value for profile generation.
70 *
71 * - Units: rotations per second
72 *
73 */
74 units::angular_velocity::turns_per_second_t Velocity;
75 /**
76 * \brief Acceleration for profiling. The signage does not matter as the device
77 * will use the absolute value for profile generation.
78 *
79 * - Units: rotations per second²
80 *
81 */
82 units::angular_acceleration::turns_per_second_squared_t Acceleration;
83 /**
84 * \brief Jerk for profiling. The signage does not matter as the device will
85 * use the absolute value for profile generation.
86 *
87 * Jerk is optional; if this is set to zero, then Motion Magic® will not apply a
88 * Jerk limit.
89 *
90 * - Units: rotations per second³
91 *
92 */
93 units::angular_jerk::turns_per_second_cubed_t Jerk;
94 /**
95 * \brief Feedforward to apply in torque current in Amperes. User can use
96 * motor's kT to scale Newton-meter to Amperes.
97 *
98 * - Units: A
99 *
100 */
101 units::current::ampere_t FeedForward = 0.0_A;
102 /**
103 * \brief Select which gains are applied by selecting the slot. Use the
104 * configuration api to set the gain values for the selected slot before
105 * enabling this feature. Slot must be within [0,2].
106 */
107 int Slot = 0;
108 /**
109 * \brief Set to true to coast the rotor when output is zero (or within
110 * deadband). Set to false to use the NeutralMode configuration setting
111 * (default). This flag exists to provide the fundamental behavior of this
112 * control when output is zero, which is to provide 0A (zero torque).
113 */
115 /**
116 * \brief Set to true to force forward limiting. This allows users to use other
117 * limit switch sensors connected to robot controller. This also allows use of
118 * active sensors that require external power.
119 */
120 bool LimitForwardMotion = false;
121 /**
122 * \brief Set to true to force reverse limiting. This allows users to use other
123 * limit switch sensors connected to robot controller. This also allows use of
124 * active sensors that require external power.
125 */
126 bool LimitReverseMotion = false;
127 /**
128 * \brief Set to true to ignore hardware limit switches and the
129 * LimitForwardMotion and LimitReverseMotion parameters, instead allowing
130 * motion.
131 *
132 * This can be useful on mechanisms such as an intake/feeder, where a limit
133 * switch stops motion while intaking but should be ignored when feeding to a
134 * shooter.
135 *
136 * The hardware limit faults and Forward/ReverseLimit signals will still report
137 * the values of the limit switches regardless of this parameter.
138 */
140 /**
141 * \brief Set to true to delay applying this control request until a timesync
142 * boundary (requires Phoenix Pro and CANivore). This eliminates the impact of
143 * nondeterministic network delays in exchange for a larger but deterministic
144 * control latency.
145 *
146 * This requires setting the ControlTimesyncFreqHz config in MotorOutputConfigs.
147 * Additionally, when this is enabled, the UpdateFreqHz of this request should
148 * be set to 0 Hz.
149 */
150 bool UseTimesync = false;
151
152 /**
153 * \brief The period at which this control will update at.
154 * This is designated in Hertz, with a minimum of 20 Hz
155 * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
156 *
157 * If this field is set to 0 Hz, the control request will
158 * be sent immediately as a one-shot frame. This may be useful
159 * for advanced applications that require outputs to be
160 * synchronized with data acquisition. In this case, we
161 * recommend not exceeding 50 ms between control calls.
162 */
163 units::frequency::hertz_t UpdateFreqHz{100_Hz};
164
165 /**
166 * \brief Requires Phoenix Pro and CANivore;
167 * Requests Motion Magic® to target a final position using a motion
168 * profile. This dynamic request allows runtime changes to Cruise
169 * Velocity, Acceleration, and Jerk. Users can optionally provide a
170 * torque current feedforward. This control requires use of a CANivore.
171 *
172 * \details Motion Magic® produces a motion profile in real-time while
173 * attempting to honor the specified Cruise Velocity, Acceleration, and
174 * (optional) Jerk. This control mode does not use the Expo_kV or
175 * Expo_kA configs.
176 *
177 * Target position can be changed on-the-fly and Motion Magic® will do
178 * its best to adjust the profile. This control mode is based on torque
179 * current, so relevant closed-loop gains will use Amperes for the
180 * numerator.
181 *
182 * \param Position Position to drive toward in rotations.
183 * \param Velocity Cruise velocity for profiling. The signage does not
184 * matter as the device will use the absolute value for
185 * profile generation.
186 * \param Acceleration Acceleration for profiling. The signage does not
187 * matter as the device will use the absolute value for
188 * profile generation.
189 * \param Jerk Jerk for profiling. The signage does not matter as the device
190 * will use the absolute value for profile generation.
191 *
192 * Jerk is optional; if this is set to zero, then Motion Magic®
193 * will not apply a Jerk limit.
194 */
195 DynamicMotionMagicTorqueCurrentFOC(units::angle::turn_t Position, units::angular_velocity::turns_per_second_t Velocity, units::angular_acceleration::turns_per_second_squared_t Acceleration, units::angular_jerk::turns_per_second_cubed_t Jerk) : ControlRequest{"DynamicMotionMagicTorqueCurrentFOC"},
196 Position{std::move(Position)},
197 Velocity{std::move(Velocity)},
199 Jerk{std::move(Jerk)}
200 {}
201
202 /**
203 * \brief Modifies this Control Request's Position parameter and returns itself for
204 * method-chaining and easier to use request API.
205 *
206 * Position to drive toward in rotations.
207 *
208 * - Units: rotations
209 *
210 *
211 * \param newPosition Parameter to modify
212 * \returns Itself
213 */
214 DynamicMotionMagicTorqueCurrentFOC &WithPosition(units::angle::turn_t newPosition)
215 {
216 Position = std::move(newPosition);
217 return *this;
218 }
219
220 /**
221 * \brief Modifies this Control Request's Velocity parameter and returns itself for
222 * method-chaining and easier to use request API.
223 *
224 * Cruise velocity for profiling. The signage does not matter as the device
225 * will use the absolute value for profile generation.
226 *
227 * - Units: rotations per second
228 *
229 *
230 * \param newVelocity Parameter to modify
231 * \returns Itself
232 */
233 DynamicMotionMagicTorqueCurrentFOC &WithVelocity(units::angular_velocity::turns_per_second_t newVelocity)
234 {
235 Velocity = std::move(newVelocity);
236 return *this;
237 }
238
239 /**
240 * \brief Modifies this Control Request's Acceleration parameter and returns itself for
241 * method-chaining and easier to use request API.
242 *
243 * Acceleration for profiling. The signage does not matter as the device will
244 * use the absolute value for profile generation.
245 *
246 * - Units: rotations per second²
247 *
248 *
249 * \param newAcceleration Parameter to modify
250 * \returns Itself
251 */
252 DynamicMotionMagicTorqueCurrentFOC &WithAcceleration(units::angular_acceleration::turns_per_second_squared_t newAcceleration)
253 {
254 Acceleration = std::move(newAcceleration);
255 return *this;
256 }
257
258 /**
259 * \brief Modifies this Control Request's Jerk parameter and returns itself for
260 * method-chaining and easier to use request API.
261 *
262 * Jerk for profiling. The signage does not matter as the device will use the
263 * absolute value for profile generation.
264 *
265 * Jerk is optional; if this is set to zero, then Motion Magic® will not apply a
266 * Jerk limit.
267 *
268 * - Units: rotations per second³
269 *
270 *
271 * \param newJerk Parameter to modify
272 * \returns Itself
273 */
274 DynamicMotionMagicTorqueCurrentFOC &WithJerk(units::angular_jerk::turns_per_second_cubed_t newJerk)
275 {
276 Jerk = std::move(newJerk);
277 return *this;
278 }
279
280 /**
281 * \brief Modifies this Control Request's FeedForward parameter and returns itself for
282 * method-chaining and easier to use request API.
283 *
284 * Feedforward to apply in torque current in Amperes. User can use motor's kT
285 * to scale Newton-meter to Amperes.
286 *
287 * - Units: A
288 *
289 *
290 * \param newFeedForward Parameter to modify
291 * \returns Itself
292 */
293 DynamicMotionMagicTorqueCurrentFOC &WithFeedForward(units::current::ampere_t newFeedForward)
294 {
295 FeedForward = std::move(newFeedForward);
296 return *this;
297 }
298
299 /**
300 * \brief Modifies this Control Request's Slot parameter and returns itself for
301 * method-chaining and easier to use request API.
302 *
303 * Select which gains are applied by selecting the slot. Use the configuration
304 * api to set the gain values for the selected slot before enabling this
305 * feature. Slot must be within [0,2].
306 *
307 * \param newSlot Parameter to modify
308 * \returns Itself
309 */
311 {
312 Slot = std::move(newSlot);
313 return *this;
314 }
315
316 /**
317 * \brief Modifies this Control Request's OverrideCoastDurNeutral parameter and returns itself for
318 * method-chaining and easier to use request API.
319 *
320 * Set to true to coast the rotor when output is zero (or within deadband). Set
321 * to false to use the NeutralMode configuration setting (default). This flag
322 * exists to provide the fundamental behavior of this control when output is
323 * zero, which is to provide 0A (zero torque).
324 *
325 * \param newOverrideCoastDurNeutral Parameter to modify
326 * \returns Itself
327 */
329 {
330 OverrideCoastDurNeutral = std::move(newOverrideCoastDurNeutral);
331 return *this;
332 }
333
334 /**
335 * \brief Modifies this Control Request's LimitForwardMotion parameter and returns itself for
336 * method-chaining and easier to use request API.
337 *
338 * Set to true to force forward limiting. This allows users to use other limit
339 * switch sensors connected to robot controller. This also allows use of active
340 * sensors that require external power.
341 *
342 * \param newLimitForwardMotion Parameter to modify
343 * \returns Itself
344 */
346 {
347 LimitForwardMotion = std::move(newLimitForwardMotion);
348 return *this;
349 }
350
351 /**
352 * \brief Modifies this Control Request's LimitReverseMotion parameter and returns itself for
353 * method-chaining and easier to use request API.
354 *
355 * Set to true to force reverse limiting. This allows users to use other limit
356 * switch sensors connected to robot controller. This also allows use of active
357 * sensors that require external power.
358 *
359 * \param newLimitReverseMotion Parameter to modify
360 * \returns Itself
361 */
363 {
364 LimitReverseMotion = std::move(newLimitReverseMotion);
365 return *this;
366 }
367
368 /**
369 * \brief Modifies this Control Request's IgnoreHardwareLimits parameter and returns itself for
370 * method-chaining and easier to use request API.
371 *
372 * Set to true to ignore hardware limit switches and the LimitForwardMotion and
373 * LimitReverseMotion parameters, instead allowing motion.
374 *
375 * This can be useful on mechanisms such as an intake/feeder, where a limit
376 * switch stops motion while intaking but should be ignored when feeding to a
377 * shooter.
378 *
379 * The hardware limit faults and Forward/ReverseLimit signals will still report
380 * the values of the limit switches regardless of this parameter.
381 *
382 * \param newIgnoreHardwareLimits Parameter to modify
383 * \returns Itself
384 */
386 {
387 IgnoreHardwareLimits = std::move(newIgnoreHardwareLimits);
388 return *this;
389 }
390
391 /**
392 * \brief Modifies this Control Request's UseTimesync parameter and returns itself for
393 * method-chaining and easier to use request API.
394 *
395 * Set to true to delay applying this control request until a timesync boundary
396 * (requires Phoenix Pro and CANivore). This eliminates the impact of
397 * nondeterministic network delays in exchange for a larger but deterministic
398 * control latency.
399 *
400 * This requires setting the ControlTimesyncFreqHz config in MotorOutputConfigs.
401 * Additionally, when this is enabled, the UpdateFreqHz of this request should
402 * be set to 0 Hz.
403 *
404 * \param newUseTimesync Parameter to modify
405 * \returns Itself
406 */
408 {
409 UseTimesync = std::move(newUseTimesync);
410 return *this;
411 }
412 /**
413 * \brief Sets the period at which this control will update at.
414 * This is designated in Hertz, with a minimum of 20 Hz
415 * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
416 *
417 * If this field is set to 0 Hz, the control request will
418 * be sent immediately as a one-shot frame. This may be useful
419 * for advanced applications that require outputs to be
420 * synchronized with data acquisition. In this case, we
421 * recommend not exceeding 50 ms between control calls.
422 *
423 * \param newUpdateFreqHz Parameter to modify
424 * \returns Itself
425 */
426 DynamicMotionMagicTorqueCurrentFOC &WithUpdateFreqHz(units::frequency::hertz_t newUpdateFreqHz)
427 {
428 UpdateFreqHz = newUpdateFreqHz;
429 return *this;
430 }
431 /**
432 * \brief Returns a string representation of the object.
433 *
434 * \returns a string representation of the object.
435 */
436 std::string ToString() const override
437 {
438 std::stringstream ss;
439 ss << "Control: DynamicMotionMagicTorqueCurrentFOC" << std::endl;
440 ss << " Position: " << Position.to<double>() << " rotations" << std::endl;
441 ss << " Velocity: " << Velocity.to<double>() << " rotations per second" << std::endl;
442 ss << " Acceleration: " << Acceleration.to<double>() << " rotations per second²" << std::endl;
443 ss << " Jerk: " << Jerk.to<double>() << " rotations per second³" << std::endl;
444 ss << " FeedForward: " << FeedForward.to<double>() << " A" << std::endl;
445 ss << " Slot: " << Slot << std::endl;
446 ss << " OverrideCoastDurNeutral: " << OverrideCoastDurNeutral << std::endl;
447 ss << " LimitForwardMotion: " << LimitForwardMotion << std::endl;
448 ss << " LimitReverseMotion: " << LimitReverseMotion << std::endl;
449 ss << " IgnoreHardwareLimits: " << IgnoreHardwareLimits << std::endl;
450 ss << " UseTimesync: " << UseTimesync << std::endl;
451 return ss.str();
452 }
453
454 /**
455 * \brief Gets information about this control request.
456 *
457 * \returns Map of control parameter names and corresponding applied values
458 */
459 std::map<std::string, std::string> GetControlInfo() const override
460 {
461 std::map<std::string, std::string> controlInfo;
462 std::stringstream ss;
463 controlInfo["Name"] = GetName();
464 ss << Position.to<double>(); controlInfo["Position"] = ss.str(); ss.str(std::string{});
465 ss << Velocity.to<double>(); controlInfo["Velocity"] = ss.str(); ss.str(std::string{});
466 ss << Acceleration.to<double>(); controlInfo["Acceleration"] = ss.str(); ss.str(std::string{});
467 ss << Jerk.to<double>(); controlInfo["Jerk"] = ss.str(); ss.str(std::string{});
468 ss << FeedForward.to<double>(); controlInfo["FeedForward"] = ss.str(); ss.str(std::string{});
469 ss << Slot; controlInfo["Slot"] = ss.str(); ss.str(std::string{});
470 ss << OverrideCoastDurNeutral; controlInfo["OverrideCoastDurNeutral"] = ss.str(); ss.str(std::string{});
471 ss << LimitForwardMotion; controlInfo["LimitForwardMotion"] = ss.str(); ss.str(std::string{});
472 ss << LimitReverseMotion; controlInfo["LimitReverseMotion"] = ss.str(); ss.str(std::string{});
473 ss << IgnoreHardwareLimits; controlInfo["IgnoreHardwareLimits"] = ss.str(); ss.str(std::string{});
474 ss << UseTimesync; controlInfo["UseTimesync"] = ss.str(); ss.str(std::string{});
475 return controlInfo;
476 }
477};
478
479}
480}
481}
482
CTREXPORT int c_ctre_phoenix6_RequestControlDynamicMotionMagicTorqueCurrentFOC(const char *canbus, uint32_t ecuEncoding, double updateFrequency, double Position, double Velocity, double Acceleration, double Jerk, double FeedForward, int Slot, bool OverrideCoastDurNeutral, bool LimitForwardMotion, bool LimitReverseMotion, bool IgnoreHardwareLimits, bool UseTimesync)
Abstract Control Request class that other control requests extend for use.
Definition ControlRequest.hpp:30
std::string const & GetName() const
Definition ControlRequest.hpp:53
Requires Phoenix Pro and CANivore; Requests Motion Magic® to target a final position using a motion p...
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:40
bool IgnoreHardwareLimits
Set to true to ignore hardware limit switches and the LimitForwardMotion and LimitReverseMotion param...
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:139
DynamicMotionMagicTorqueCurrentFOC & WithPosition(units::angle::turn_t newPosition)
Modifies this Control Request's Position parameter and returns itself for method-chaining and easier ...
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:214
DynamicMotionMagicTorqueCurrentFOC & WithIgnoreHardwareLimits(bool newIgnoreHardwareLimits)
Modifies this Control Request's IgnoreHardwareLimits parameter and returns itself for method-chaining...
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:385
DynamicMotionMagicTorqueCurrentFOC & WithUseTimesync(bool newUseTimesync)
Modifies this Control Request's UseTimesync parameter and returns itself for method-chaining and easi...
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:407
units::angle::turn_t Position
Position to drive toward in rotations.
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:66
DynamicMotionMagicTorqueCurrentFOC & WithSlot(int newSlot)
Modifies this Control Request's Slot parameter and returns itself for method-chaining and easier to u...
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:310
bool OverrideCoastDurNeutral
Set to true to coast the rotor when output is zero (or within deadband).
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:114
int Slot
Select which gains are applied by selecting the slot.
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:107
DynamicMotionMagicTorqueCurrentFOC & WithOverrideCoastDurNeutral(bool newOverrideCoastDurNeutral)
Modifies this Control Request's OverrideCoastDurNeutral parameter and returns itself for method-chain...
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:328
DynamicMotionMagicTorqueCurrentFOC & WithVelocity(units::angular_velocity::turns_per_second_t newVelocity)
Modifies this Control Request's Velocity parameter and returns itself for method-chaining and easier ...
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:233
std::string ToString() const override
Returns a string representation of the object.
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:436
bool LimitReverseMotion
Set to true to force reverse limiting.
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:126
units::angular_jerk::turns_per_second_cubed_t Jerk
Jerk for profiling.
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:93
units::angular_velocity::turns_per_second_t Velocity
Cruise velocity for profiling.
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:74
DynamicMotionMagicTorqueCurrentFOC & WithAcceleration(units::angular_acceleration::turns_per_second_squared_t newAcceleration)
Modifies this Control Request's Acceleration parameter and returns itself for method-chaining and eas...
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:252
bool UseTimesync
Set to true to delay applying this control request until a timesync boundary (requires Phoenix Pro an...
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:150
DynamicMotionMagicTorqueCurrentFOC & WithLimitForwardMotion(bool newLimitForwardMotion)
Modifies this Control Request's LimitForwardMotion parameter and returns itself for method-chaining a...
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:345
bool LimitForwardMotion
Set to true to force forward limiting.
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:120
DynamicMotionMagicTorqueCurrentFOC(units::angle::turn_t Position, units::angular_velocity::turns_per_second_t Velocity, units::angular_acceleration::turns_per_second_squared_t Acceleration, units::angular_jerk::turns_per_second_cubed_t Jerk)
Requires Phoenix Pro and CANivore; Requests Motion Magic® to target a final position using a motion p...
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:195
DynamicMotionMagicTorqueCurrentFOC & WithFeedForward(units::current::ampere_t newFeedForward)
Modifies this Control Request's FeedForward parameter and returns itself for method-chaining and easi...
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:293
DynamicMotionMagicTorqueCurrentFOC & WithUpdateFreqHz(units::frequency::hertz_t newUpdateFreqHz)
Sets the period at which this control will update at.
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:426
units::frequency::hertz_t UpdateFreqHz
The period at which this control will update at.
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:163
DynamicMotionMagicTorqueCurrentFOC & WithLimitReverseMotion(bool newLimitReverseMotion)
Modifies this Control Request's LimitReverseMotion parameter and returns itself for method-chaining a...
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:362
DynamicMotionMagicTorqueCurrentFOC & WithJerk(units::angular_jerk::turns_per_second_cubed_t newJerk)
Modifies this Control Request's Jerk parameter and returns itself for method-chaining and easier to u...
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:274
units::current::ampere_t FeedForward
Feedforward to apply in torque current in Amperes.
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:101
std::map< std::string, std::string > GetControlInfo() const override
Gets information about this control request.
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:459
units::angular_acceleration::turns_per_second_squared_t Acceleration
Acceleration for profiling.
Definition DynamicMotionMagicTorqueCurrentFOC.hpp:82
Status codes reported by APIs, including OK, warnings, and errors.
Definition StatusCodes.h:27
Definition Diff_PositionDutyCycle_Position.hpp:15
Definition span.hpp:401