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