JSBSim Flight Dynamics Model  1.0 (02 March 2017)
An Open Source Flight Dynamics and Control Software Library in C++
FGFCSComponent.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 
3  Module: FGFCSComponent.cpp
4  Author: Jon S. Berndt
5  Date started: 11/1999
6 
7  ------------- Copyright (C) 2000 -------------
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 Software
11  Foundation; either version 2 of the License, or (at your option) any later
12  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 with
20  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21  Place - Suite 330, Boston, MA 02111-1307, USA.
22 
23  Further information about the GNU Lesser General Public License can also be found on
24  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 <iostream>
41 #include <cstdlib>
42 
43 #include "FGFCSComponent.h"
44 #include "input_output/FGXMLElement.h"
45 #include "math/FGPropertyValue.h"
46 #include "models/FGFCS.h"
47 
48 using namespace std;
49 
50 namespace JSBSim {
51 
52 IDENT(IdSrc,"$Id: FGFCSComponent.cpp,v 1.42 2016/02/27 16:54:15 bcoconni Exp $");
53 IDENT(IdHdr,ID_FCSCOMPONENT);
54 
55 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
56 CLASS IMPLEMENTATION
57 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
58 
59 FGFCSComponent::FGFCSComponent(FGFCS* _fcs, Element* element) : fcs(_fcs)
60 {
61  Element *input_element,*init_element, *clip_el;
62  Input = Output = clipmin = clipmax = delay_time = 0.0;
63  treenode = 0;
64  delay = index = 0;
65  ClipMinPropertyNode = ClipMaxPropertyNode = 0;
66  clipMinSign = clipMaxSign = 1.0;
67  IsOutput = clip = false;
68  string input,init, clip_string;
69  dt = fcs->GetDt();
70 
71  PropertyManager = fcs->GetPropertyManager();
72  if (element->GetName() == string("lag_filter")) {
73  Type = "LAG_FILTER";
74  } else if (element->GetName() == string("lead_lag_filter")) {
75  Type = "LEAD_LAG_FILTER";
76  } else if (element->GetName() == string("washout_filter")) {
77  Type = "WASHOUT_FILTER";
78  } else if (element->GetName() == string("second_order_filter")) {
79  Type = "SECOND_ORDER_FILTER";
80  } else if (element->GetName() == string("integrator")) {
81  Type = "INTEGRATOR";
82  } else if (element->GetName() == string("summer")) {
83  Type = "SUMMER";
84  } else if (element->GetName() == string("pure_gain")) {
85  Type = "PURE_GAIN";
86  } else if (element->GetName() == string("scheduled_gain")) {
87  Type = "SCHEDULED_GAIN";
88  } else if (element->GetName() == string("aerosurface_scale")) {
89  Type = "AEROSURFACE_SCALE";
90  } else if (element->GetName() == string("switch")) {
91  Type = "SWITCH";
92  } else if (element->GetName() == string("kinematic")) {
93  Type = "KINEMATIC";
94  } else if (element->GetName() == string("deadband")) {
95  Type = "DEADBAND";
96  } else if (element->GetName() == string("fcs_function")) {
97  Type = "FCS_FUNCTION";
98  } else if (element->GetName() == string("pid")) {
99  Type = "PID";
100  } else if (element->GetName() == string("sensor")) {
101  Type = "SENSOR";
102  } else if (element->GetName() == string("accelerometer")) {
103  Type = "ACCELEROMETER";
104  } else if (element->GetName() == string("magnetometer")) {
105  Type = "MAGNETOMETER";
106  } else if (element->GetName() == string("gyro")) {
107  Type = "GYRO";
108  } else if (element->GetName() == string("actuator")) {
109  Type = "ACTUATOR";
110  } else if (element->GetName() == string("waypoint_heading")) {
111  Type = "WAYPOINT_HEADING";
112  } else if (element->GetName() == string("waypoint_distance")) {
113  Type = "WAYPOINT_DISTANCE";
114  } else if (element->GetName() == string("angle")) {
115  Type = "ANGLE";
116  } else if (element->GetName() == string("distributor")) {
117  Type = "DISTRIBUTOR";
118  } else { // illegal component in this channel
119  Type = "UNKNOWN";
120  }
121 
122  Name = element->GetAttributeValue("name");
123 
124  init_element = element->FindElement("init");
125  while (init_element) {
126  init = init_element->GetDataLine();
127  if (init[0] == '-') {
128  InitSigns.push_back(-1.0);
129  init.erase(0,1);
130  } else {
131  InitSigns.push_back( 1.0);
132  }
133 
134  if (PropertyManager->HasNode(init)) {
135  FGPropertyNode* node = PropertyManager->GetNode(init);
136  InitNodes.push_back(new FGPropertyValue( node ));
137  } else {
138  InitNodes.push_back(new FGPropertyValue( init,
139  PropertyManager ));
140  }
141  InitNames.push_back( init );
142 
143  init_element = element->FindNextElement("init");
144  }
145 
146  input_element = element->FindElement("input");
147  while (input_element) {
148  input = input_element->GetDataLine();
149  if (input[0] == '-') {
150  InputSigns.push_back(-1.0);
151  input.erase(0,1);
152  } else {
153  InputSigns.push_back( 1.0);
154  }
155 
156  if (PropertyManager->HasNode(input)) {
157  FGPropertyNode* node = PropertyManager->GetNode(input);
158  InputNodes.push_back(new FGPropertyValue( node ));
159  } else {
160  InputNodes.push_back(new FGPropertyValue( input,
161  PropertyManager ));
162  }
163  InputNames.push_back( input );
164 
165  input_element = element->FindNextElement("input");
166  }
167 
168  Element *out_elem = element->FindElement("output");
169  while (out_elem) {
170  IsOutput = true;
171  string output_node_name = out_elem->GetDataLine();
172  FGPropertyNode* OutputNode = PropertyManager->GetNode( output_node_name, true );
173  OutputNodes.push_back(OutputNode);
174  if (!OutputNode) {
175  cerr << endl << " Unable to process property: " << output_node_name << endl;
176  throw(string("Invalid output property name in flight control definition"));
177  }
178  out_elem = element->FindNextElement("output");
179  }
180 
181  Element* delay_elem = element->FindElement("delay");
182  if ( delay_elem ) {
183  delay_time = delay_elem->GetDataAsNumber();
184  string delayType = delay_elem->GetAttributeValue("type");
185  if (delayType.length() > 0) {
186  if (delayType == "time") {
187  delay = (unsigned int)(delay_time / dt);
188  } else if (delayType == "frames") {
189  delay = (unsigned int)delay_time;
190  } else {
191  cerr << "Unallowed delay type" << endl;
192  }
193  } else {
194  delay = (unsigned int)(delay_time / dt);
195  }
196  output_array.resize(delay);
197  for (unsigned int i=0; i<delay; i++) output_array[i] = 0.0;
198  }
199 
200  clip_el = element->FindElement("clipto");
201  if (clip_el) {
202  clip_string = clip_el->FindElementValue("min");
203  if (!is_number(clip_string)) { // it's a property
204  if (clip_string[0] == '-') {
205  clipMinSign = -1.0;
206  clip_string.erase(0,1);
207  }
208  ClipMinPropertyNode = PropertyManager->GetNode( clip_string );
209  } else {
210  clipmin = clip_el->FindElementValueAsNumber("min");
211  }
212  clip_string = clip_el->FindElementValue("max");
213  if (!is_number(clip_string)) { // it's a property
214  if (clip_string[0] == '-') {
215  clipMaxSign = -1.0;
216  clip_string.erase(0,1);
217  }
218  ClipMaxPropertyNode = PropertyManager->GetNode( clip_string );
219  } else {
220  clipmax = clip_el->FindElementValueAsNumber("max");
221  }
222  clip = true;
223  }
224 
225  Debug(0);
226 }
227 
228 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
229 
231 {
232  Debug(1);
233  for (unsigned int i=0; i<InputNodes.size(); i++) {
234  delete InputNodes[i];
235  }
236 }
237 
238 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
239 
240 void FGFCSComponent::ResetPastStates(void)
241 {
242  index = 0;
243  for (unsigned int i = 0; i < output_array.size(); ++i)
244  output_array[i] = 0.0;
245 }
246 
247 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
248 
249 void FGFCSComponent::SetOutput(void)
250 {
251  for (unsigned int i=0; i<OutputNodes.size(); i++) OutputNodes[i]->setDoubleValue(Output);
252 }
253 
254 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
255 
256 void FGFCSComponent::SetDtForFrameCount(int FrameCount)
257 {
258  dt = fcs->GetDt() * FrameCount;
259 }
260 
261 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
262 
263 void FGFCSComponent::Delay(void)
264 {
265  output_array[index] = Output;
266  if ((unsigned int)index == delay-1) index = 0;
267  else index++;
268  Output = output_array[index];
269 }
270 
271 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
272 
273 void FGFCSComponent::Clip(void)
274 {
275  if (clip) {
276  if (ClipMinPropertyNode != 0) clipmin = clipMinSign*ClipMinPropertyNode->getDoubleValue();
277  if (ClipMaxPropertyNode != 0) clipmax = clipMaxSign*ClipMaxPropertyNode->getDoubleValue();
278  if (Output > clipmax) Output = clipmax;
279  else if (Output < clipmin) Output = clipmin;
280  }
281 }
282 
283 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
284 //
285 // The old way of naming FCS components allowed upper or lower case, spaces,
286 // etc. but then the names were modified to fit into a property name
287 // hierarchy. This was confusing (it wasn't done intentionally - it was a
288 // carryover from the early design). We now support the direct naming of
289 // properties in the FCS component name attribute. The old way is supported in
290 // code at this time, but deprecated.
291 
292 void FGFCSComponent::bind(void)
293 {
294  string tmp;
295  if (Name.find("/") == string::npos) {
296  tmp = "fcs/" + PropertyManager->mkPropertyName(Name, true);
297  } else {
298  tmp = Name;
299  }
300  PropertyManager->Tie( tmp, this, &FGFCSComponent::GetOutput);
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 explicity 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 
322 void FGFCSComponent::Debug(int from)
323 {
324  if (debug_lvl <= 0) return;
325 
326  if (debug_lvl & 1) { // Standard console startup message output
327  if (from == 0) {
328  cout << endl << " Loading Component \"" << Name
329  << "\" of type: " << Type << endl;
330 
331  if (clip) {
332  string propsign;
333 
334  if (ClipMinPropertyNode != 0L) {
335  if (clipMinSign < 0.0) propsign="-";
336  cout << " Minimum limit: " << propsign << ClipMinPropertyNode->GetName() << endl;
337  } else {
338  cout << " Minimum limit: " << clipmin << endl;
339  }
340 
341  propsign="";
342 
343  if (ClipMaxPropertyNode != 0L) {
344  if (clipMaxSign < 0.0) propsign="-";
345  cout << " Maximum limit: " << propsign << ClipMaxPropertyNode->GetName() << endl;
346  } else {
347  cout << " Maximum limit: " << clipmax << endl;
348  }
349  }
350  if (delay > 0) cout <<" Frame delay: " << delay
351  << " frames (" << delay*dt << " sec)" << endl;
352  }
353  }
354  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
355  if (from == 0) cout << "Instantiated: FGFCSComponent" << endl;
356  if (from == 1) cout << "Destroyed: FGFCSComponent" << endl;
357  }
358  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
359  }
360  if (debug_lvl & 8 ) { // Runtime state variables
361  }
362  if (debug_lvl & 16) { // Sanity checking
363  }
364  if (debug_lvl & 64) {
365  if (from == 0) { // Constructor
366  cout << IdSrc << endl;
367  cout << IdHdr << endl;
368  }
369  }
370 }
371 }
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
virtual ~FGFCSComponent()
Destructor.
std::string mkPropertyName(std::string name, bool lowercase)
Property-ify a name replaces spaces with &#39;-&#39; and, optionally, makes name all lower case...
Represents a property value which can use late binding.
Class wrapper for property handling.
STL namespace.
Element * FindElement(const std::string &el="")
Searches for a specified element.
double FindElementValueAsNumber(const std::string &el="")
Searches for the named element and returns the data belonging to it as a number.
void Tie(const std::string &name, bool *pointer, bool useDefault=true)
Tie a property to an external bool variable.
double GetDataAsNumber(void)
Converts the element data to a number.
const std::string & GetName(void) const
Retrieves the element name.
Definition: FGXMLElement.h:186
std::string FindElementValue(const std::string &el="")
Searches for the named element and returns the string data belonging to it.
std::string GetDataLine(unsigned int i=0)
Gets a line of data belonging to an element.
Encapsulates the Flight Control System (FCS) functionality.
Definition: FGFCS.h:193
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.