FlightGear next
FGActuator.cpp
Go to the documentation of this file.
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"
43#include "models/FGFCS.h"
44
45using namespace std;
46
47namespace JSBSim {
48
49/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50CLASS IMPLEMENTATION
51%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
52
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,
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
134{
136
137 PreviousOutput = PreviousHystOutput = PreviousRateLimOutput
138 = PreviousLagInput = PreviousLagOutput = Output = 0.0;
139}
140
141//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
142
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
196void FGActuator::Bias(void)
197{
198 Output += bias;
199}
200
201//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
202
203void 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
222void 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
241void 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
265void 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
283void FGActuator::bind(Element* el)
284{
285 string tmp = Name;
286
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
300 PropertyManager->Tie( tmp_sat, this, &FGActuator::IsSaturated);
301}
302
303//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
304
305void 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
332void 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}
#define min(X, Y)
double FindElementValueAsNumber(const std::string &el="")
Searches for the named element and returns the data belonging to it as a number.
std::string GetDataLine(unsigned int i=0)
Gets a line of data belonging to an element.
bool HasAttribute(const std::string &key)
Determines if an element has the supplied attribute.
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
Element * FindElement(const std::string &el="")
Searches for a specified element.
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
void ResetPastStates(void) override
bool GetFailHardover(void) const
Definition FGActuator.h:150
bool Run(void) override
This function processes the input.
void SetFailHardover(bool set)
Definition FGActuator.h:146
FGActuator(FGFCS *fcs, Element *element)
Constructor.
bool GetFailStuck(void) const
Definition FGActuator.h:151
void SetFailZero(bool set)
This function fails the actuator to zero.
Definition FGActuator.h:145
bool GetFailZero(void) const
Definition FGActuator.h:149
bool IsSaturated(void) const
Definition FGActuator.h:152
void SetFailStuck(bool set)
Definition FGActuator.h:147
~FGActuator()
Destructor.
FGFCSComponent(FGFCS *fcs, Element *el)
Constructor.
std::vector< FGPropertyValue_ptr > InputNodes
void CheckInputNodes(size_t MinNodes, size_t MaxNodes, Element *el)
FGParameter_ptr ClipMax
virtual void ResetPastStates(void)
FGPropertyManager * PropertyManager
virtual void bind(Element *el)
FGParameter_ptr ClipMin
virtual void SetOutput(void)
std::vector< FGPropertyNode_ptr > OutputNodes
static short debug_lvl
Definition FGJSBBase.h:190
Represents a either a real value or a property value.
Represents various types of parameters.
Definition FGParameter.h:59
virtual double GetValue(void) const =0