1 // steam.cxx - Steam Gauge Calculations
3 // Copyright (C) 2000 Alexander Perry - alex.perry@ieee.org
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #if defined( FG_HAVE_NATIVE_SGI_COMPILERS )
27 # include <iostream.h>
32 #include <simgear/constants.h>
33 #include <simgear/math/fg_types.hxx>
34 #include <Main/options.hxx>
35 #include <Main/bfi.hxx>
37 FG_USING_NAMESPACE(std);
43 ////////////////////////////////////////////////////////////////////////
44 // Declare the functions that read the variables
45 ////////////////////////////////////////////////////////////////////////
47 // Anything that reads the BFI directly is not implemented at all!
53 double FGSteam::the_STATIC_inhg = 29.92;
54 double FGSteam::the_ALT_ft = 0.0;
55 double FGSteam::get_ALT_ft() { _CatchUp(); return the_ALT_ft; }
57 double FGSteam::get_ASI_kias() { return FGBFI::getAirspeed(); }
59 double FGSteam::the_VSI_case = 29.92;
60 double FGSteam::the_VSI_fps = 0.0;
61 double FGSteam::get_VSI_fps() { _CatchUp(); return the_VSI_fps; }
63 double FGSteam::the_VACUUM_inhg = 0.0;
64 double FGSteam::get_VACUUM_inhg() { _CatchUp(); return the_VACUUM_inhg; }
66 double FGSteam::get_MH_deg () {
67 return FGBFI::getHeading () - FGBFI::getMagVar ();
69 double FGSteam::get_DG_deg () {
70 return FGBFI::getHeading () - FGBFI::getMagVar ();
73 double FGSteam::get_TC_rad () { return FGBFI::getSideSlip (); }
74 double FGSteam::get_TC_radps () { return FGBFI::getRoll (); }
77 ////////////////////////////////////////////////////////////////////////
78 // Recording the current time
79 ////////////////////////////////////////////////////////////////////////
82 int FGSteam::_UpdatesPending = 9999; /* Forces filter to reset */
85 void FGSteam::update ( int timesteps )
87 _UpdatesPending += timesteps;
91 void FGSteam::set_lowpass ( double *outthe, double inthe, double tc )
95 { /* time went backwards; kill the filter */
98 { /* ignore mildly negative time */
102 { /* Normal mode of operation */
103 (*outthe) = (*outthe) * ( 1.0 - tc )
107 { /* Huge time step; assume filter has settled */
110 { /* Moderate time step; non linear response */
111 double keep = exp ( -tc );
112 printf ( "ARP: Keep is %f\n", keep );
113 (*outthe) = (*outthe) * keep
114 + inthe * ( 1.0 - keep );
120 ////////////////////////////////////////////////////////////////////////
121 // Here the fun really begins
122 ////////////////////////////////////////////////////////////////////////
125 void FGSteam::_CatchUp()
126 { if ( _UpdatesPending != 0 )
127 { double dt = _UpdatesPending * 1.0 / current_options.get_model_hz();
129 double d, the_ENGINE_rpm;
131 Someone has called our update function and
132 it turns out that we are running somewhat behind.
133 Here, we recalculate everything for a 'dt' second step.
136 /**************************
137 This is not actually correct, but provides a
138 scaling capability for the vacuum pump later on.
139 When we have a real engine model, we can ask it.
141 the_ENGINE_rpm = FGBFI::getThrottle() * 26.0;
143 /**************************
144 This is just temporary, until the static source works,
145 so we just filter the actual value by one second to
146 account for the line impedance of the plumbing.
148 set_lowpass ( & the_ALT_ft, FGBFI::getAltitude(), dt );
150 /**************************
151 First, we need to know what the static line is reporting,
152 which is a whole simulation area in itself. For now, we cheat.
154 the_STATIC_inhg = 29.92;
155 i = (int) the_ALT_ft;
157 { the_STATIC_inhg *= 0.707;
160 the_STATIC_inhg *= ( 1.0 - 0.293 * i / 9000.0 );
163 NO alternate static source error (student feature),
164 NO possibility of blockage (instructor feature),
165 NO slip-induced error, important for C172 for example.
168 /**************************
169 The VSI case is a low-pass filter of the static line pressure.
170 The instrument reports the difference, scaled to approx ft.
171 NO option for student to break glass when static source fails.
172 NO capability for a fixed non-zero reading when level.
173 NO capability to have a scaling error of maybe a factor of two.
175 the_VSI_fps = ( the_VSI_case - the_STATIC_inhg )
176 * 10000.0; /* manual scaling factor */
177 set_lowpass ( & the_VSI_case, the_STATIC_inhg, dt/6.0 );
179 /**************************
180 The engine driven vacuum pump is directly attached
181 to the engine shaft, so each engine rotation pumps
182 a fixed volume. The amount of air in that volume
183 is determined by the vacuum line's internal pressure.
184 The instruments are essentially leaking air like
185 a fixed source impedance from atmospheric pressure.
186 The regulator provides a digital limit setting,
187 which is open circuit unless the pressure drop is big.
188 Thus, we can compute the vacuum line pressure directly.
189 We assume that there is negligible reservoir space.
190 NO failure of the pump supported (yet)
192 the_VACUUM_inhg = the_STATIC_inhg *
193 the_ENGINE_rpm / ( the_ENGINE_rpm + 10000.0 );
194 if ( the_VACUUM_inhg > 5.0 )
195 the_VACUUM_inhg = 5.0;
198 > I was merely going to do the engine rpm driven vacuum pump for both
199 > the AI and DG, have the gyros spin down down in power off descents,
200 > have it tumble when you exceed the usual pitch or bank limits,
201 > put in those insidious turning errors ... for now anyway.
204 /**************************
205 Finished updates, now clear the timer
212 ////////////////////////////////////////////////////////////////////////
213 // Everything below is a transient hack; expect it to disappear
214 ////////////////////////////////////////////////////////////////////////
218 #define NAV1_Lat ( 32.0 + 48.94/60.0)
219 #define NAV1_Lon (-117.0 - 08.37/60.0)
220 #define NAV1_Rad 280.0
223 /* MZB stepdown radial */
224 #define NAV2_Lat ( 32.0 + 46.93/60.0)
225 #define NAV2_Lon (-117.0 - 13.53/60.0)
226 #define NAV2_Rad 068.0
228 /* HAILE intersection */
229 #define ADF_Lat ( 32.0 + 46.79/60.0)
230 #define ADF_Lon (-117.0 - 02.70/60.0)
234 double FGSteam::get_HackGS_deg ()
236 if (0==NAV1_LOC) return 0.0;
237 y = 60.0 * ( NAV1_Lat - FGBFI::getLatitude () );
238 x = 60.0 * ( NAV1_Lon - FGBFI::getLongitude() )
239 * cos ( FGBFI::getLatitude () / RAD_TO_DEG );
241 if ( dme > 0.1 ) x = sqrt ( dme ); else x = 0.3;
242 y = FGBFI::getAltitude() - NAV1_Alt;
243 return 3.0 - (y/x) * 60.0 / 6000.0;
247 double FGSteam::get_HackVOR1_deg ()
250 y = 60.0 * ( NAV1_Lat - FGBFI::getLatitude () );
251 x = 60.0 * ( NAV1_Lon - FGBFI::getLongitude() )
252 * cos ( FGBFI::getLatitude () / RAD_TO_DEG );
253 r = atan2 ( x, y ) * RAD_TO_DEG - NAV1_Rad - FGBFI::getMagVar();
254 if (r> 180.0) r-=360.0; else
255 if (r<-180.0) r+=360.0;
256 if ( fabs(r) > 90.0 )
257 r = ( r<0.0 ? -r-180.0 : -r+180.0 );
258 if (NAV1_LOC) r*=5.0;
263 double FGSteam::get_HackVOR2_deg ()
266 y = 60.0 * ( NAV2_Lat - FGBFI::getLatitude () );
267 x = 60.0 * ( NAV2_Lon - FGBFI::getLongitude() )
268 * cos ( FGBFI::getLatitude () / RAD_TO_DEG );
269 r = atan2 ( x, y ) * RAD_TO_DEG - NAV2_Rad - FGBFI::getMagVar();
270 if (r> 180.0) r-=360.0; else
271 if (r<-180.0) r+=360.0;
272 if ( fabs(r) > 90.0 )
273 r = ( r<0.0 ? -r-180.0 : -r+180.0 );
278 double FGSteam::get_HackOBS1_deg ()
283 double FGSteam::get_HackOBS2_deg ()
288 double FGSteam::get_HackADF_deg ()
291 y = 60.0 * ( ADF_Lat - FGBFI::getLatitude () );
292 x = 60.0 * ( ADF_Lon - FGBFI::getLongitude() )
293 * cos ( FGBFI::getLatitude () / RAD_TO_DEG );
294 r = atan2 ( x, y ) * RAD_TO_DEG - FGBFI::getHeading ();