1 /* -------------------------------------------------------------------------- *
2 * OpenSim: Coordinate.cpp *
3 * -------------------------------------------------------------------------- *
4 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation. *
5 * See http://opensim.stanford.edu and the NOTICE file for more information. *
6 * OpenSim is developed at Stanford University and supported by the US *
7 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA *
8 * through the Warrior Web program. *
9 * *
10 * Author(s): Ajay Seth, Michael A. Sherman, Ayman Habib *
11 * Contributor(s): Frank C. Anderson, Jeffrey A. Reinbolt *
12 * *
13 * Licensed under the Apache License, Version 2.0 (the "License"); you may *
14 * not use this file except in compliance with the License. You may obtain a *
15 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. *
16 * *
17 * Unless required by applicable law or agreed to in writing, software *
18 * distributed under the License is distributed on an "AS IS" BASIS, *
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
20 * See the License for the specific language governing permissions and *
21 * limitations under the License. *
22 * -------------------------------------------------------------------------- */
23
24 //=============================================================================
25 // INCLUDES
26 //=============================================================================
27 #include "Coordinate.h"
28 #include "CoordinateCouplerConstraint.h"
29 #include <OpenSim/Simulation/Model/Model.h>
30 #include <OpenSim/Simulation/SimbodyEngine/Joint.h>
31 #include "simbody/internal/Constraint.h"
32
33 //=============================================================================
34 // STATICS
35 //=============================================================================
36 using namespace std;
37 using namespace SimTK;
38 using namespace OpenSim;
39
40 /** @cond **/ // hide from Doxygen
41 /**
42 * A Constant Function whose value is modifiable.
43 */
44 class ModifiableConstant : public SimTK::Function_<SimTK::Real>{
45 public:
ModifiableConstant(const SimTK::Real & value,int argumentSize)46 ModifiableConstant(const SimTK::Real& value, int argumentSize) :
47 argumentSize(argumentSize), value(value) { }
48
clone() const49 ModifiableConstant* clone() const override {
50 return new ModifiableConstant(this->value, this->argumentSize);
51 }
52
calcValue(const SimTK::Vector & x) const53 SimTK::Real calcValue(const SimTK::Vector& x) const override {
54 assert(x.size() == argumentSize);
55 return value;
56 }
57
calcDerivative(const std::vector<int> & derivComponents,const SimTK::Vector & x) const58 SimTK::Real calcDerivative(const std::vector<int>& derivComponents,
59 const SimTK::Vector& x) const {
60 return calcDerivative(SimTK::ArrayViewConst_<int>(derivComponents),x);
61 }
calcDerivative(const SimTK::Array_<int> & derivComponents,const SimTK::Vector & x) const62 SimTK::Real calcDerivative(const SimTK::Array_<int>& derivComponents,
63 const SimTK::Vector& x) const override {
64 return 0;
65 }
66
getArgumentSize() const67 int getArgumentSize() const override {
68 return argumentSize;
69 }
getMaxDerivativeOrder() const70 int getMaxDerivativeOrder() const override {
71 return std::numeric_limits<int>::max();
72 }
73
setValue(SimTK::Real newValue)74 void setValue(SimTK::Real newValue){
75 value = newValue;
76 }
77 private:
78 const int argumentSize;
79 SimTK::Real value;
80 };
81 /** @endcond **/
82
83 //=============================================================================
84 // CONSTRUCTOR(S) AND DESTRUCTOR
85 //=============================================================================
86 //_____________________________________________________________________________
87 /**
88 * Default constructor.
89 */
Coordinate()90 Coordinate::Coordinate()
91 {
92 constructProperties();
93 }
94
95 //_____________________________________________________________________________
96 /**
97 * Constructor.
98 */
Coordinate(const std::string & aName,MotionType aMotionType,double defaultValue,double aRangeMin,double aRangeMax)99 Coordinate::Coordinate(const std::string &aName, MotionType aMotionType,
100 double defaultValue, double aRangeMin, double aRangeMax) :
101 Coordinate()
102 {
103 setName(aName);
104 //setMotionType(aMotionType);
105 setDefaultValue(defaultValue);
106 setRangeMin(aRangeMin);
107 setRangeMax(aRangeMax);
108 }
109
110 //_____________________________________________________________________________
111 /**
112 * Connect properties to local pointers.
113 */
constructProperties(void)114 void Coordinate::constructProperties(void)
115 {
116 setAuthors("Ajay Seth, Ayman Habib, Michael Sherman");
117
118 constructProperty_default_value(0.0);
119 constructProperty_default_speed_value(0.0);
120
121 Array<double> defaultRange(-10.0, 2); //two values in range
122 defaultRange[1] = 10.0; // second value in range is 10.0
123 constructProperty_range(defaultRange);
124
125 constructProperty_clamped(false);
126 constructProperty_locked(false);
127
128 constructProperty_prescribed_function();
129 constructProperty_prescribed(false);
130 constructProperty_is_free_to_satisfy_constraints(false);
131 }
132
133
134 //_____________________________________________________________________________
135 /*
136 * Perform some set up functions that happen after the
137 * object has been deserialized or copied.
138 *
139 */
extendFinalizeFromProperties()140 void Coordinate::extendFinalizeFromProperties()
141 {
142 Super::extendFinalizeFromProperties();
143
144 string prefix = "Coordinate("+getName()+")::extendFinalizeFromProperties:";
145
146 // Make sure the default value is within the range when clamped
147 if (get_clamped()){
148 // Make sure the range is min to max.
149 SimTK_ERRCHK_ALWAYS(get_range(0) <= get_range(1), prefix.c_str(),
150 "Maximum coordinate range less than minimum.");
151
152 double dv = get_default_value();
153 SimTK_ERRCHK2_ALWAYS(dv > (get_range(0) - SimTK::SqrtEps), prefix.c_str(),
154 "Default coordinate value is less than range minimum.\n"
155 "Default value = %g < min = %g.", dv, get_range(0));
156
157 SimTK_ERRCHK2_ALWAYS(dv < (get_range(1) + SimTK::SqrtEps), prefix.c_str(),
158 "Default coordinate value is greater than range maximum.\n"
159 "Default value = %g > max = %g.", dv, get_range(1));
160 }
161
162 _lockedWarningGiven=false;
163
164 _speedName = getName() + "/speed";
165 }
166
extendAddToSystem(SimTK::MultibodySystem & system) const167 void Coordinate::extendAddToSystem(SimTK::MultibodySystem& system) const
168 {
169 Super::extendAddToSystem(system);
170
171 // Make this modifiable temporarily so we can record information needed
172 // to later access our pieces of the SimTK::MultibodySystem. That info is
173 // const after the system has been built.
174 Coordinate* mutableThis = const_cast<Coordinate *>(this);
175
176 // Define the locked value for the constraint as a function.
177 // The PrescribedMotion will take ownership, but we'll keep a reference
178 // pointer here to allow for later modification.
179 std::unique_ptr<ModifiableConstant>
180 funcOwner(new ModifiableConstant(get_default_value(), 1));
181 mutableThis->_lockFunction = funcOwner.get();
182
183 // The underlying SimTK constraint
184 SimTK::Constraint::PrescribedMotion
185 lock(system.updMatterSubsystem(),
186 funcOwner.release(), // give up ownership
187 _bodyIndex,
188 SimTK::MobilizerQIndex(_mobilizerQIndex));
189
190 // Save the index so we can access the SimTK::Constraint later
191 mutableThis->_lockedConstraintIndex = lock.getConstraintIndex();
192
193 if(!getProperty_prescribed_function().empty()){
194 //create prescribed motion constraint automatically
195 SimTK::Constraint::PrescribedMotion prescribe(
196 _model->updMatterSubsystem(),
197 get_prescribed_function().createSimTKFunction(),
198 _bodyIndex,
199 SimTK::MobilizerQIndex(_mobilizerQIndex));
200 mutableThis->_prescribedConstraintIndex = prescribe.getConstraintIndex();
201 }
202 else if(get_prescribed()){
203 // if prescribed is set to true, and there is no prescribed
204 // function defined, then it cannot be prescribed (set to false).
205 mutableThis->upd_prescribed() = false;
206 }
207
208 //Now add clamping
209 addModelingOption("is_clamped", 1);
210
211 SimTK::SubsystemIndex sbsix =
212 getModel().getMatterSubsystem().getMySubsystemIndex();
213
214 //Expose coordinate state variable
215 CoordinateStateVariable* csv
216 = new CoordinateStateVariable("value", *this,
217 sbsix, _mobilizerQIndex );
218 addStateVariable(csv);
219
220 //Expose coordinate's speed state variable
221 SpeedStateVariable* ssv =
222 new SpeedStateVariable("speed", *this, sbsix, _mobilizerQIndex);
223 addStateVariable(ssv);
224 }
225
extendRealizeInstance(const SimTK::State & state) const226 void Coordinate::extendRealizeInstance(const SimTK::State& state) const
227 {
228 //const MobilizedBody& mb
229 // = getModel().getMatterSubsystem().getMobilizedBody(_bodyIndex);
230
231 //int uix = state.getUStart() + mb.getFirstUIndex(state) + _mobilizerQIndex;
232
233 /* Set the YIndex on the StateVariable */
234 }
235
extendInitStateFromProperties(State & s) const236 void Coordinate::extendInitStateFromProperties(State& s) const
237 {
238 // Cannot enforce the constraint, since state of constraints may still be undefined
239 const MobilizedBody& mb=_model->getMatterSubsystem().getMobilizedBody(_bodyIndex);
240 int nq=mb.getNumQ(s);
241 if (_mobilizerQIndex>=nq){
242 //Something is wrong/inconsistent with model definition. Abort
243 throw(Exception("Coordinate: "+getName()+" is not consistent with owner Joint. Aborting."));
244 }
245 _model->getMatterSubsystem().getMobilizedBody(_bodyIndex)
246 .setOneQ(s,_mobilizerQIndex,get_default_value());
247 _model->getMatterSubsystem().getMobilizedBody(_bodyIndex)
248 .setOneU(s,_mobilizerQIndex,get_default_speed_value());
249 setIsPrescribed(s, get_prescribed());
250 setClamped(s, get_clamped());
251 // Locking takes precedence if all joint constraints are ON
252 setLocked(s, get_locked());
253 }
254
extendSetPropertiesFromState(const SimTK::State & state)255 void Coordinate::extendSetPropertiesFromState(const SimTK::State& state)
256 {
257 upd_default_value() = _model->getMatterSubsystem().getMobilizedBody(_bodyIndex).getOneQ(state,_mobilizerQIndex);
258 upd_default_speed_value() = _model->getMatterSubsystem().getMobilizedBody(_bodyIndex).getOneU(state,_mobilizerQIndex);
259 upd_prescribed() = isPrescribed(state);
260 upd_clamped() = getClamped(state);
261 upd_locked() = getLocked(state);
262 }
263
264
265 //=============================================================================
266 // GET AND SET
267 //=============================================================================
268 //-----------------------------------------------------------------------------
269 // JOINT
270 //-----------------------------------------------------------------------------
271 //_____________________________________________________________________________
272 /**
273 * Set the joint to which this coordinate belongs.
274 *
275 * @param aowningJoint Joint to which this coordinate belongs.
276 */
setJoint(const Joint & aOwningJoint)277 void Coordinate::setJoint(const Joint& aOwningJoint)
278 {
279 _joint = &aOwningJoint;
280 }
281 //_____________________________________________________________________________
282 /**
283 * Get the joint to which this coordinate belongs.
284 *
285 * @return Joint to which this coordinate belongs.
286 */
getJoint() const287 const Joint& Coordinate::getJoint() const
288 {
289 return(_joint.getRef());
290 }
291
getMotionType() const292 Coordinate::MotionType Coordinate::getMotionType() const
293 {
294 int ix = getJoint().getProperty_coordinates().findIndexForName(getName());
295 return getJoint().getMotionType(Joint::CoordinateIndex(ix));
296 }
297
298
299 //-----------------------------------------------------------------------------
300 // VALUE
301 //-----------------------------------------------------------------------------
302 //done_____________________________________________________________________________
303 /**
304 * Get the value.
305 *
306 * @return The current value of the coordinate.
307 */
getValue(const SimTK::State & s) const308 double Coordinate::getValue(const SimTK::State& s) const
309 {
310 return _model->getMatterSubsystem().getMobilizedBody(_bodyIndex).getOneQ(s,_mobilizerQIndex);
311 }
312 //done_____________________________________________________________________________
313 /**
314 * Set the value.
315 *
316 * @param aValue value to change to.
317 */
setValue(SimTK::State & s,double aValue,bool enforceConstraints) const318 void Coordinate::setValue(SimTK::State& s, double aValue , bool enforceConstraints) const
319 {
320 // If enforceConstraints is true and coordinate is clamped, clamp aValue into range
321 if (enforceConstraints && getClamped(s)) {
322 if (aValue < get_range(0))
323 aValue = get_range(0);
324 else if (aValue > get_range(1))
325 aValue = get_range(1);
326 }
327
328 // If the coordinate is locked and aValue is not the current value, print an error.
329 // Otherwise, set the value to aValue.
330 if (getLocked(s)) {
331 if (aValue != getValue(s) && !_lockedWarningGiven){
332 cout<<"Coordinate.setValue: WARN- coordinate "<<getName()<<" is locked. Unable to change its value." << endl;
333 _lockedWarningGiven=true;
334 }
335 } else {
336 _model->updMatterSubsystem().getMobilizedBody(_bodyIndex).setOneQ(s,_mobilizerQIndex,aValue);
337 }
338
339 // The Q that was set might not satisfy constraints, so if enforceConstraints then call model assemble.
340 // You want to do this even if the coordinate is locked and its value hasn't changed, because this may be
341 // the last setValue() call in a string of them (e.g., to set a model pose), in which case you only try to
342 // enforce constraints during the last call.
343 if (enforceConstraints) {
344 if (_model->getConstraintSet().getSize()>0 || isConstrained(s)){
345 // if this coordinate is set up to be dependent on other coordinates
346 // its value should be dictated by the other coordinates and not its present value
347 double weight = isDependent(s) ? 0.0 : 10;
348 // assemble model so that states satisfy ALL constraints
349 _model->assemble(s, this, weight);
350 }
351 else
352 _model->getMultibodySystem().realize(s, Stage::Position );
353 }
354 }
355
getSpeedValue(const SimTK::State & s) const356 double Coordinate::getSpeedValue(const SimTK::State& s) const
357 {
358 return _model->getMatterSubsystem().getMobilizedBody(_bodyIndex).getOneU(s,_mobilizerQIndex);
359 }
360
361
setSpeedValue(SimTK::State & s,double aValue) const362 void Coordinate::setSpeedValue(SimTK::State& s, double aValue) const
363 {
364 _model->updMatterSubsystem().getMobilizedBody(_bodyIndex).setOneU(s,_mobilizerQIndex,aValue);
365 }
366
getSpeedName() const367 const std::string& Coordinate::getSpeedName() const
368 {
369 return _speedName;
370 }
371
getAccelerationValue(const SimTK::State & s) const372 double Coordinate::getAccelerationValue(const SimTK::State& s) const
373 {
374 return getModel().getMatterSubsystem().getMobilizedBody(_bodyIndex).getOneUDot(s, _mobilizerQIndex);
375 }
376
377
378 //_____________________________________________________________________________
379 /**
380 * Set the range min and max.
381 *
382 * @param aRange range min and man to change to.
383 * @return Whether or not the range was changed.
384 */
setRange(double aRange[2])385 void Coordinate::setRange(double aRange[2])
386 {
387 if (aRange[1] >= aRange[0]) {
388 upd_range(0) = aRange[0];
389 upd_range(1) = aRange[1];
390 }
391 else
392 throw Exception("Coordinate::setRange, range is invalid, "
393 "min range value exceeds max.");
394 }
395
396 //_____________________________________________________________________________
397 /**
398 * Set the range min.
399 *
400 * @param aRange range min to change to.
401 * @return Whether or not the range min was changed.
402 */
setRangeMin(double aMin)403 void Coordinate::setRangeMin(double aMin)
404 {
405 upd_range(0) = aMin;
406 }
407
408 //_____________________________________________________________________________
409 /**
410 * Set the range max.
411 *
412 * @param aRange range max to change to.
413 * @return Whether or not the range max was changed.
414 */
setRangeMax(double aMax)415 void Coordinate::setRangeMax(double aMax)
416 {
417 upd_range(1) = aMax;
418 }
419
420 //_____________________________________________________________________________
421 /**
422 * Set the default value.
423 *
424 * @param aDefaultValue new default value to change to.
425 */
setDefaultValue(double aDefaultValue)426 void Coordinate::setDefaultValue(double aDefaultValue)
427 {
428 upd_default_value() = aDefaultValue;
429 }
430
431 //_____________________________________________________________________________
432 /**
433 * Get the prescribed motion function.
434 *
435 * @return const reference to the prescribed motion function.
436 */
getPrescribedFunction() const437 const OpenSim::Function& Coordinate::getPrescribedFunction() const
438 {
439 return get_prescribed_function();
440 }
441
442 //_____________________________________________________________________________
443 /**
444 * Set the prescribed motion function.
445 */
setPrescribedFunction(const OpenSim::Function & function)446 void Coordinate::setPrescribedFunction(const OpenSim::Function& function)
447 {
448 //Optional property so clear out previous function value if any
449 updProperty_prescribed_function().clear();
450 updProperty_prescribed_function().adoptAndAppendValue(function.clone());
451 }
452
453 //_____________________________________________________________________________
454 /**
455 * Determine if the coordinate is dependent on other coordinates or not,
456 * by checking to see whether there is a CoordinateCouplerConstraint relating
457 * it to another coordinate. If so return true, false otherwise.
458 * TODO: note that this will fail to detect any other kind of constraint that
459 * might couple coordinates together.
460 */
isDependent(const SimTK::State & s) const461 bool Coordinate::isDependent(const SimTK::State& s) const
462 {
463 if(get_is_free_to_satisfy_constraints())
464 return true;
465
466 for(int i=0; i<_model->getConstraintSet().getSize(); i++){
467 Constraint& constraint = _model->getConstraintSet().get(i);
468 CoordinateCouplerConstraint* couplerp =
469 dynamic_cast<CoordinateCouplerConstraint*>(&constraint);
470 if(couplerp) {
471 if (couplerp->getDependentCoordinateName() == getName())
472 return couplerp->isEnforced(s);
473 }
474 }
475 return false;
476 }
477 //_____________________________________________________________________________
478 /**
479 * Determine if the coordinate is constrained or not.
480 * Specifically, is locked, prescribed, or completely dependent on other coordinates?
481 * If so return true, false otherwise.
482 */
isConstrained(const SimTK::State & s) const483 bool Coordinate::isConstrained(const SimTK::State& s) const
484 {
485 return (getLocked(s) || isPrescribed(s) || isDependent(s));
486 }
487
488
489 //-----------------------------------------------------------------------------
490 // LOCK
491 //-----------------------------------------------------------------------------
492 //_____________________________________________________________________________
493 /**
494 * Set whether or not this coordinate is locked.
495 * A prescribed constraint is used to lock the joint at the dynamics level.
496 * If lock is applied after clamping or prescribed motion it takes precedence.
497 *
498 * @param aLocked If true the joint is locked; if false the joint is unlocked.
499 */
setLocked(SimTK::State & s,bool aLocked) const500 void Coordinate::setLocked(SimTK::State& s, bool aLocked) const
501 {
502 // Do nothing if the same, but make sure _locked is also up-to-date
503 if(aLocked == getLocked(s)){
504 return;
505 }
506
507 _lockedWarningGiven=false; // reset flag in case needed later
508 SimTK::Constraint *lock = NULL;
509
510 // Get constraint
511 if(_lockedConstraintIndex.isValid()){
512 lock = &_model->updMultibodySystem().updMatterSubsystem().updConstraint(_lockedConstraintIndex);
513 }
514 else{
515 string msg = "Lock constraint for coordinate could not be found.";
516 throw Exception(msg,__FILE__,__LINE__);
517 }
518
519 // Now enable if locked otherwise disable
520 if(aLocked){
521 // Update the locked value of the constraint before locking
522 _lockFunction->setValue(getValue(s));
523 lock->enable(s);
524 //Cannot be locked and have prescribed motion and/or clamping
525 setIsPrescribed(s, false);
526 }
527 else {
528 lock->disable(s);
529 }
530 }
531
532 /**
533 * Get whether or not this coordinate is locked.
534 * Calls the underlying constraint at the dynamics level.
535 *
536 * @return true if the coordinate is locked and false if unlocked.
537 */
getLocked(const SimTK::State & s) const538 bool Coordinate::getLocked(const SimTK::State& s) const
539 {
540 if(_lockedConstraintIndex.isValid()){
541 bool disabled = _model->updMultibodySystem().updMatterSubsystem().getConstraint(_lockedConstraintIndex).isDisabled(s);
542 return !disabled;
543 }
544 else{
545 return get_locked();
546 }
547 }
548
549 //-----------------------------------------------------------------------------
550 // PRESCRIBED MOTION
551 //-----------------------------------------------------------------------------
552 //_____________________________________________________________________________
553 /**
554 * Set whether or not this coordinate is being prescribed.
555 * A prescribed constraint is used specify motion at the dynamics level.
556 * If isPrescribed is set after clamping or locking it takes precedence-
557 * and clamped and locked are false.
558 *
559 * @param isPrescribed If true the coordinate is prescribed; if false not prescribed.
560 */
setIsPrescribed(SimTK::State & s,bool isPrescribed) const561 void Coordinate::setIsPrescribed(SimTK::State& s, bool isPrescribed) const
562 {
563 // Do nothing if the same
564 if(isPrescribed == this->isPrescribed(s) ) return;
565
566 // The underlying SimTK constraint
567 SimTK::Constraint *prescribe = NULL;
568
569 // Get constraint
570 if(_prescribedConstraintIndex.isValid()){
571 //get constraint
572 prescribe = &_model->updMultibodySystem().updMatterSubsystem().updConstraint(_prescribedConstraintIndex);
573 }
574 else{
575 string msg = "Prescribed motion for coordinate not found.";
576 throw Exception(msg,__FILE__,__LINE__);
577 }
578
579 // Now enable if prescribed motion constraint otherwise disable
580 if(isPrescribed){
581 prescribe->enable(s);
582 setLocked(s, false);
583 }
584 else
585 prescribe->disable(s);
586 }
587
isPrescribed(const SimTK::State & s) const588 bool Coordinate::isPrescribed(const SimTK::State& s) const
589 {
590 if(_prescribedConstraintIndex.isValid()){
591 bool disabled = _model->updMultibodySystem().updMatterSubsystem().getConstraint(_prescribedConstraintIndex).isDisabled(s);
592 return !disabled;
593 }
594 else{
595 return get_prescribed();
596 }
597
598 }
599
600 //-----------------------------------------------------------------------------
601 // CLAMP
602 //-----------------------------------------------------------------------------
603 //_____________________________________________________________________________
getClamped(const SimTK::State & s) const604 bool Coordinate::getClamped(const SimTK::State& s) const
605 {
606 return getModelingOption(s, "is_clamped") > 0;
607 }
608
setClamped(SimTK::State & s,bool aLocked) const609 void Coordinate::setClamped(SimTK::State& s, bool aLocked) const
610 {
611 setModelingOption(s, "is_clamped", (int)aLocked);
612 }
613
614 //-----------------------------------------------------------------------------
615 // Coordinate::CoordinateStateVariable
616 //-----------------------------------------------------------------------------
617 double Coordinate::CoordinateStateVariable::
getValue(const SimTK::State & state) const618 getValue(const SimTK::State& state) const
619 {
620 return ((Coordinate *)&getOwner())->getValue(state);
621 }
622
623 void Coordinate::CoordinateStateVariable::
setValue(SimTK::State & state,double value) const624 setValue(SimTK::State& state, double value) const
625 {
626 ((Coordinate *)&getOwner())->setValue(state, value, false);
627 }
628
629 double Coordinate::CoordinateStateVariable::
getDerivative(const SimTK::State & state) const630 getDerivative(const SimTK::State& state) const
631 {
632 //TODO: update to get qdot value from the mobilized body
633 return ((Coordinate *)&getOwner())->getSpeedValue(state);
634 }
635
636
637 void Coordinate::CoordinateStateVariable::
setDerivative(const SimTK::State & state,double deriv) const638 setDerivative(const SimTK::State& state, double deriv) const
639 {
640 string msg = "CoordinateStateVariable::setDerivative() - ERROR \n";
641 msg += "Coordinate derivative (qdot) is computed by the Multibody system.";
642 throw Exception(msg);
643 }
644
645
646 //-----------------------------------------------------------------------------
647 // Coordinate::SpeedStateVariable
648 //-----------------------------------------------------------------------------
649 double Coordinate::SpeedStateVariable::
getValue(const SimTK::State & state) const650 getValue(const SimTK::State& state) const
651 {
652 //TODO: update to get qdot value from the mobilized body
653 return ((Coordinate *)&getOwner())->getSpeedValue(state);
654 }
655
656 void Coordinate::SpeedStateVariable::
setValue(SimTK::State & state,double deriv) const657 setValue(SimTK::State& state, double deriv) const
658 {
659 //TODO: update to set qdot value from the mobilized body
660 ((Coordinate *)&getOwner())->setSpeedValue(state, deriv);
661 }
662
663 double Coordinate::SpeedStateVariable::
getDerivative(const SimTK::State & state) const664 getDerivative(const SimTK::State& state) const
665 {
666 const Coordinate& owner = *((Coordinate *)&getOwner());
667 const MobilizedBody& mb = owner.getModel().getMatterSubsystem()
668 .getMobilizedBody(owner.getBodyIndex());
669
670 return mb.getUDotAsVector(state)[owner.getMobilizerQIndex()];
671 }
672
673 void Coordinate::SpeedStateVariable::
setDerivative(const SimTK::State & state,double deriv) const674 setDerivative(const SimTK::State& state, double deriv) const
675 {
676 string msg = "SpeedStateVariable::setDerivative() - ERROR \n";
677 msg += "Generalized speed derivative (udot) can only be set by the Multibody system.";
678 throw Exception(msg);
679 }
680
681 //=============================================================================
682 // XML Deserialization
683 //=============================================================================
updateFromXMLNode(SimTK::Xml::Element & aNode,int versionNumber)684 void Coordinate::updateFromXMLNode(SimTK::Xml::Element& aNode,
685 int versionNumber)
686 {
687 if (versionNumber < XMLDocument::getLatestVersion()) {
688 if (versionNumber < 30514) {
689 SimTK::Xml::element_iterator iter = aNode.element_begin("motion_type");
690 if (iter != aNode.element_end()) {
691 SimTK::Xml::Element motionTypeElm =
692 SimTK::Xml::Element::getAs(aNode.removeNode(iter));
693 std::string typeName = motionTypeElm.getValue();
694
695 if ((IO::Lowercase(typeName) == "rotational"))
696 _userSpecifiedMotionTypePriorTo40 = Rotational;
697 else if (IO::Lowercase(typeName) == "translational")
698 _userSpecifiedMotionTypePriorTo40 = Translational;
699 else if (IO::Lowercase(typeName) == "coupled")
700 _userSpecifiedMotionTypePriorTo40 = Coupled;
701 else
702 _userSpecifiedMotionTypePriorTo40 = Undefined;
703 }
704 }
705 }
706 Super::updateFromXMLNode(aNode, versionNumber);
707 }
708
getUserSpecifiedMotionTypePriorTo40() const709 const Coordinate::MotionType& Coordinate::getUserSpecifiedMotionTypePriorTo40() const
710 {
711 return _userSpecifiedMotionTypePriorTo40;
712 }
713