1 // mag_compass.cxx - a magnetic compass.
2 // Written by David Megginson, started 2003.
4 // This file is in the Public Domain and comes with no warranty.
6 // This implementation is derived from an earlier one by Alex Perry,
7 // which appeared in src/Cockpit/steam.cxx
15 #include "mag_compass.hxx"
16 #include <Main/fg_props.hxx>
17 #include <Main/util.hxx>
20 MagCompass::MagCompass ()
26 MagCompass::~MagCompass ()
34 fgGetNode("/instrumentation/magnetic-compass/serviceable", true);
36 fgGetNode("/orientation/heading-deg", true);
38 fgGetNode("/orientation/side-slip-deg", true);
40 fgGetNode("/environment/magnetic-variation-deg", true);
42 fgGetNode("/environment/magnetic-dip-deg", true);
44 fgGetNode("/accelerations/ned/north-accel-fps_sec", true);
46 fgGetNode("/accelerations/ned/east-accel-fps_sec", true);
48 fgGetNode("/accelerations/ned/down-accel-fps_sec", true);
50 fgGetNode("/instrumentation/magnetic-compass/indicated-heading-deg",
55 MagCompass::update (double delta_time_sec)
57 // algorithm from Alex Perry
58 // possibly broken by David Megginson
60 // don't update if it's broken
61 if (!_serviceable_node->getBoolValue())
64 // jam on a sideslip of 12 degrees or more
65 if (fabs(_beta_node->getDoubleValue()) > 12.0) {
67 _error_deg = _heading_node->getDoubleValue() -
68 _out_node->getDoubleValue();
72 double accelN = _north_accel_node->getDoubleValue();
73 double accelE = _east_accel_node->getDoubleValue();
74 double accelU = _down_accel_node->getDoubleValue() - 32.0; // why?
76 // force vector towards magnetic north pole
77 double var = _variation_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
78 double dip = _dip_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
79 double cosdip = cos(dip);
80 double forceN = cosdip * cos(var);
81 double forceE = cosdip * sin(var);
82 double forceU = sin(dip);
84 // rotation is around acceleration axis
85 // (magnitude doesn't matter)
86 double accel = accelN * accelN + accelE * accelE + accelU * accelU;
92 // North marking on compass card
93 double edgeN = cos(_error_deg * SGD_DEGREES_TO_RADIANS);
94 double edgeE = sin(_error_deg * SGD_DEGREES_TO_RADIANS);
97 // apply the force to that edge to get torques
98 double torqueN = edgeE * forceU - edgeU * forceE;
99 double torqueE = edgeU * forceN - edgeN * forceU;
100 double torqueU = edgeN * forceE - edgeE * forceN;
102 // get the component parallel to the axis
103 double torque = (torqueN * accelN +
105 torqueU * accelU) * 5.0 / accel;
107 // the compass has angular momentum,
108 // so apply a torque and wait
109 if (delta_time_sec < 1.0) {
110 _rate_degps = _rate_degps * (1.0 - delta_time_sec) - torque;
111 _error_deg += delta_time_sec * _rate_degps;
113 if (_error_deg > 180.0)
115 else if (_error_deg < -180.0)
118 // Set the indicated heading
119 _out_node->setDoubleValue(_heading_node->getDoubleValue() - _error_deg);
122 // end of altimeter.cxx