JSBSim Flight Dynamics Model  1.2.1 (08 Aug 2024)
An Open Source Flight Dynamics and Control Software Library in C++
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 
26 FUNCTIONAL DESCRIPTION
27 --------------------------------------------------------------------------------
28 
29 HISTORY
30 --------------------------------------------------------------------------------
31 
32 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
33 COMMENTS, REFERENCES, and NOTES
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 
36 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37 INCLUDES
38 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
39 
40 #include "FGSensor.h"
41 #include "models/FGFCS.h"
42 #include "input_output/FGXMLElement.h"
43 
44 using namespace std;
45 
46 namespace JSBSim {
47 
48 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
49 CLASS IMPLEMENTATION
50 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
51 
52 
53 FGSensor::FGSensor(FGFCS* fcs, Element* element)
54  : FGFCSComponent(fcs, element), generator(fcs->GetExec()->GetRandomGenerator())
55 {
56  // inputs are read from the base class constructor
57 
58  bits = quantized = divisions = 0;
59  PreviousInput = PreviousOutput = 0.0;
60  min = max = bias = gain = noise_variance = lag = drift_rate = drift = span = 0.0;
61  granularity = 0.0;
62  noise_type = 0;
63  fail_low = fail_high = fail_stuck = false;
64 
65  Element* quantization_element = element->FindElement("quantization");
66  if ( quantization_element) {
67  if ( quantization_element->FindElement("bits") ) {
68  bits = (int)quantization_element->FindElementValueAsNumber("bits");
69  }
70  divisions = (1<<bits);
71  if ( quantization_element->FindElement("min") ) {
72  min = quantization_element->FindElementValueAsNumber("min");
73  }
74  if ( quantization_element->FindElement("max") ) {
75  max = quantization_element->FindElementValueAsNumber("max");
76  }
77  quant_property = quantization_element->GetAttributeValue("name");
78  span = max - min;
79  granularity = span/divisions;
80  }
81  if ( element->FindElement("bias") ) {
82  bias = element->FindElementValueAsNumber("bias");
83  }
84  if ( element->FindElement("gain") ) {
85  gain = element->FindElementValueAsNumber("gain");
86  }
87  if ( element->FindElement("drift_rate") ) {
88  drift_rate = element->FindElementValueAsNumber("drift_rate");
89  }
90  if ( element->FindElement("lag") ) {
91  lag = element->FindElementValueAsNumber("lag");
92  double denom = 2.00 + dt*lag;
93  ca = dt*lag / denom;
94  cb = (2.00 - dt*lag) / denom;
95  }
96  if ( element->FindElement("noise") ) {
97  noise_variance = element->FindElementValueAsNumber("noise");
98  string variation = element->FindElement("noise")->GetAttributeValue("variation");
99  if (variation == "PERCENT") {
100  NoiseType = ePercent;
101  } else if (variation == "ABSOLUTE") {
102  NoiseType = eAbsolute;
103  } else {
104  NoiseType = ePercent;
105  cerr << "Unknown noise type in sensor: " << Name << endl;
106  cerr << " defaulting to PERCENT." << endl;
107  }
108  string distribution = element->FindElement("noise")->GetAttributeValue("distribution");
109  if (distribution == "UNIFORM") {
110  DistributionType = eUniform;
111  } else if (distribution == "GAUSSIAN") {
112  DistributionType = eGaussian;
113  } else {
114  DistributionType = eUniform;
115  cerr << "Unknown random distribution type in sensor: " << Name << endl;
116  cerr << " defaulting to UNIFORM." << endl;
117  }
118  }
119 
120  bind(element, fcs->GetPropertyManager().get());
121 
122  Debug(0);
123 }
124 
125 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
126 
127 FGSensor::~FGSensor()
128 {
129  Debug(1);
130 }
131 
132 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
133 
134 void FGSensor::ResetPastStates(void)
135 {
136  FGFCSComponent::ResetPastStates();
137 
138  PreviousOutput = PreviousInput = Output = 0.0;
139 }
140 
141 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
142 
143 bool FGSensor::Run(void)
144 {
145  Input = InputNodes[0]->getDoubleValue();
146 
147  ProcessSensorSignal();
148 
149  SetOutput();
150 
151  return true;
152 }
153 
154 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
155 
156 void FGSensor::ProcessSensorSignal(void)
157 {
158  // Degrade signal as specified
159 
160  if (!fail_stuck) {
161  Output = Input; // perfect sensor
162 
163  if (lag != 0.0) Lag(); // models sensor lag and filter
164  if (noise_variance != 0.0) Noise(); // models noise
165  if (drift_rate != 0.0) Drift(); // models drift over time
166  if (gain != 0.0) Gain(); // models a finite gain
167  if (bias != 0.0) Bias(); // models a finite bias
168 
169  if (delay != 0) Delay(); // models system signal transport latencies
170 
171  if (fail_low) Output = -HUGE_VAL;
172  if (fail_high) Output = HUGE_VAL;
173 
174  if (bits != 0) Quantize(); // models quantization degradation
175 
176  Clip();
177  }
178 }
179 
180 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
181 
182 void FGSensor::Noise(void)
183 {
184  double random_value=0.0;
185 
186  if (DistributionType == eUniform)
187  random_value = generator->GetUniformRandomNumber();
188  else
189  random_value = generator->GetNormalRandomNumber();
190 
191  switch( NoiseType ) {
192  case ePercent:
193  Output *= (1.0 + noise_variance*random_value);
194  break;
195 
196  case eAbsolute:
197  Output += noise_variance*random_value;
198  break;
199  }
200 }
201 
202 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
203 
204 void FGSensor::Bias(void)
205 {
206  Output += bias;
207 }
208 
209 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
210 
211 void FGSensor::Gain(void)
212 {
213  Output *= gain;
214 }
215 
216 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
217 
218 void FGSensor::Drift(void)
219 {
220  drift += drift_rate*dt;
221  Output += drift;
222 }
223 
224 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
225 
226 void FGSensor::Quantize(void)
227 {
228  if (Output < min) Output = min;
229  if (Output > max) Output = max;
230  double portion = Output - min;
231  quantized = (int)(portion/granularity);
232  Output = quantized*granularity + min;
233 }
234 
235 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
236 
237 void FGSensor::Lag(void)
238 {
239  // "Output" on the right side of the "=" is the current input
240  Output = ca * (Output + PreviousInput) + PreviousOutput * cb;
241 
242  PreviousOutput = Output;
243  PreviousInput = Input;
244 }
245 
246 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
247 
248 void FGSensor::bind(Element* el, FGPropertyManager* PropertyManager)
249 {
250  string tmp = Name;
251 
252  FGFCSComponent::bind(el, PropertyManager);
253 
254  if (Name.find("/") == string::npos) {
255  tmp = "fcs/" + PropertyManager->mkPropertyName(Name, true);
256  }
257  const string tmp_low = tmp + "/malfunction/fail_low";
258  const string tmp_high = tmp + "/malfunction/fail_high";
259  const string tmp_stuck = tmp + "/malfunction/fail_stuck";
260 
261  PropertyManager->Tie( tmp_low, this, &FGSensor::GetFailLow, &FGSensor::SetFailLow);
262  PropertyManager->Tie( tmp_high, this, &FGSensor::GetFailHigh, &FGSensor::SetFailHigh);
263  PropertyManager->Tie( tmp_stuck, this, &FGSensor::GetFailStuck, &FGSensor::SetFailStuck);
264 
265  if (!quant_property.empty()) {
266  if (quant_property.find("/") == string::npos) { // not found
267  string qprop = "fcs/" + PropertyManager->mkPropertyName(quant_property, true);
268  FGPropertyNode* node = PropertyManager->GetNode(qprop, true);
269  if (node->isTied()) {
270  cerr << el->ReadFrom()
271  << "Property " << tmp << " has already been successfully bound (late)." << endl;
272  throw("Failed to bind the property to an existing already tied node.");
273  }
274  else
275  PropertyManager->Tie(qprop, this, &FGSensor::GetQuantized);
276  }
277  }
278 
279 }
280 
281 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
282 // The bitmasked value choices are as follows:
283 // unset: In this case (the default) JSBSim would only print
284 // out the normally expected messages, essentially echoing
285 // the config files as they are read. If the environment
286 // variable is not set, debug_lvl is set to 1 internally
287 // 0: This requests JSBSim not to output any messages
288 // whatsoever.
289 // 1: This value explicity requests the normal JSBSim
290 // startup messages
291 // 2: This value asks for a message to be printed out when
292 // a class is instantiated
293 // 4: When this value is set, a message is displayed when a
294 // FGModel object executes its Run() method
295 // 8: When this value is set, various runtime state variables
296 // are printed out periodically
297 // 16: When set various parameters are sanity checked and
298 // a message is printed out when they go out of bounds
299 
300 void FGSensor::Debug(int from)
301 {
302  if (debug_lvl <= 0) return;
303 
304  if (debug_lvl & 1) { // Standard console startup message output
305  if (from == 0) { // Constructor
306  if (!InputNodes.empty())
307  cout << " INPUT: " << InputNodes[0]->GetNameWithSign() << endl;
308  if (bits != 0) {
309  if (quant_property.empty())
310  cout << " Quantized output" << endl;
311  else
312  cout << " Quantized output (property: " << quant_property << ")" << endl;
313 
314  cout << " Bits: " << bits << endl;
315  cout << " Min value: " << min << endl;
316  cout << " Max value: " << max << endl;
317  cout << " (span: " << span << ", granularity: " << granularity << ")" << endl;
318  }
319  if (bias != 0.0) cout << " Bias: " << bias << endl;
320  if (gain != 0.0) cout << " Gain: " << gain << endl;
321  if (drift_rate != 0) cout << " Sensor drift rate: " << drift_rate << endl;
322  if (lag != 0) cout << " Sensor lag: " << lag << endl;
323  if (noise_variance != 0) {
324  if (NoiseType == eAbsolute) {
325  cout << " Noise variance (absolute): " << noise_variance << endl;
326  } else if (NoiseType == ePercent) {
327  cout << " Noise variance (percent): " << noise_variance << endl;
328  } else {
329  cout << " Noise variance type is invalid" << endl;
330  }
331  if (DistributionType == eUniform) {
332  cout << " Random noise is uniformly distributed." << endl;
333  } else if (DistributionType == eGaussian) {
334  cout << " Random noise is gaussian distributed." << endl;
335  }
336  }
337  for (auto node: OutputNodes)
338  cout << " OUTPUT: " << node->getNameString() << endl;
339  }
340  }
341  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
342  if (from == 0) cout << "Instantiated: FGSensor" << endl;
343  if (from == 1) cout << "Destroyed: FGSensor" << endl;
344  }
345  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
346  }
347  if (debug_lvl & 8 ) { // Runtime state variables
348  }
349  if (debug_lvl & 16) { // Sanity checking
350  }
351  if (debug_lvl & 64) {
352  if (from == 0) { // Constructor
353  }
354  }
355 }
356 }