FlightGear next
inst_vertical_speed_indicator.cxx
Go to the documentation of this file.
1// inst_vertical_speed_indicator.cxx
2// -- Instantaneous VSI (emulation calibrated to standard atmosphere).
3//
4// Started September 2004.
5//
6// Copyright (C) 2004
7//
8// This program is free software; you can redistribute it and/or
9// modify it under the terms of the GNU General Public License as
10// published by the Free Software Foundation; either version 2 of the
11// License, or (at your option) any later version.
12//
13// This program is distributed in the hope that it will be useful, but
14// WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// General Public License for more details.
17//
18// You should have received a copy of the GNU General Public License
19// along with this program; if not, write to the Free Software
20// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21//
22
23#ifdef HAVE_CONFIG_H
24# include "config.h"
25#endif
26
27#include <limits>
28#include <simgear/math/interpolater.hxx>
29
31#include <Main/fg_props.hxx>
32#include <Main/util.hxx>
33
34
35// Altitude based on pressure difference from sea level.
36//
37// See http://www.pdas.com/programs/atmos.f90, the tables match exactly
38// environment.cxx (Standard 1976).
39// Example :
40// - at 27900 m the level is 20 km :
41// geopotential altitude 27.9 x 6369 / ( 27.9 + 6369) = 27.778 km.
42// - deltah = 27.778 - 20 = 7.778 km and tbase 216.65 degK.
43// - delta = 5.403295E-2 x exp( -34.163195 x 7.778 / 216.65 ) = 0.016.
44// - pressure = (1 - delta ) x 29.92 = 29.44 inhg.
45// - to construct the tables, round delta to 3 digits.
46
47// altitude ft, pressure step inHG
48static double pressure_data[][2] = {
49 { 0.00, 3.33 }, // guess !
50 { 2952.76, 3.05 },
51 { 5905.51, 2.81 },
52 { 8858.27, 2.55 },
53 { 11811.02, 2.33 },
54 { 14763.78, 2.13 },
55 { 17716.54, 1.91 },
56 { 20669.29, 1.77 },
57 { 23622.05, 1.58 },
58 { 26574.80, 1.49 },
59 { 29527.56, 1.20 },
60 { 32480.31, 1.14 },
61 { 35433.07, 1.05 },
62 { 38385.83, 0.90 },
63 { 41338.58, 0.80 },
64 { 44291.34, 0.69 },
65 { 47244.09, 0.60 },
66 { 50196.85, 0.51 },
67 { 53149.61, 0.45 },
68 { 56102.36, 0.39 },
69 { 59055.12, 0.33 },
70 { 62007.87, 0.30 },
71 { 64960.63, 0.26 },
72 { 67913.39, 0.21 },
73 { 70866.14, 0.18 },
74 { 73818.90, 0.18 },
75 { 76771.65, 0.15 },
76 { 79724.41, 0.12 },
77 { 82677.17, 0.12 },
78 { 85629.92, 0.09 },
79 { 88582.68, 0.09 },
80 { 91535.43, 0.06 },
81 { 94488.19, 0.06 },
82 { 97440.94, 0.06 },
83 { 100393.70, 0.06 },
84 { -1, -1 }
85};
86
87// pressure difference inHG, altitude ft
88static double altitude_data[][2] = {
89 { 0.00, 0.00 },
90 { 3.05, 2952.76 },
91 { 5.86, 5905.51 },
92 { 8.41, 8858.27 },
93 { 10.74, 11811.02 },
94 { 12.87, 14763.78 },
95 { 14.78, 17716.54 },
96 { 16.55, 20669.29 },
97 { 18.13, 23622.05 },
98 { 19.62, 26574.80 },
99 { 20.82, 29527.56 },
100 { 21.96, 32480.31 },
101 { 23.01, 35433.07 },
102 { 23.91, 38385.83 },
103 { 24.71, 41338.58 },
104 { 25.40, 44291.34 },
105 { 26.00, 47244.09 },
106 { 26.51, 50196.85 },
107 { 26.96, 53149.61 },
108 { 27.35, 56102.36 },
109 { 27.68, 59055.12 },
110 { 27.98, 62007.87 },
111 { 28.24, 64960.63 },
112 { 28.45, 67913.39 },
113 { 28.63, 70866.14 },
114 { 28.81, 73818.90 },
115 { 28.96, 76771.65 },
116 { 29.08, 79724.41 },
117 { 29.20, 82677.17 },
118 { 29.29, 85629.92 },
119 { 29.38, 88582.68 },
120 { 29.44, 91535.43 },
121 { 29.50, 94488.19 },
122 { 29.56, 97440.94 },
123 { 29.62, 100393.70 },
124 { -1, -1 }
125};
126
127
128// SI constants
129#define SEA_LEVEL_INHG 29.92
130
131// A higher number means more responsive.
132#define RESPONSIVENESS 5.0
133
134// External environment
135#define MAX_INHG_PER_S 0.0002
136
137
139 _name(node->getStringValue("name", "inst-vertical-speed-indicator")),
140 _num(node->getIntValue("number", 0)),
141 _internal_pressure_inhg( SEA_LEVEL_INHG ),
142 _internal_sea_inhg( SEA_LEVEL_INHG ),
143 _speed_ft_per_s( 0 ),
144 _pressure_table(new SGInterpTable),
145 _altitude_table(new SGInterpTable)
146{
147 int i;
148 for ( i = 0; pressure_data[i][0] != -1; i++)
149 _pressure_table->addEntry( pressure_data[i][0], pressure_data[i][1] );
150
151 for ( i = 0; altitude_data[i][0] != -1; i++)
152 _altitude_table->addEntry( altitude_data[i][0], altitude_data[i][1] );
153}
154
155
157{
158 delete _pressure_table;
159 delete _altitude_table;
160}
161
162
164{
165 SGPropertyNode *node = fgGetNode("/instrumentation", true)->getChild(_name, _num, true);
166
167 _serviceable_node =
168 node->getNode("serviceable", true);
169 _freeze_node =
170 fgGetNode("/sim/freeze/master", true);
171
172 // A real IVSI is operated by static pressure changes.
173 // It operates like a conventional VSI, except that an internal sensor
174 // detects load factors, to momentarily alters the static pressure
175 // (with lag).
176 // It appears lag free at subsonic speed; at high altitude indication may
177 // be less than 1/3 of actual conditions.
178 _pressure_node =
179 fgGetNode("/environment/pressure-inhg", true);
180 _sea_node =
181 fgGetNode("/environment/pressure-sea-level-inhg", true);
182 _speed_node =
183 node->getNode("indicated-speed-fps", true);
184 _speed_min_node =
185 node->getNode("indicated-speed-fpm", true);
186}
187
189{
190 // Initialize at ambient pressure
191 _internal_pressure_inhg = _pressure_node->getDoubleValue();
192 _speed_ft_per_s = 0.0;
193 _internal_sea_inhg = _sea_node->getDoubleValue();
194}
195
196
198{
199 if (_serviceable_node->getBoolValue())
200 {
201 // avoids hang, when freeze
202 if( !_freeze_node->getBoolValue() && std::numeric_limits<double>::min() < fabs(dt))
203 {
204 double pressure_inhg = _pressure_node->getDoubleValue();
205 double sea_inhg = _sea_node->getDoubleValue();
206
207 // limit effect of external environment
208 double rate_sea_inhg_per_s = ( sea_inhg - _internal_sea_inhg ) / dt;
209
210 if( rate_sea_inhg_per_s > - MAX_INHG_PER_S && rate_sea_inhg_per_s < MAX_INHG_PER_S )
211 {
212 double rate_inhg_per_s = ( pressure_inhg - _internal_pressure_inhg ) / dt;
213
214 // IVSI determines alone the current altitude, without altimeter setting.
215 // Altimeter setting is 29.92 above 10000 or 18000 ft.
216 // Below this level, the slope is slightly wrong.
217 double altitude_ft = _altitude_table->interpolate( SEA_LEVEL_INHG - pressure_inhg );
218 double slope_inhg = _pressure_table->interpolate( altitude_ft );
219
220
221 double last_speed_ft_per_s = _speed_ft_per_s;
222
223 // slope at 900 m
224 _speed_ft_per_s = - rate_inhg_per_s * 2952.75591 / slope_inhg;
225
226 // filter noise
227 _speed_ft_per_s = fgGetLowPass( last_speed_ft_per_s, _speed_ft_per_s,
228 dt * RESPONSIVENESS );
229 }
230
231 _speed_node->setDoubleValue( _speed_ft_per_s );
232 _speed_min_node->setDoubleValue( _speed_ft_per_s * 60.0 );
233
234 // backup
235 _internal_pressure_inhg = pressure_inhg;
236 _internal_sea_inhg = sea_inhg;
237 }
238 }
239}
240
241
242// Register the subsystem.
243#if 0
244SGSubsystemMgr::InstancedRegistrant<InstVerticalSpeedIndicator> registrantInstVerticalSpeedIndicator(
245 SGSubsystemMgr::FDM,
246 {{"instrumentation", SGSubsystemMgr::Dependency::HARD}});
247#endif
248
249// end of inst_vertical_speed_indicator.cxx
#define i(x)
#define RESPONSIVENESS
Definition adf.cxx:31
static double altitude_data[][2]
static double pressure_data[][2]
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27
double fgGetLowPass(double current, double target, double timeratio)
Move a value towards a target.
Definition util.cxx:46