JSBSim Flight Dynamics Model 1.3.0 (09 Apr 2026)
An Open Source Flight Dynamics and Control Software Library in C++
Loading...
Searching...
No Matches
FGSensor.cpp
1/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3 Module: FGSensor.cpp
4 Author: Jon Berndt
5 Date started: 9 July 2005
6
7 ------------- Copyright (C) 2005 -------------
8
9 This program is free software; you can redistribute it and/or modify it under
10 the terms of the GNU Lesser General Public License as published by the Free
11 Software Foundation; either version 2 of the License, or (at your option) any
12 later version.
13
14 This program is distributed in the hope that it will be useful, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
17 details.
18
19 You should have received a copy of the GNU Lesser General Public License along
20 with this program; if not, write to the Free Software Foundation, Inc., 59
21 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23 Further information about the GNU Lesser General Public License can also be
24 found on the world wide web at http://www.gnu.org.
25
26FUNCTIONAL DESCRIPTION
27--------------------------------------------------------------------------------
28
29HISTORY
30--------------------------------------------------------------------------------
31
32%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
33COMMENTS, REFERENCES, and NOTES
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35
36%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37INCLUDES
38%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
39
40#include "FGSensor.h"
41#include "models/FGFCS.h"
42#include "input_output/FGXMLElement.h"
43#include "input_output/FGLog.h"
44
45using namespace std;
46
47namespace JSBSim {
48
49/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50CLASS IMPLEMENTATION
51%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
52
53
54FGSensor::FGSensor(FGFCS* fcs, Element* element)
55 : FGFCSComponent(fcs, element), generator(fcs->GetExec()->GetRandomGenerator())
56{
57 // inputs are read from the base class constructor
58
59 bits = quantized = divisions = 0;
60 PreviousInput = PreviousOutput = 0.0;
61 min = max = bias = gain = noise_variance = lag = drift_rate = drift = span = 0.0;
62 granularity = 0.0;
63 noise_type = 0;
64 fail_low = fail_high = fail_stuck = false;
65
66 Element* quantization_element = element->FindElement("quantization");
67 if ( quantization_element) {
68 if ( quantization_element->FindElement("bits") ) {
69 bits = (int)quantization_element->FindElementValueAsNumber("bits");
70 }
71 divisions = (1<<bits);
72 if ( quantization_element->FindElement("min") ) {
73 min = quantization_element->FindElementValueAsNumber("min");
74 }
75 if ( quantization_element->FindElement("max") ) {
76 max = quantization_element->FindElementValueAsNumber("max");
77 }
78 quant_property = quantization_element->GetAttributeValue("name");
79 span = max - min;
80 granularity = span/divisions;
81 }
82 if ( element->FindElement("bias") ) {
83 bias = element->FindElementValueAsNumber("bias");
84 }
85 if ( element->FindElement("gain") ) {
86 gain = element->FindElementValueAsNumber("gain");
87 }
88 if ( element->FindElement("drift_rate") ) {
89 drift_rate = element->FindElementValueAsNumber("drift_rate");
90 }
91 if ( element->FindElement("lag") ) {
92 lag = element->FindElementValueAsNumber("lag");
93 double denom = 2.00 + dt*lag;
94 ca = dt*lag / denom;
95 cb = (2.00 - dt*lag) / denom;
96 }
97 if ( element->FindElement("noise") ) {
98 noise_variance = element->FindElementValueAsNumber("noise");
99 string variation = element->FindElement("noise")->GetAttributeValue("variation");
100 if (variation == "PERCENT") {
101 NoiseType = ePercent;
102 } else if (variation == "ABSOLUTE") {
103 NoiseType = eAbsolute;
104 } else {
105 NoiseType = ePercent;
106 FGLogging log(LogLevel::ERROR);
107 log << "Unknown noise type in sensor: " << Name
108 << "\n defaulting to PERCENT.\n";
109 }
110 string distribution = element->FindElement("noise")->GetAttributeValue("distribution");
111 if (distribution == "UNIFORM") {
112 DistributionType = eUniform;
113 } else if (distribution == "GAUSSIAN") {
114 DistributionType = eGaussian;
115 } else {
116 DistributionType = eUniform;
117 FGLogging log(LogLevel::ERROR);
118 log << "Unknown random distribution type in sensor: " << Name
119 << "\n defaulting to UNIFORM.\n";
120 }
121 }
122
123 bind(element, fcs->GetPropertyManager().get());
124
125 Debug(0);
126}
127
128//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
129
130FGSensor::~FGSensor()
131{
132 Debug(1);
133}
134
135//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
136
137void FGSensor::ResetPastStates(void)
138{
139 FGFCSComponent::ResetPastStates();
140
141 PreviousOutput = PreviousInput = Output = 0.0;
142}
143
144//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
145
146bool FGSensor::Run(void)
147{
148 Input = InputNodes[0]->getDoubleValue();
149
150 ProcessSensorSignal();
151
152 SetOutput();
153
154 return true;
155}
156
157//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
158
159void FGSensor::ProcessSensorSignal(void)
160{
161 // Degrade signal as specified
162
163 if (!fail_stuck) {
164 Output = Input; // perfect sensor
165
166 if (lag != 0.0) Lag(); // models sensor lag and filter
167 if (noise_variance != 0.0) Noise(); // models noise
168 if (drift_rate != 0.0) Drift(); // models drift over time
169 if (gain != 0.0) Gain(); // models a finite gain
170 if (bias != 0.0) Bias(); // models a finite bias
171
172 if (delay != 0) Delay(); // models system signal transport latencies
173
174 if (fail_low) Output = -HUGE_VAL;
175 if (fail_high) Output = HUGE_VAL;
176
177 if (bits != 0) Quantize(); // models quantization degradation
178
179 Clip();
180 }
181}
182
183//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
184
185void FGSensor::Noise(void)
186{
187 double random_value=0.0;
188
189 if (DistributionType == eUniform)
190 random_value = generator->GetUniformRandomNumber();
191 else
192 random_value = generator->GetNormalRandomNumber();
193
194 switch( NoiseType ) {
195 case ePercent:
196 Output *= (1.0 + noise_variance*random_value);
197 break;
198
199 case eAbsolute:
200 Output += noise_variance*random_value;
201 break;
202 }
203}
204
205//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
206
207void FGSensor::Bias(void)
208{
209 Output += bias;
210}
211
212//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
213
214void FGSensor::Gain(void)
215{
216 Output *= gain;
217}
218
219//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
220
221void FGSensor::Drift(void)
222{
223 drift += drift_rate*dt;
224 Output += drift;
225}
226
227//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
228
229void FGSensor::Quantize(void)
230{
231 if (Output < min) Output = min;
232 if (Output > max) Output = max;
233 double portion = Output - min;
234 quantized = (int)(portion/granularity);
235 Output = quantized*granularity + min;
236}
237
238//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
239
240void FGSensor::Lag(void)
241{
242 // "Output" on the right side of the "=" is the current input
243 Output = ca * (Output + PreviousInput) + PreviousOutput * cb;
244
245 PreviousOutput = Output;
246 PreviousInput = Input;
247}
248
249//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
250
251void FGSensor::bind(Element* el, FGPropertyManager* PropertyManager)
252{
253 string tmp = Name;
254
255 FGFCSComponent::bind(el, PropertyManager);
256
257 if (Name.find("/") == string::npos) {
258 tmp = "fcs/" + PropertyManager->mkPropertyName(Name, true);
259 }
260 const string tmp_low = tmp + "/malfunction/fail_low";
261 const string tmp_high = tmp + "/malfunction/fail_high";
262 const string tmp_stuck = tmp + "/malfunction/fail_stuck";
263 const string tmp_randomseed = tmp + "/randomseed";
264
265 PropertyManager->Tie( tmp_low, this, &FGSensor::GetFailLow, &FGSensor::SetFailLow);
266 PropertyManager->Tie( tmp_high, this, &FGSensor::GetFailHigh, &FGSensor::SetFailHigh);
267 PropertyManager->Tie( tmp_stuck, this, &FGSensor::GetFailStuck, &FGSensor::SetFailStuck);
268 PropertyManager->Tie( tmp_randomseed, this, &FGSensor::GetNoiseRandomSeed, &FGSensor::SetNoiseRandomSeed);
269
270 if (!quant_property.empty()) {
271 if (quant_property.find("/") == string::npos) { // not found
272 string qprop = "fcs/" + PropertyManager->mkPropertyName(quant_property, true);
273 SGPropertyNode* node = PropertyManager->GetNode(qprop, true);
274 if (node->isTied()) {
275 XMLLogException err(el);
276 err << "Property " << tmp << " has already been successfully bound (late).\n";
277 throw err;
278 }
279 else
280 PropertyManager->Tie(qprop, this, &FGSensor::GetQuantized);
281 }
282 }
283}
284
285//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
286// User is supplying a random seed specifically for this sensor to override the
287// random seed used by FGFDMExec.
288
289void FGSensor::SetNoiseRandomSeed(int sr)
290{
291 RandomSeed = sr;
292 generator = std::make_shared<RandomNumberGenerator>(*RandomSeed);
293}
294
295int FGSensor::GetNoiseRandomSeed(void) const
296{
297 if (RandomSeed)
298 return *RandomSeed;
299 else
300 return fcs->GetExec()->SRand();
301}
302
303//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
304// The bitmasked value choices are as follows:
305// unset: In this case (the default) JSBSim would only print
306// out the normally expected messages, essentially echoing
307// the config files as they are read. If the environment
308// variable is not set, debug_lvl is set to 1 internally
309// 0: This requests JSBSim not to output any messages
310// whatsoever.
311// 1: This value explicitly requests the normal JSBSim
312// startup messages
313// 2: This value asks for a message to be printed out when
314// a class is instantiated
315// 4: When this value is set, a message is displayed when a
316// FGModel object executes its Run() method
317// 8: When this value is set, various runtime state variables
318// are printed out periodically
319// 16: When set various parameters are sanity checked and
320// a message is printed out when they go out of bounds
321
322void FGSensor::Debug(int from)
323{
324 if (debug_lvl <= 0) return;
325
326 if (debug_lvl & 1) { // Standard console startup message output
327 FGLogging log(LogLevel::DEBUG);
328 if (from == 0) { // Constructor
329 if (!InputNodes.empty())
330 log << " INPUT: " << InputNodes[0]->GetNameWithSign() << fixed
331 << setprecision(4) << "\n";
332 if (bits != 0) {
333 if (quant_property.empty())
334 log << " Quantized output\n";
335 else
336 log << " Quantized output (property: " << quant_property << ")\n";
337
338 log << " Bits: " << bits << "\n";
339 log << " Min value: " << min << "\n";
340 log << " Max value: " << max << "\n";
341 log << " (span: " << span << ", granularity: " << granularity << ")\n";
342 }
343 if (bias != 0.0) log << " Bias: " << bias << " \n";
344 if (gain != 0.0) log << " Gain: " << gain << " \n";
345 if (drift_rate != 0) log << " Sensor drift rate: " << drift_rate << " \n";
346 if (lag != 0) log << " Sensor lag: " << lag << " \n";
347 if (noise_variance != 0) {
348 if (NoiseType == eAbsolute) {
349 log << " Noise variance (absolute): " << noise_variance << " \n";
350 } else if (NoiseType == ePercent) {
351 log << " Noise variance (percent): " << noise_variance << " \n";
352 } else {
353 log << " Noise variance type is invalid\n";
354 }
355 if (DistributionType == eUniform) {
356 log << " Random noise is uniformly distributed.\n";
357 } else if (DistributionType == eGaussian) {
358 log << " Random noise is gaussian distributed.\n";
359 }
360 }
361 for (auto node: OutputNodes)
362 log << " OUTPUT: " << node->getNameString() << "\n";
363 }
364 }
365 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
366 FGLogging log(LogLevel::DEBUG);
367 if (from == 0) log << "Instantiated: FGSensor\n";
368 if (from == 1) log << "Destroyed: FGSensor\n";
369 }
370 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
371 }
372 if (debug_lvl & 8 ) { // Runtime state variables
373 }
374 if (debug_lvl & 16) { // Sanity checking
375 }
376 if (debug_lvl & 64) {
377 if (from == 0) { // Constructor
378 }
379 }
380}
381}
A node in a property tree.
Definition props.hxx:747
bool isTied() const
Test whether this node is bound to an external data source.
Definition props.hxx:1349
Main namespace for the JSBSim Flight Dynamics Model.
Definition FGFDMExec.cpp:71