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/pilot/x-accel-fps_sec", true);
75 fgGetNode("/accelerations/pilot/y-accel-fps_sec", true);
77 fgGetNode("/accelerations/pilot/z-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 // This is the real magnetic
88 // heading, which will almost
90 double heading_mag_deg = _heading_node->getDoubleValue();
93 // don't update if it's broken
94 if (!_serviceable_node->getBoolValue())
98 Jam on an excessive sideslip.
100 if (fabs(_beta_node->getDoubleValue()) > 12.0) {
107 Formula for northernly turning error from
108 http://williams.best.vwh.net/compass/node4.html:
111 psi: magnetic heading
112 theta: bank angle (right positive; should be phi here)
113 mu: dip angle (down positive)
115 Hc = atan2(sin(Hm)cos(theta)-tan(mu)sin(theta), cos(Hm))
117 This function changes the variable names to the more common
118 psi for the heading, theta for the pitch, and phi for the
119 roll. It also modifies the equation to incorporate pitch
120 as well as roll, as suggested by Chris Metzler.
123 // bank angle (radians)
124 double phi = _roll_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
126 // pitch angle (radians)
127 double theta = _pitch_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
129 // magnetic heading (radians)
130 double psi = _heading_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
132 // magnetic dip (radians)
133 double mu = _dip_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
137 Tilt adjustments for accelerations.
139 The magnitudes of these are totally made up, but in real life,
140 they would depend on the fluid level, the amount of friction,
141 etc. anyway. Basically, the compass float tilts forward for
142 acceleration and backward for deceleration. Tilt about 4
143 degrees (0.07 radians) for every G (32 fps/sec) of
146 TODO: do something with the vertical acceleration.
148 double x_accel_g = _x_accel_node->getDoubleValue() / 32;
149 double y_accel_g = _y_accel_node->getDoubleValue() / 32;
150 double z_accel_g = _z_accel_node->getDoubleValue() / 32;
152 theta -= 0.07 * x_accel_g;
153 phi -= 0.07 * y_accel_g;
155 ////////////////////////////////////////////////////////////////////
156 // calculate target compass heading Hc in degrees
157 ////////////////////////////////////////////////////////////////////
159 // these are expensive: don't repeat
160 double sin_phi = sin(phi);
161 double sin_theta = sin(theta);
162 double sin_mu = sin(mu);
163 double cos_theta = cos(theta);
164 double cos_psi = cos(psi);
165 double cos_mu = cos(mu);
167 double a = cos(phi) * sin(psi) * cos_mu
168 - sin_phi * cos_theta * sin_mu
169 - sin_phi* sin_theta * cos_mu * cos_psi;
171 double b = cos_theta * cos_psi * cos(mu)
172 - sin_theta * sin_mu;
174 // This is the value that the compass
175 // is *trying* to display, but it
176 // takes time to move there, and because
177 // of momentum, the compass will often
179 double Hc = atan2(a, b) * SGD_RADIANS_TO_DEGREES;
181 _out_node->setDoubleValue(Hc);
184 // end of altimeter.cxx