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 ( SGPropertyNode *node )
23 name("magnetic-compass"),
27 for ( i = 0; i < node->nChildren(); ++i ) {
28 SGPropertyNode *child = node->getChild(i);
29 string cname = child->getName();
30 string cval = child->getStringValue();
31 if ( cname == "name" ) {
33 } else if ( cname == "number" ) {
34 num = child->getIntValue();
36 SG_LOG( SG_INSTR, SG_WARN, "Error in magnetic-compass config logic" );
37 if ( name.length() ) {
38 SG_LOG( SG_INSTR, SG_WARN, "Section = " << name );
44 MagCompass::MagCompass ()
50 MagCompass::~MagCompass ()
58 branch = "/instrumentation/" + name;
60 SGPropertyNode *node = fgGetNode(branch.c_str(), num, true );
61 _serviceable_node = node->getChild("serviceable", 0, true);
63 fgGetNode("/orientation/roll-deg", true);
65 fgGetNode("/orientation/pitch-deg", true);
67 fgGetNode("/orientation/heading-magnetic-deg", true);
69 fgGetNode("/orientation/side-slip-deg", true);
71 fgGetNode("/environment/magnetic-dip-deg", true);
73 fgGetNode("/accelerations/ned/north-accel-fps_sec", true);
75 fgGetNode("/accelerations/ned/east-accel-fps_sec", true);
77 fgGetNode("/accelerations/ned/down-accel-fps_sec", true);
78 _out_node = node->getChild("indicated-heading-deg", 0, true);
80 _serviceable_node->setBoolValue(true);
85 MagCompass::update (double delta_time_sec)
87 // don't update if it's broken
88 if (!_serviceable_node->getBoolValue())
92 Formula for northernly turning error from
93 http://williams.best.vwh.net/compass/node4.html:
97 theta: bank angle (right positive; should be phi here)
98 mu: dip angle (down positive)
100 Hc = atan2(sin(Hm)cos(theta)-tan(mu)sin(theta), cos(Hm))
102 This function changes the variable names to the more common
103 psi for the heading, theta for the pitch, and phi for the
104 roll. It also modifies the equation to incorporate pitch
105 as well as roll, as suggested by Chris Metzler.
108 // bank angle (radians)
109 double phi = _roll_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
111 // pitch angle (radians)
112 double theta = _pitch_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
114 // magnetic heading (radians)
115 double psi = _heading_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
117 // magnetic dip (radians)
118 double mu = _dip_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
121 ////////////////////////////////////////////////////////////////////
122 // calculate target compass heading Hc in degrees
123 ////////////////////////////////////////////////////////////////////
125 // these are expensive: don't repeat
126 double sin_phi = sin(phi);
127 double sin_theta = sin(theta);
128 double sin_mu = sin(mu);
129 double cos_theta = cos(theta);
130 double cos_psi = cos(psi);
131 double cos_mu = cos(mu);
133 double a = cos(phi) * sin(psi) * cos_mu
134 - sin_phi * cos_theta * sin_mu
135 - sin_phi* sin_theta * cos_mu * cos_psi;
137 double b = cos_theta * cos_psi * cos(mu)
138 - sin_theta * sin_mu;
140 double Hc = atan2(a, b) * SGD_RADIANS_TO_DEGREES;
147 // TODO add acceleration error
148 // TODO allow binding with excessive dip/sideslip
150 _out_node->setDoubleValue(Hc);
153 // algorithm from Alex Perry
154 // possibly broken by David Megginson
156 // // jam on a sideslip of 12 degrees or more
157 // if (fabs(_beta_node->getDoubleValue()) > 12.0) {
158 // _rate_degps = 0.0;
159 // _error_deg = _heading_node->getDoubleValue() -
160 // _out_node->getDoubleValue();
164 // double accelN = _north_accel_node->getDoubleValue();
165 // double accelE = _east_accel_node->getDoubleValue();
166 // double accelU = _down_accel_node->getDoubleValue() - 32.0; // why?
168 // // force vector towards magnetic north pole
169 // double var = _variation_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
170 // double dip = _dip_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
171 // double cosdip = cos(dip);
172 // double forceN = cosdip * cos(var);
173 // double forceE = cosdip * sin(var);
174 // double forceU = sin(dip);
176 // // rotation is around acceleration axis
177 // // (magnitude doesn't matter)
178 // double accel = accelN * accelN + accelE * accelE + accelU * accelU;
180 // accel = sqrt(accel);
184 // // North marking on compass card
185 // double edgeN = cos(_error_deg * SGD_DEGREES_TO_RADIANS);
186 // double edgeE = sin(_error_deg * SGD_DEGREES_TO_RADIANS);
187 // double edgeU = 0.0;
189 // // apply the force to that edge to get torques
190 // double torqueN = edgeE * forceU - edgeU * forceE;
191 // double torqueE = edgeU * forceN - edgeN * forceU;
192 // double torqueU = edgeN * forceE - edgeE * forceN;
194 // // get the component parallel to the axis
195 // double torque = (torqueN * accelN +
196 // torqueE * accelE +
197 // torqueU * accelU) * 5.0 / accel;
199 // // the compass has angular momentum,
200 // // so apply a torque and wait
201 // if (delta_time_sec < 1.0) {
202 // _rate_degps = _rate_degps * (1.0 - delta_time_sec) - torque;
203 // _error_deg += delta_time_sec * _rate_degps;
205 // if (_error_deg > 180.0)
206 // _error_deg -= 360.0;
207 // else if (_error_deg < -180.0)
208 // _error_deg += 360.0;
210 // // Set the indicated heading
211 // _out_node->setDoubleValue(_heading_node->getDoubleValue() - _error_deg);
214 // end of altimeter.cxx