FlightGear next
FGFCSComponent.cpp
Go to the documentation of this file.
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
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 "FGFCSComponent.h"
41#include "models/FGFCS.h"
43
44using namespace std;
45
46namespace JSBSim {
47
48/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
49CLASS IMPLEMENTATION
50%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
51
53{
54 Input = Output = delay_time = 0.0;
55 delay = index = 0;
56 ClipMin = ClipMax = new FGRealValue(0.0);
57 clip = cyclic_clip = false;
58 dt = fcs->GetChannelDeltaT();
59
60 PropertyManager = fcs->GetPropertyManager();
61 if (element->GetName() == string("lag_filter")) {
62 Type = "LAG_FILTER";
63 } else if (element->GetName() == string("lead_lag_filter")) {
64 Type = "LEAD_LAG_FILTER";
65 } else if (element->GetName() == string("washout_filter")) {
66 Type = "WASHOUT_FILTER";
67 } else if (element->GetName() == string("second_order_filter")) {
68 Type = "SECOND_ORDER_FILTER";
69 } else if (element->GetName() == string("integrator")) {
70 Type = "INTEGRATOR";
71 } else if (element->GetName() == string("summer")) {
72 Type = "SUMMER";
73 } else if (element->GetName() == string("pure_gain")) {
74 Type = "PURE_GAIN";
75 } else if (element->GetName() == string("scheduled_gain")) {
76 Type = "SCHEDULED_GAIN";
77 } else if (element->GetName() == string("aerosurface_scale")) {
78 Type = "AEROSURFACE_SCALE";
79 } else if (element->GetName() == string("switch")) {
80 Type = "SWITCH";
81 } else if (element->GetName() == string("kinematic")) {
82 Type = "KINEMATIC";
83 } else if (element->GetName() == string("deadband")) {
84 Type = "DEADBAND";
85 } else if (element->GetName() == string("fcs_function")) {
86 Type = "FCS_FUNCTION";
87 } else if (element->GetName() == string("pid")) {
88 Type = "PID";
89 } else if (element->GetName() == string("sensor")) {
90 Type = "SENSOR";
91 } else if (element->GetName() == string("accelerometer")) {
92 Type = "ACCELEROMETER";
93 } else if (element->GetName() == string("magnetometer")) {
94 Type = "MAGNETOMETER";
95 } else if (element->GetName() == string("gyro")) {
96 Type = "GYRO";
97 } else if (element->GetName() == string("actuator")) {
98 Type = "ACTUATOR";
99 } else if (element->GetName() == string("waypoint_heading")) {
100 Type = "WAYPOINT_HEADING";
101 } else if (element->GetName() == string("waypoint_distance")) {
102 Type = "WAYPOINT_DISTANCE";
103 } else if (element->GetName() == string("angle")) {
104 Type = "ANGLE";
105 } else if (element->GetName() == string("distributor")) {
106 Type = "DISTRIBUTOR";
107 } else { // illegal component in this channel
108 Type = "UNKNOWN";
109 }
110
111 Name = element->GetAttributeValue("name");
112
113 Element *init_element = element->FindElement("init");
114 while (init_element) {
115 InitNodes.push_back(new FGPropertyValue(init_element->GetDataLine(),
117 init_element = element->FindNextElement("init");
118 }
119
120 Element *input_element = element->FindElement("input");
121 while (input_element) {
122 InputNodes.push_back(new FGPropertyValue(input_element->GetDataLine(),
124
125 input_element = element->FindNextElement("input");
126 }
127
128 Element *out_elem = element->FindElement("output");
129 while (out_elem) {
130 string output_node_name = out_elem->GetDataLine();
131 bool node_exists = PropertyManager->HasNode(output_node_name);
132 FGPropertyNode* OutputNode = PropertyManager->GetNode( output_node_name, true );
133 if (!OutputNode) {
134 cerr << out_elem->ReadFrom() << " Unable to process property: "
135 << output_node_name << endl;
136 throw(string("Invalid output property name in flight control definition"));
137 }
138 OutputNodes.push_back(OutputNode);
139 // If the node has just been created then it must be initialized to a
140 // sensible value since FGPropertyNode::GetNode() does not take care of
141 // that. If the node was already existing, its current value is kept
142 // unchanged.
143 if (!node_exists)
144 OutputNode->setDoubleValue(Output);
145 out_elem = element->FindNextElement("output");
146 }
147
148 Element* delay_elem = element->FindElement("delay");
149 if ( delay_elem ) {
150 delay_time = delay_elem->GetDataAsNumber();
151 string delayType = delay_elem->GetAttributeValue("type");
152 if (delayType.length() > 0) {
153 if (delayType == "time") {
154 delay = (unsigned int)(delay_time / dt);
155 } else if (delayType == "frames") {
156 delay = (unsigned int)delay_time;
157 } else {
158 cerr << "Unallowed delay type" << endl;
159 }
160 } else {
161 delay = (unsigned int)(delay_time / dt);
162 }
163 output_array.resize(delay);
164 for (unsigned int i=0; i<delay; i++) output_array[i] = 0.0;
165 }
166
167 Element *clip_el = element->FindElement("clipto");
168 if (clip_el) {
169 Element* el = clip_el->FindElement("min");
170 if (!el) {
171 cerr << clip_el->ReadFrom()
172 << "Element <min> is missing, <clipto> is ignored." << endl;
173 return;
174 }
175
177
178 el = clip_el->FindElement("max");
179 if (!el) {
180 cerr << clip_el->ReadFrom()
181 << "Element <max> is missing, <clipto> is ignored." << endl;
182 ClipMin = nullptr;
183 return;
184 }
185
187
188 if (clip_el->GetAttributeValue("type") == "cyclic")
189 cyclic_clip = true;
190
191 clip = true;
192 }
193
194 Debug(0);
195}
196
197//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
198
203
204//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
205
207{
208 index = 0;
209 for (auto &elm: output_array)
210 elm = 0.0;
211}
212
213//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
214
215void FGFCSComponent::CheckInputNodes(size_t MinNodes, size_t MaxNodes, Element* el)
216{
217 size_t num = InputNodes.size();
218
219 if (num < MinNodes) {
220 cerr << el->ReadFrom()
221 << " Not enough <input> nodes are provided" << endl
222 << " Expecting " << MinNodes << " while " << num
223 << " are provided." << endl;
224 throw("Some inputs are missing.");
225 }
226
227 if (num > MaxNodes) {
228 cerr << el->ReadFrom()
229 << " Too many <input> nodes are provided" << endl
230 << " Expecting " << MaxNodes << " while " << num
231 << " are provided." << endl
232 << " The last " << num-MaxNodes << " input nodes will be ignored."
233 << endl;
234 }
235}
236
237//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
238
240{
241 for (auto node: OutputNodes)
242 node->setDoubleValue(Output);
243}
244
245//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
246
248{
249 if (fcs->GetTrimStatus()) {
250 // Update the whole history while trim routines are executing.
251 // Don't want to model delays while calculating a trim solution.
252 std::fill(output_array.begin(), output_array.end(), Output);
253 }
254 else {
256 if ((unsigned int)index == delay-1) index = 0;
257 else index++;
259 }
260}
261
262//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
263
265{
266 if (clip) {
267 double vmin = ClipMin->GetValue();
268 double vmax = ClipMax->GetValue();
269 double range = vmax - vmin;
270
271 if (range < 0.0) {
272 cerr << "Trying to clip with a max value (" << vmax << ") from "
273 << ClipMax->GetName() << " lower than the min value (" << vmin
274 << ") from " << ClipMin->GetName() << "." << endl
275 << "Clipping is ignored." << endl;
276 return;
277 }
278
279 if (cyclic_clip && range != 0.0) {
280 double value = Output - vmin;
281 Output = fmod(value, range) + vmin;
282 if (Output < vmin)
283 Output += range;
284 }
285 else
286 Output = Constrain(vmin, Output, vmax);
287 }
288}
289
290//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
291//
292// The old way of naming FCS components allowed upper or lower case, spaces,
293// etc. but then the names were modified to fit into a property name
294// hierarchy. This was confusing (it wasn't done intentionally - it was a
295// carryover from the early design). We now support the direct naming of
296// properties in the FCS component name attribute. The old way is supported in
297// code at this time, but deprecated.
298
300{
301 string tmp;
302 if (Name.find("/") == string::npos)
303 tmp = "fcs/" + PropertyManager->mkPropertyName(Name, true);
304 else
305 tmp = Name;
306
307 bool node_exists = PropertyManager->HasNode(tmp);
308 FGPropertyNode* node = PropertyManager->GetNode(tmp, true);
309
310 if (node) {
311 OutputNodes.push_back(node);
312 // If the node has just been created then it must be initialized to a
313 // sensible value since FGPropertyNode::GetNode() does not take care of
314 // that. If the node was already existing, its current value is kept
315 // unchanged.
316 if (!node_exists)
317 node->setDoubleValue(Output);
318 }
319 else {
320 cerr << el->ReadFrom()
321 << "Could not get or create property " << tmp << endl;
322 }
323}
324
325//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
326// The bitmasked value choices are as follows:
327// unset: In this case (the default) JSBSim would only print
328// out the normally expected messages, essentially echoing
329// the config files as they are read. If the environment
330// variable is not set, debug_lvl is set to 1 internally
331// 0: This requests JSBSim not to output any messages
332// whatsoever.
333// 1: This value explicity requests the normal JSBSim
334// startup messages
335// 2: This value asks for a message to be printed out when
336// a class is instantiated
337// 4: When this value is set, a message is displayed when a
338// FGModel object executes its Run() method
339// 8: When this value is set, various runtime state variables
340// are printed out periodically
341// 16: When set various parameters are sanity checked and
342// a message is printed out when they go out of bounds
343
345{
346 if (debug_lvl <= 0) return;
347
348 if (debug_lvl & 1) { // Standard console startup message output
349 if (from == 0) {
350 cout << endl << " Loading Component \"" << Name
351 << "\" of type: " << Type << endl;
352
353 if (clip) {
354 cout << " Minimum limit: " << ClipMin->GetName() << endl;
355 cout << " Maximum limit: " << ClipMax->GetName() << endl;
356 }
357 if (delay > 0) cout <<" Frame delay: " << delay
358 << " frames (" << delay*dt << " sec)" << endl;
359 }
360 }
361 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
362 if (from == 0) cout << "Instantiated: FGFCSComponent" << endl;
363 if (from == 1) cout << "Destroyed: FGFCSComponent" << endl;
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}
#define i(x)
const std::string & GetName(void) const
Retrieves the element name.
std::string ReadFrom(void) const
Return a string that contains a description of the location where the current XML element was read fr...
std::string GetDataLine(unsigned int i=0)
Gets a line of data belonging to an element.
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
double GetDataAsNumber(void)
Converts the element data to a number.
Element * FindElement(const std::string &el="")
Searches for a specified element.
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
FGFCSComponent(FGFCS *fcs, Element *el)
Constructor.
std::vector< FGPropertyValue_ptr > InitNodes
std::vector< FGPropertyValue_ptr > InputNodes
void CheckInputNodes(size_t MinNodes, size_t MaxNodes, Element *el)
virtual void Debug(int from)
virtual ~FGFCSComponent()
Destructor.
std::vector< double > output_array
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 constexpr double Constrain(double min, double value, double max)
Constrain a value between a minimum and a maximum value.
Definition FGJSBBase.h:333
static short debug_lvl
Definition FGJSBBase.h:190
Represents a either a real value or a property value.
Class wrapper for property handling.
Represents a property value which can use late binding.
Represents a real value.
Definition FGRealValue.h:58