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
14 #include <simgear/sg_inlines.h>
16 #include "mag_compass.hxx"
17 #include <Main/fg_props.hxx>
18 #include <Main/util.hxx>
21 MagCompass::MagCompass ( SGPropertyNode *node )
24 name("magnetic-compass"),
28 for ( i = 0; i < node->nChildren(); ++i ) {
29 SGPropertyNode *child = node->getChild(i);
30 string cname = child->getName();
31 string cval = child->getStringValue();
32 if ( cname == "name" ) {
34 } else if ( cname == "number" ) {
35 num = child->getIntValue();
37 SG_LOG( SG_INSTR, SG_WARN, "Error in magnetic-compass config logic" );
38 if ( name.length() ) {
39 SG_LOG( SG_INSTR, SG_WARN, "Section = " << name );
45 MagCompass::MagCompass ()
51 MagCompass::~MagCompass ()
59 branch = "/instrumentation/" + name;
61 SGPropertyNode *node = fgGetNode(branch.c_str(), num, true );
62 _serviceable_node = node->getChild("serviceable", 0, true);
64 fgGetNode("/orientation/roll-deg", true);
66 fgGetNode("/orientation/pitch-deg", true);
68 fgGetNode("/orientation/heading-magnetic-deg", true);
70 fgGetNode("/orientation/side-slip-deg", true);
72 fgGetNode("/environment/magnetic-dip-deg", true);
74 fgGetNode("/accelerations/pilot/x-accel-fps_sec", true);
76 fgGetNode("/accelerations/pilot/y-accel-fps_sec", true);
78 fgGetNode("/accelerations/pilot/z-accel-fps_sec", true);
79 _out_node = node->getChild("indicated-heading-deg", 0, true);
84 MagCompass::update (double delta_time_sec)
86 // This is the real magnetic
87 // which would be displayed
88 // if the compass had no errors.
89 //double heading_mag_deg = _heading_node->getDoubleValue();
92 // don't update if the compass
94 if (!_serviceable_node->getBoolValue())
97 // jam on excessive sideslip
98 if (fabs(_beta_node->getDoubleValue()) > 12.0) {
105 Formula for northernly turning error from
106 http://williams.best.vwh.net/compass/node4.html:
109 psi: magnetic heading
110 theta: bank angle (right positive; should be phi here)
111 mu: dip angle (down positive)
113 Hc = atan2(sin(Hm)cos(theta)-tan(mu)sin(theta), cos(Hm))
115 This function changes the variable names to the more common psi
116 for the heading, theta for the pitch, and phi for the roll (and
117 target_deg for Hc). It also modifies the equation to
118 incorporate pitch as well as roll, as suggested by Chris
122 // bank angle (radians)
123 double phi = _roll_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
125 // pitch angle (radians)
126 double theta = _pitch_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
128 // magnetic heading (radians)
129 double psi = _heading_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
131 // magnetic dip (radians)
132 double mu = _dip_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
136 Tilt adjustments for accelerations.
138 The magnitudes of these are totally made up, but in real life,
139 they would depend on the fluid level, the amount of friction,
140 etc. anyway. Basically, the compass float tilts forward for
141 acceleration and backward for deceleration. Tilt about 4
142 degrees (0.07 radians) for every G (32 fps/sec) of
145 TODO: do something with the vertical acceleration.
147 double x_accel_g = _x_accel_node->getDoubleValue() / 32;
148 double y_accel_g = _y_accel_node->getDoubleValue() / 32;
149 //double z_accel_g = _z_accel_node->getDoubleValue() / 32;
151 theta -= 0.07 * x_accel_g;
152 phi -= 0.07 * y_accel_g;
154 ////////////////////////////////////////////////////////////////////
155 // calculate target compass heading degrees
156 ////////////////////////////////////////////////////////////////////
158 // these are expensive: don't repeat
159 double sin_phi = sin(phi);
160 double sin_theta = sin(theta);
161 double sin_mu = sin(mu);
162 double cos_theta = cos(theta);
163 double cos_psi = cos(psi);
164 double cos_mu = cos(mu);
166 double a = cos(phi) * sin(psi) * cos_mu
167 - sin_phi * cos_theta * sin_mu
168 - sin_phi* sin_theta * cos_mu * cos_psi;
170 double b = cos_theta * cos_psi * cos(mu)
171 - sin_theta * sin_mu;
173 // This is the value that the compass
174 // is *trying* to display.
175 double target_deg = atan2(a, b) * SGD_RADIANS_TO_DEGREES;
176 double old_deg = _out_node->getDoubleValue();
178 while ((target_deg - old_deg) > 180.0)
180 while ((target_deg - old_deg) < -180.0)
183 // The compass has a current rate of
184 // rotation -- move the rate of rotation
185 // towards one that will turn the compass
186 // to the correct heading, but lag a bit.
187 // (so that the compass can keep overshooting
189 double error = target_deg - old_deg;
190 _rate_degps = fgGetLowPass(_rate_degps, error, delta_time_sec / 5.0);
191 double indicated_deg = old_deg + _rate_degps * delta_time_sec;
192 SG_NORMALIZE_RANGE(indicated_deg, 0.0, 360.0);
194 // That's it -- set the messed-up heading.
195 _out_node->setDoubleValue(indicated_deg);
198 // end of altimeter.cxx