JSBSim Flight Dynamics Model  1.1.11 (13 Feb 2022)
An Open Source Flight Dynamics and Control Software Library in C++
FGActuator.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 
3  Module: FGActuator.cpp
4  Author: Jon Berndt
5  Date started: 21 February 2006
6 
7  ------------- Copyright (C) 2007 Jon S. Berndt (jon@jsbsim.org) -------------
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 "FGActuator.h"
41 #include "input_output/FGXMLElement.h"
42 #include "math/FGParameterValue.h"
43 #include "models/FGFCS.h"
44 
45 using namespace std;
46 
47 namespace JSBSim {
48 
49 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50 CLASS IMPLEMENTATION
51 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
52 
53 FGActuator::FGActuator(FGFCS* fcs, Element* element)
54  : FGFCSComponent(fcs, element)
55 {
56  // inputs are read from the base class constructor
57 
58  PreviousOutput = 0.0;
59  PreviousHystOutput = 0.0;
60  PreviousRateLimOutput = 0.0;
61  PreviousLagInput = PreviousLagOutput = 0.0;
62  bias = hysteresis_width = deadband_width = 0.0;
63  lag = nullptr;
64  rate_limit_incr = rate_limit_decr = 0; // no limit
65  fail_zero = fail_hardover = fail_stuck = false;
66  ca = cb = 0.0;
67  initialized = 0;
68  saturated = false;
69 
70  CheckInputNodes(1, 1, element);
71 
72  if ( element->FindElement("deadband_width") ) {
73  deadband_width = element->FindElementValueAsNumber("deadband_width");
74  }
75  if ( element->FindElement("hysteresis_width") ) {
76  hysteresis_width = element->FindElementValueAsNumber("hysteresis_width");
77  }
78 
79  // There can be a single rate limit specified, or increasing and
80  // decreasing rate limits specified, and rate limits can be numeric, or
81  // a property.
82  Element* ratelim_el = element->FindElement("rate_limit");
83  while ( ratelim_el ) {
84  string rate_limit_str = ratelim_el->GetDataLine();
85  FGParameter* rate_limit = new FGParameterValue(rate_limit_str,
86  PropertyManager);
87 
88  if (ratelim_el->HasAttribute("sense")) {
89  string sense = ratelim_el->GetAttributeValue("sense");
90  if (sense.substr(0,4) == "incr")
91  rate_limit_incr = rate_limit;
92  else if (sense.substr(0,4) == "decr")
93  rate_limit_decr = rate_limit;
94  } else {
95  rate_limit_incr = rate_limit;
96  rate_limit_decr = rate_limit;
97  }
98  ratelim_el = element->FindNextElement("rate_limit");
99  }
100 
101  if ( element->FindElement("bias") ) {
102  bias = element->FindElementValueAsNumber("bias");
103  }
104 
105  // Lag if specified can be numeric or a property
106  Element* lag_el = element->FindElement("lag");
107  if ( lag_el ) {
108  string lag_str = lag_el->GetDataLine();
109  lag = new FGParameterValue(lag_str, PropertyManager);
110  InitializeLagCoefficients();
111  }
112 
113  bind(element);
114 
115  Debug(0);
116 }
117 
118 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119 
121 {
122  delete rate_limit_incr;
123  if (rate_limit_decr != rate_limit_incr)
124  delete rate_limit_decr;
125 
126  delete lag;
127 
128  Debug(1);
129 }
130 
131 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
132 
133 void FGActuator::ResetPastStates(void)
134 {
135  FGFCSComponent::ResetPastStates();
136 
137  PreviousOutput = PreviousHystOutput = PreviousRateLimOutput
138  = PreviousLagInput = PreviousLagOutput = Output = 0.0;
139 }
140 
141 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
142 
143 bool FGActuator::Run(void )
144 {
145  Input = InputNodes[0]->getDoubleValue();
146 
147  if( fcs->GetTrimStatus() ) initialized = 0;
148 
149  if (fail_zero) Input = 0;
150  if (fail_hardover) Input = Input < 0.0 ? ClipMin->GetValue() : ClipMax->GetValue();
151 
152  Output = Input; // Perfect actuator. At this point, if no failures are present
153  // and no subsequent lag, limiting, etc. is done, the output
154  // is simply the input. If any further processing is done
155  // (below) such as lag, rate limiting, hysteresis, etc., then
156  // the Input will be further processed and the eventual Output
157  // will be overwritten from this perfect value.
158 
159  if (fail_stuck) {
160  Output = PreviousOutput;
161  } else {
162  if (lag) Lag(); // models actuator lag
163  if (rate_limit_incr != 0 || rate_limit_decr != 0) RateLimit(); // limit the actuator rate
164  if (deadband_width != 0.0) Deadband();
165  if (hysteresis_width != 0.0) Hysteresis();
166  if (bias != 0.0) Bias(); // models a finite bias
167  if (delay != 0) Delay(); // Model transport latency
168  }
169 
170  PreviousOutput = Output; // previous value needed for "stuck" malfunction
171 
172  initialized = 1;
173 
174  Clip();
175 
176  if (clip) {
177  double clipmax = ClipMax->GetValue();
178  saturated = false;
179 
180  if (Output >= clipmax && clipmax != 0)
181  saturated = true;
182  else{
183  double clipmin = ClipMin->GetValue();
184  if (Output <= clipmin && clipmin != 0)
185  saturated = true;
186  }
187  }
188 
189  SetOutput();
190 
191  return true;
192 }
193 
194 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
195 
196 void FGActuator::Bias(void)
197 {
198  Output += bias;
199 }
200 
201 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
202 
203 void FGActuator::Lag(void)
204 {
205  // "Output" on the right side of the "=" is the current frame input
206  // for this Lag filter
207  double input = Output;
208 
209  if (initialized) {
210  // Check if lag value has changed via dynamic property
211  if (lagVal != lag->GetValue())
212  InitializeLagCoefficients();
213  Output = ca * (input + PreviousLagInput) + PreviousLagOutput * cb;
214  }
215 
216  PreviousLagInput = input;
217  PreviousLagOutput = Output;
218 }
219 
220 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
221 
222 void FGActuator::Hysteresis(void)
223 {
224  // Note: this function acts cumulatively on the "Output" parameter. So,
225  // "Output" is - for the purposes of this Hysteresis method - really the input
226  // to the method.
227  double input = Output;
228 
229  if ( initialized ) {
230  if (input > PreviousHystOutput)
231  Output = max(PreviousHystOutput, input-0.5*hysteresis_width);
232  else if (input < PreviousHystOutput)
233  Output = min(PreviousHystOutput, input+0.5*hysteresis_width);
234  }
235 
236  PreviousHystOutput = Output;
237 }
238 
239 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
240 
241 void FGActuator::RateLimit(void)
242 {
243  // Note: this function acts cumulatively on the "Output" parameter. So,
244  // "Output" is - for the purposes of this RateLimit method - really the input
245  // to the method.
246  double input = Output;
247  if ( initialized ) {
248  double delta = input - PreviousRateLimOutput;
249  if (rate_limit_incr) {
250  double rate_limit = rate_limit_incr->GetValue();
251  if (delta > dt * rate_limit)
252  Output = PreviousRateLimOutput + rate_limit * dt;
253  }
254  if (rate_limit_decr) {
255  double rate_limit = -rate_limit_decr->GetValue();
256  if (delta < dt * rate_limit)
257  Output = PreviousRateLimOutput + rate_limit * dt;
258  }
259  }
260  PreviousRateLimOutput = Output;
261 }
262 
263 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
264 
265 void FGActuator::Deadband(void)
266 {
267  // Note: this function acts cumulatively on the "Output" parameter. So,
268  // "Output" is - for the purposes of this Deadband method - really the input
269  // to the method.
270  double input = Output;
271 
272  if (input < -deadband_width/2.0) {
273  Output = (input + deadband_width/2.0);
274  } else if (input > deadband_width/2.0) {
275  Output = (input - deadband_width/2.0);
276  } else {
277  Output = 0.0;
278  }
279 }
280 
281 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
282 
283 void FGActuator::bind(Element* el)
284 {
285  string tmp = Name;
286 
287  FGFCSComponent::bind(el);
288 
289  if (Name.find("/") == string::npos) {
290  tmp = "fcs/" + PropertyManager->mkPropertyName(Name, true);
291  }
292  const string tmp_zero = tmp + "/malfunction/fail_zero";
293  const string tmp_hardover = tmp + "/malfunction/fail_hardover";
294  const string tmp_stuck = tmp + "/malfunction/fail_stuck";
295  const string tmp_sat = tmp + "/saturated";
296 
297  PropertyManager->Tie( tmp_zero, this, &FGActuator::GetFailZero, &FGActuator::SetFailZero);
298  PropertyManager->Tie( tmp_hardover, this, &FGActuator::GetFailHardover, &FGActuator::SetFailHardover);
299  PropertyManager->Tie( tmp_stuck, this, &FGActuator::GetFailStuck, &FGActuator::SetFailStuck);
300  PropertyManager->Tie( tmp_sat, this, &FGActuator::IsSaturated);
301 }
302 
303 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
304 
305 void FGActuator::InitializeLagCoefficients()
306 {
307  lagVal = lag->GetValue();
308  double denom = 2.00 + dt * lagVal;
309  ca = dt * lagVal / denom;
310  cb = (2.00 - dt * lagVal) / denom;
311 }
312 
313 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
314 // The bitmasked value choices are as follows:
315 // unset: In this case (the default) JSBSim would only print
316 // out the normally expected messages, essentially echoing
317 // the config files as they are read. If the environment
318 // variable is not set, debug_lvl is set to 1 internally
319 // 0: This requests JSBSim not to output any messages
320 // whatsoever.
321 // 1: This value explicity requests the normal JSBSim
322 // startup messages
323 // 2: This value asks for a message to be printed out when
324 // a class is instantiated
325 // 4: When this value is set, a message is displayed when a
326 // FGModel object executes its Run() method
327 // 8: When this value is set, various runtime state variables
328 // are printed out periodically
329 // 16: When set various parameters are sanity checked and
330 // a message is printed out when they go out of bounds
331 
332 void FGActuator::Debug(int from)
333 {
334  if (debug_lvl <= 0) return;
335 
336  if (debug_lvl & 1) { // Standard console startup message output
337  if (from == 0) { // Constructor
338  cout << " INPUT: " << InputNodes[0]->GetNameWithSign() << endl;
339 
340  if (!OutputNodes.empty()) {
341  for (auto node: OutputNodes)
342  cout << " OUTPUT: " << node->GetName() << endl;
343  }
344  if (bias != 0.0) cout << " Bias: " << bias << endl;
345  if (rate_limit_incr != 0) {
346  cout << " Increasing rate limit: " << rate_limit_incr->GetName() << endl;
347  }
348  if (rate_limit_decr != 0) {
349  cout << " Decreasing rate limit: " << rate_limit_decr->GetName() << endl;
350  }
351  if (lag != 0) cout << " Actuator lag: " << lag << endl;
352  if (hysteresis_width != 0) cout << " Hysteresis width: " << hysteresis_width << endl;
353  if (deadband_width != 0) cout << " Deadband width: " << deadband_width << endl;
354  }
355  }
356  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
357  if (from == 0) cout << "Instantiated: FGActuator" << endl;
358  if (from == 1) cout << "Destroyed: FGActuator" << endl;
359  }
360  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
361  }
362  if (debug_lvl & 8 ) { // Runtime state variables
363  }
364  if (debug_lvl & 16) { // Sanity checking
365  }
366  if (debug_lvl & 64) {
367  if (from == 0) { // Constructor
368  }
369  }
370 }
371 }
JSBSim::Element::HasAttribute
bool HasAttribute(const std::string &key)
Determines if an element has the supplied attribute.
Definition: FGXMLElement.h:155
JSBSim::Element::GetAttributeValue
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
Definition: FGXMLElement.cpp:260
JSBSim::Element::FindElement
Element * FindElement(const std::string &el="")
Searches for a specified element.
Definition: FGXMLElement.cpp:389
JSBSim::Element::FindElementValueAsNumber
double FindElementValueAsNumber(const std::string &el="")
Searches for the named element and returns the data belonging to it as a number.
Definition: FGXMLElement.cpp:429
JSBSim::FGParameter
Represents various types of parameters.
Definition: FGParameter.h:58
JSBSim::FGFCSComponent
Base class for JSBSim Flight Control System Components.
Definition: FGFCSComponent.h:84
JSBSim::FGActuator::Run
bool Run(void) override
This function processes the input.
Definition: FGActuator.cpp:143
JSBSim::FGActuator::SetFailZero
void SetFailZero(bool set)
This function fails the actuator to zero.
Definition: FGActuator.h:145
JSBSim::FGParameterValue
Represents a either a real value or a property value.
Definition: FGParameterValue.h:63
JSBSim::FGActuator::~FGActuator
~FGActuator()
Destructor.
Definition: FGActuator.cpp:120
JSBSim::Element::GetDataLine
std::string GetDataLine(unsigned int i=0)
Gets a line of data belonging to an element.
Definition: FGXMLElement.cpp:333
JSBSim::FGFCS
Encapsulates the Flight Control System (FCS) functionality.
Definition: FGFCS.h:187
JSBSim::FGPropertyManager::mkPropertyName
std::string mkPropertyName(std::string name, bool lowercase)
Property-ify a name replaces spaces with '-' and, optionally, makes name all lower case.
Definition: FGPropertyManager.cpp:64
JSBSim::Element::FindNextElement
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
Definition: FGXMLElement.cpp:407
JSBSim::Element
Definition: FGXMLElement.h:143
JSBSim::FGPropertyManager::Tie
void Tie(const std::string &name, T *pointer)
Tie a property to an external variable.
Definition: FGPropertyManager.h:449