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);
39 #include "radiostack.hxx"
44 ////////////////////////////////////////////////////////////////////////
45 // Declare the functions that read the variables
46 ////////////////////////////////////////////////////////////////////////
48 // Anything that reads the BFI directly is not implemented at all!
54 double FGSteam::the_STATIC_inhg = 29.92;
55 double FGSteam::the_ALT_ft = 0.0;
56 double FGSteam::get_ALT_ft() { _CatchUp(); return the_ALT_ft; }
58 double FGSteam::get_ASI_kias() { return FGBFI::getAirspeed(); }
60 double FGSteam::the_VSI_case = 29.92;
61 double FGSteam::the_VSI_fps = 0.0;
62 double FGSteam::get_VSI_fps() { _CatchUp(); return the_VSI_fps; }
64 double FGSteam::the_VACUUM_inhg = 0.0;
65 double FGSteam::get_VACUUM_inhg() { _CatchUp(); return the_VACUUM_inhg; }
67 double FGSteam::get_MH_deg () {
68 return FGBFI::getHeading () - FGBFI::getMagVar ();
70 double FGSteam::get_DG_deg () {
71 return FGBFI::getHeading () - FGBFI::getMagVar ();
74 double FGSteam::get_TC_rad () { return FGBFI::getSideSlip (); }
75 double FGSteam::get_TC_radps () { return FGBFI::getRoll (); }
78 ////////////////////////////////////////////////////////////////////////
79 // Recording the current time
80 ////////////////////////////////////////////////////////////////////////
83 int FGSteam::_UpdatesPending = 9999; /* Forces filter to reset */
86 void FGSteam::update ( int timesteps )
88 _UpdatesPending += timesteps;
92 void FGSteam::set_lowpass ( double *outthe, double inthe, double tc )
96 { /* time went backwards; kill the filter */
99 { /* ignore mildly negative time */
103 { /* Normal mode of operation */
104 (*outthe) = (*outthe) * ( 1.0 - tc )
108 { /* Huge time step; assume filter has settled */
111 { /* Moderate time step; non linear response */
112 double keep = exp ( -tc );
113 // printf ( "ARP: Keep is %f\n", keep );
114 (*outthe) = (*outthe) * keep
115 + inthe * ( 1.0 - keep );
121 ////////////////////////////////////////////////////////////////////////
122 // Here the fun really begins
123 ////////////////////////////////////////////////////////////////////////
126 void FGSteam::_CatchUp()
127 { if ( _UpdatesPending != 0 )
128 { double dt = _UpdatesPending * 1.0 / current_options.get_model_hz();
130 double d, the_ENGINE_rpm;
132 Someone has called our update function and
133 it turns out that we are running somewhat behind.
134 Here, we recalculate everything for a 'dt' second step.
137 /**************************
138 This is not actually correct, but provides a
139 scaling capability for the vacuum pump later on.
140 When we have a real engine model, we can ask it.
142 the_ENGINE_rpm = FGBFI::getThrottle() * 26.0;
144 /**************************
145 This is just temporary, until the static source works,
146 so we just filter the actual value by one second to
147 account for the line impedance of the plumbing.
149 set_lowpass ( & the_ALT_ft, FGBFI::getAltitude(), dt );
151 /**************************
152 First, we need to know what the static line is reporting,
153 which is a whole simulation area in itself. For now, we cheat.
155 the_STATIC_inhg = 29.92;
156 i = (int) the_ALT_ft;
158 { the_STATIC_inhg *= 0.707;
161 the_STATIC_inhg *= ( 1.0 - 0.293 * i / 9000.0 );
164 NO alternate static source error (student feature),
165 NO possibility of blockage (instructor feature),
166 NO slip-induced error, important for C172 for example.
169 /**************************
170 The VSI case is a low-pass filter of the static line pressure.
171 The instrument reports the difference, scaled to approx ft.
172 NO option for student to break glass when static source fails.
173 NO capability for a fixed non-zero reading when level.
174 NO capability to have a scaling error of maybe a factor of two.
176 the_VSI_fps = ( the_VSI_case - the_STATIC_inhg )
177 * 10000.0; /* manual scaling factor */
178 set_lowpass ( & the_VSI_case, the_STATIC_inhg, dt/6.0 );
180 /**************************
181 The engine driven vacuum pump is directly attached
182 to the engine shaft, so each engine rotation pumps
183 a fixed volume. The amount of air in that volume
184 is determined by the vacuum line's internal pressure.
185 The instruments are essentially leaking air like
186 a fixed source impedance from atmospheric pressure.
187 The regulator provides a digital limit setting,
188 which is open circuit unless the pressure drop is big.
189 Thus, we can compute the vacuum line pressure directly.
190 We assume that there is negligible reservoir space.
191 NO failure of the pump supported (yet)
193 the_VACUUM_inhg = the_STATIC_inhg *
194 the_ENGINE_rpm / ( the_ENGINE_rpm + 10000.0 );
195 if ( the_VACUUM_inhg > 5.0 )
196 the_VACUUM_inhg = 5.0;
199 > I was merely going to do the engine rpm driven vacuum pump for both
200 > the AI and DG, have the gyros spin down down in power off descents,
201 > have it tumble when you exceed the usual pitch or bank limits,
202 > put in those insidious turning errors ... for now anyway.
205 /**************************
206 Finished updates, now clear the timer
213 ////////////////////////////////////////////////////////////////////////
214 // Everything below is a transient hack; expect it to disappear
215 ////////////////////////////////////////////////////////////////////////
219 #define NAV1_Lat ( 32.0 + 48.94/60.0)
220 #define NAV1_Lon (-117.0 - 08.37/60.0)
221 #define NAV1_Rad 280.0
224 /* MZB stepdown radial */
225 #define NAV2_Lat ( 32.0 + 46.93/60.0)
226 #define NAV2_Lon (-117.0 - 13.53/60.0)
227 #define NAV2_Rad 068.0
229 /* HAILE intersection */
230 #define ADF_Lat ( 32.0 + 46.79/60.0)
231 #define ADF_Lon (-117.0 - 02.70/60.0)
235 double FGSteam::get_HackGS_deg ()
237 if (0==NAV1_LOC) return 0.0;
238 y = 60.0 * ( NAV1_Lat - FGBFI::getLatitude () );
239 x = 60.0 * ( NAV1_Lon - FGBFI::getLongitude() )
240 * cos ( FGBFI::getLatitude () / RAD_TO_DEG );
242 if ( dme > 0.1 ) x = sqrt ( dme ); else x = 0.3;
243 y = FGBFI::getAltitude() - NAV1_Alt;
244 return 3.0 - (y/x) * 60.0 / 6000.0;
248 double FGSteam::get_HackVOR1_deg ()
251 y = 60.0 * ( NAV1_Lat - FGBFI::getLatitude () );
252 x = 60.0 * ( NAV1_Lon - FGBFI::getLongitude() )
253 * cos ( FGBFI::getLatitude () / RAD_TO_DEG );
254 r = atan2 ( x, y ) * RAD_TO_DEG - NAV1_Rad - FGBFI::getMagVar();
255 if (r> 180.0) r-=360.0; else
256 if (r<-180.0) r+=360.0;
257 if ( fabs(r) > 90.0 )
258 r = ( r<0.0 ? -r-180.0 : -r+180.0 );
259 if (NAV1_LOC) r*=5.0;
264 double FGSteam::get_HackVOR2_deg ()
267 y = 60.0 * ( NAV2_Lat - FGBFI::getLatitude () );
268 x = 60.0 * ( NAV2_Lon - FGBFI::getLongitude() )
269 * cos ( FGBFI::getLatitude () / RAD_TO_DEG );
270 r = atan2 ( x, y ) * RAD_TO_DEG - NAV2_Rad - FGBFI::getMagVar();
271 if (r> 180.0) r-=360.0; else
272 if (r<-180.0) r+=360.0;
273 if ( fabs(r) > 90.0 )
274 r = ( r<0.0 ? -r-180.0 : -r+180.0 );
279 double FGSteam::get_HackOBS1_deg ()
284 double FGSteam::get_HackOBS2_deg ()
289 double FGSteam::get_HackADF_deg ()
292 y = 60.0 * ( ADF_Lat - FGBFI::getLatitude () );
293 x = 60.0 * ( ADF_Lon - FGBFI::getLongitude() )
294 * cos ( FGBFI::getLatitude () / RAD_TO_DEG );
295 r = atan2 ( x, y ) * RAD_TO_DEG - FGBFI::getHeading ();