]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/models/FGFCS.cpp
ac015f9b010301ef6c6e50a331f1a047a018ccb6
[flightgear.git] / src / FDM / JSBSim / models / FGFCS.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3  Module:       FGFCS.cpp 
4  Author:       Jon Berndt
5  Date started: 12/12/98
6  Purpose:      Model the flight controls
7  Called by:    FDMExec
8
9  ------------- Copyright (C) 1999  Jon S. Berndt (jsb@hal-pc.org) -------------
10
11  This program is free software; you can redistribute it and/or modify it under
12  the terms of the GNU Lesser General Public License as published by the Free Software
13  Foundation; either version 2 of the License, or (at your option) any later
14  version.
15
16  This program is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
19  details.
20
21  You should have received a copy of the GNU Lesser General Public License along with
22  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
23  Place - Suite 330, Boston, MA  02111-1307, USA.
24
25  Further information about the GNU Lesser General Public License can also be found on
26  the world wide web at http://www.gnu.org.
27
28 FUNCTIONAL DESCRIPTION
29 --------------------------------------------------------------------------------
30 This class models the flight controls for a specific airplane
31
32 HISTORY
33 --------------------------------------------------------------------------------
34 12/12/98   JSB   Created
35
36 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37 INCLUDES
38 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
39
40 #include "FGFCS.h"
41 #include <FGFDMExec.h>
42 #include <input_output/FGPropertyManager.h>
43 #include <fstream>
44 #include <sstream>
45 #include <iomanip>
46
47 #include <models/flight_control/FGFilter.h>
48 #include <models/flight_control/FGDeadBand.h>
49 #include <models/flight_control/FGGain.h>
50 #include <models/flight_control/FGPID.h>
51 #include <models/flight_control/FGSwitch.h>
52 #include <models/flight_control/FGSummer.h>
53 #include <models/flight_control/FGKinemat.h>
54 #include <models/flight_control/FGFCSFunction.h>
55 #include <models/flight_control/FGSensor.h>
56 #include <models/flight_control/FGActuator.h>
57
58 namespace JSBSim {
59
60 static const char *IdSrc = "$Id$";
61 static const char *IdHdr = ID_FCS;
62
63 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
64 CLASS IMPLEMENTATION
65 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
66
67 FGFCS::FGFCS(FGFDMExec* fdmex) : FGModel(fdmex)
68 {
69   int i;
70   Name = "FGFCS";
71
72   DaCmd = DeCmd = DrCmd = DsCmd = DfCmd = DsbCmd = DspCmd = 0;
73   PTrimCmd = YTrimCmd = RTrimCmd = 0.0;
74   GearCmd = GearPos = 1; // default to gear down
75   LeftBrake = RightBrake = CenterBrake = 0.0;
76   TailhookPos = WingFoldPos = 0.0; 
77
78   bind();
79   for (i=0;i<NForms;i++) {
80     DePos[i] = DaLPos[i] = DaRPos[i] = DrPos[i] = 0.0;
81     DfPos[i] = DsbPos[i] = DspPos[i] = 0.0;
82   }
83
84   Debug(0);
85 }
86
87 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88
89 FGFCS::~FGFCS()
90 {
91   ThrottleCmd.clear();
92   ThrottlePos.clear();
93   MixtureCmd.clear();
94   MixturePos.clear();
95   PropAdvanceCmd.clear();
96   PropAdvance.clear();
97   SteerPosDeg.clear();
98   PropFeatherCmd.clear();
99   PropFeather.clear();
100
101   unsigned int i;
102
103   for (i=0;i<sensors.size();i++) delete sensors[i];
104   sensors.clear();
105   for (i=0;i<APComponents.size();i++) delete APComponents[i];
106   APComponents.clear();
107   for (i=0;i<FCSComponents.size();i++) delete FCSComponents[i];
108   FCSComponents.clear();
109   for (i=0;i<Systems.size();i++) delete Systems[i];
110   Systems.clear();
111
112   for (unsigned int i=0; i<interface_properties.size(); i++) delete interface_properties[i];
113   interface_properties.clear();
114
115   Debug(1);
116 }
117
118 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119
120 bool FGFCS::InitModel(void)
121 {
122   unsigned int i;
123
124   if (!FGModel::InitModel()) return false;
125
126   for (i=0; i<ThrottlePos.size(); i++) ThrottlePos[i] = 0.0;
127   for (i=0; i<MixturePos.size(); i++) MixturePos[i] = 0.0;
128   for (i=0; i<ThrottleCmd.size(); i++) ThrottleCmd[i] = 0.0;
129   for (i=0; i<MixtureCmd.size(); i++) MixtureCmd[i] = 0.0;
130   for (i=0; i<PropAdvance.size(); i++) PropAdvance[i] = 0.0;
131   for (i=0; i<PropFeather.size(); i++) PropFeather[i] = 0.0;
132
133   DaCmd = DeCmd = DrCmd = DsCmd = DfCmd = DsbCmd = DspCmd = 0;
134   PTrimCmd = YTrimCmd = RTrimCmd = 0.0;
135   TailhookPos = WingFoldPos = 0.0;
136
137   for (i=0;i<NForms;i++) {
138     DePos[i] = DaLPos[i] = DaRPos[i] = DrPos[i] = 0.0;
139     DfPos[i] = DsbPos[i] = DspPos[i] = 0.0;
140   }
141
142   for (unsigned int i=0; i<Systems.size(); i++) {
143     if (Systems[i]->GetType() == "LAG" ||
144         Systems[i]->GetType() == "LEAD_LAG" ||
145         Systems[i]->GetType() == "WASHOUT" ||
146         Systems[i]->GetType() == "SECOND_ORDER_FILTER" ||
147         Systems[i]->GetType() == "INTEGRATOR")
148     {
149       ((FGFilter*)Systems[i])->ResetPastStates();
150     } else if (Systems[i]->GetType() == "PID" ) {
151       ((FGPID*)Systems[i])->ResetPastStates();
152     }
153   }
154
155   for (unsigned int i=0; i<FCSComponents.size(); i++) {
156     if (FCSComponents[i]->GetType() == "LAG" ||
157         FCSComponents[i]->GetType() == "LEAD_LAG" ||
158         FCSComponents[i]->GetType() == "WASHOUT" ||
159         FCSComponents[i]->GetType() == "SECOND_ORDER_FILTER" ||
160         FCSComponents[i]->GetType() == "INTEGRATOR")
161     {
162       ((FGFilter*)FCSComponents[i])->ResetPastStates();
163     } else if (FCSComponents[i]->GetType() == "PID" ) {
164       ((FGPID*)FCSComponents[i])->ResetPastStates();
165     }
166   }
167
168   for (unsigned int i=0; i<APComponents.size(); i++) {
169     if (APComponents[i]->GetType() == "LAG" ||
170         APComponents[i]->GetType() == "LEAD_LAG" ||
171         APComponents[i]->GetType() == "WASHOUT" ||
172         APComponents[i]->GetType() == "SECOND_ORDER_FILTER" ||
173         APComponents[i]->GetType() == "INTEGRATOR")
174     {
175       ((FGFilter*)APComponents[i])->ResetPastStates();
176     } else if (APComponents[i]->GetType() == "PID" ) {
177       ((FGPID*)APComponents[i])->ResetPastStates();
178     }
179   }
180
181   return true;
182 }
183   
184 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
185 // Notes: In this logic the default engine commands are set. This is simply a
186 // sort of safe-mode method in case the user has not defined control laws for
187 // throttle, mixture, and prop-advance. The throttle, mixture, and prop advance
188 // positions are set equal to the respective commands. Any control logic that is
189 // actually present in the flight_control or autopilot section will override
190 // these simple assignments.
191
192 bool FGFCS::Run(void)
193 {
194   unsigned int i;
195
196   if (FGModel::Run()) return true; // fast exit if nothing to do
197   if (FDMExec->Holding()) return false;
198
199   for (i=0; i<ThrottlePos.size(); i++) ThrottlePos[i] = ThrottleCmd[i];
200   for (i=0; i<MixturePos.size(); i++) MixturePos[i] = MixtureCmd[i];
201   for (i=0; i<PropAdvance.size(); i++) PropAdvance[i] = PropAdvanceCmd[i];
202   for (i=0; i<PropFeather.size(); i++) PropFeather[i] = PropFeatherCmd[i];
203
204   // Set the default steering angle
205   for (i=0; i<SteerPosDeg.size(); i++) {
206     FGLGear* gear = GroundReactions->GetGearUnit(i);
207     SteerPosDeg[i] = gear->GetDefaultSteerAngle( GetDsCmd() );
208   }
209
210   // Cycle through the sensor, systems, autopilot, and flight control components
211   // Execute Sensors
212   for (i=0; i<sensors.size(); i++) sensors[i]->Run();
213
214   // Execute Systems in order
215   for (i=0; i<Systems.size(); i++) Systems[i]->Run();
216
217   // Execute Autopilot
218   for (i=0; i<APComponents.size(); i++) APComponents[i]->Run();
219
220   // Execute Flight Control System
221   for (i=0; i<FCSComponents.size(); i++) FCSComponents[i]->Run();
222
223   return false;
224 }
225
226 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
227
228 void FGFCS::SetDaLPos( int form , double pos )
229 {
230   switch(form) {
231   case ofRad:
232     DaLPos[ofRad] = pos;
233     DaLPos[ofDeg] = pos*radtodeg;
234     break;
235   case ofDeg:
236     DaLPos[ofRad] = pos*degtorad;
237     DaLPos[ofDeg] = pos;
238     break;
239   case ofNorm:
240     DaLPos[ofNorm] = pos;
241   }
242   DaLPos[ofMag] = fabs(DaLPos[ofRad]);
243 }
244
245 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
246
247 void FGFCS::SetDaRPos( int form , double pos )
248 {
249   switch(form) {
250   case ofRad:
251     DaRPos[ofRad] = pos;
252     DaRPos[ofDeg] = pos*radtodeg;
253     break;
254   case ofDeg:
255     DaRPos[ofRad] = pos*degtorad;
256     DaRPos[ofDeg] = pos;
257     break;
258   case ofNorm:
259     DaRPos[ofNorm] = pos;
260   }
261   DaRPos[ofMag] = fabs(DaRPos[ofRad]);
262 }
263
264 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
265
266 void FGFCS::SetDePos( int form , double pos )
267 {
268   switch(form) {
269   case ofRad:
270     DePos[ofRad] = pos;
271     DePos[ofDeg] = pos*radtodeg;
272     break;
273   case ofDeg:
274     DePos[ofRad] = pos*degtorad;
275     DePos[ofDeg] = pos;
276     break;
277   case ofNorm:
278     DePos[ofNorm] = pos;
279   }
280   DePos[ofMag] = fabs(DePos[ofRad]);
281 }
282
283 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
284
285 void FGFCS::SetDrPos( int form , double pos )
286 {
287   switch(form) {
288   case ofRad:
289     DrPos[ofRad] = pos;
290     DrPos[ofDeg] = pos*radtodeg;
291     break;
292   case ofDeg:
293     DrPos[ofRad] = pos*degtorad;
294     DrPos[ofDeg] = pos;
295     break;
296   case ofNorm:
297     DrPos[ofNorm] = pos;
298   }
299   DrPos[ofMag] = fabs(DrPos[ofRad]);
300 }
301
302 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
303
304 void FGFCS::SetDfPos( int form , double pos )
305 {
306   switch(form) {
307   case ofRad:
308     DfPos[ofRad] = pos;
309     DfPos[ofDeg] = pos*radtodeg;
310     break;
311   case ofDeg:
312     DfPos[ofRad] = pos*degtorad;
313     DfPos[ofDeg] = pos;
314     break;
315   case ofNorm:
316     DfPos[ofNorm] = pos;
317   }
318   DfPos[ofMag] = fabs(DfPos[ofRad]);
319 }
320
321 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
322
323 void FGFCS::SetDsbPos( int form , double pos )
324 {
325   switch(form) {
326   case ofRad:
327     DsbPos[ofRad] = pos;
328     DsbPos[ofDeg] = pos*radtodeg;
329     break;
330   case ofDeg:
331     DsbPos[ofRad] = pos*degtorad;
332     DsbPos[ofDeg] = pos;
333     break;
334   case ofNorm:
335     DsbPos[ofNorm] = pos;
336   }
337   DsbPos[ofMag] = fabs(DsbPos[ofRad]);
338 }
339
340 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
341
342 void FGFCS::SetDspPos( int form , double pos )
343 {
344   switch(form) {
345   case ofRad:
346     DspPos[ofRad] = pos;
347     DspPos[ofDeg] = pos*radtodeg;
348     break;
349   case ofDeg:
350     DspPos[ofRad] = pos*degtorad;
351     DspPos[ofDeg] = pos;
352     break;
353   case ofNorm:
354     DspPos[ofNorm] = pos;
355   }
356   DspPos[ofMag] = fabs(DspPos[ofRad]);
357 }
358
359 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
360
361 void FGFCS::SetThrottleCmd(int engineNum, double setting)
362 {
363   unsigned int ctr;
364
365   if (engineNum < (int)ThrottlePos.size()) {
366     if (engineNum < 0) {
367       for (ctr=0;ctr<ThrottleCmd.size();ctr++) ThrottleCmd[ctr] = setting;
368     } else {
369       ThrottleCmd[engineNum] = setting;
370     }
371   } else {
372     cerr << "Throttle " << engineNum << " does not exist! " << ThrottleCmd.size()
373          << " engines exist, but attempted throttle command is for engine "
374          << engineNum << endl;
375   }
376 }
377
378 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
379
380 void FGFCS::SetThrottlePos(int engineNum, double setting)
381 {
382   unsigned int ctr;
383
384   if (engineNum < (int)ThrottlePos.size()) {
385     if (engineNum < 0) {
386       for (ctr=0;ctr<ThrottlePos.size();ctr++) ThrottlePos[ctr] = setting;
387     } else {
388       ThrottlePos[engineNum] = setting;
389     }
390   } else {
391     cerr << "Throttle " << engineNum << " does not exist! " << ThrottlePos.size()
392          << " engines exist, but attempted throttle position setting is for engine "
393          << engineNum << endl;
394   }
395 }
396
397 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
398
399 double FGFCS::GetThrottleCmd(int engineNum) const
400 {
401   if (engineNum < (int)ThrottlePos.size()) {
402     if (engineNum < 0) {
403        cerr << "Cannot get throttle value for ALL engines" << endl;
404     } else {
405       return ThrottleCmd[engineNum];
406     }
407   } else {
408     cerr << "Throttle " << engineNum << " does not exist! " << ThrottleCmd.size()
409          << " engines exist, but throttle setting for engine " << engineNum
410          << " is selected" << endl;
411   }
412   return 0.0;
413 }
414
415 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
416
417 double FGFCS::GetThrottlePos(int engineNum) const
418 {
419   if (engineNum < (int)ThrottlePos.size()) {
420     if (engineNum < 0) {
421        cerr << "Cannot get throttle value for ALL engines" << endl;
422     } else {
423       return ThrottlePos[engineNum];
424     }
425   } else {
426     cerr << "Throttle " << engineNum << " does not exist! " << ThrottlePos.size()
427          << " engines exist, but attempted throttle position setting is for engine "
428          << engineNum << endl;
429   }
430   return 0.0;
431 }
432
433 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
434
435 void FGFCS::SetMixtureCmd(int engineNum, double setting)
436 {
437   unsigned int ctr;
438
439   if (engineNum < (int)ThrottlePos.size()) {
440     if (engineNum < 0) {
441       for (ctr=0;ctr<MixtureCmd.size();ctr++) MixtureCmd[ctr] = setting;
442     } else {
443       MixtureCmd[engineNum] = setting;
444     }
445   }
446 }
447
448 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
449
450 void FGFCS::SetMixturePos(int engineNum, double setting)
451 {
452   unsigned int ctr;
453
454   if (engineNum < (int)ThrottlePos.size()) {
455     if (engineNum < 0) {
456       for (ctr=0;ctr<=MixtureCmd.size();ctr++) MixturePos[ctr] = MixtureCmd[ctr];
457     } else {
458       MixturePos[engineNum] = setting;
459     }
460   }
461 }
462
463 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
464
465 void FGFCS::SetPropAdvanceCmd(int engineNum, double setting)
466 {
467   unsigned int ctr;
468
469   if (engineNum < (int)ThrottlePos.size()) {
470     if (engineNum < 0) {
471       for (ctr=0;ctr<PropAdvanceCmd.size();ctr++) PropAdvanceCmd[ctr] = setting;
472     } else {
473       PropAdvanceCmd[engineNum] = setting;
474     }
475   }
476 }
477
478 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
479
480 void FGFCS::SetPropAdvance(int engineNum, double setting)
481 {
482   unsigned int ctr;
483
484   if (engineNum < (int)ThrottlePos.size()) {
485     if (engineNum < 0) {
486       for (ctr=0;ctr<=PropAdvanceCmd.size();ctr++) PropAdvance[ctr] = PropAdvanceCmd[ctr];
487     } else {
488       PropAdvance[engineNum] = setting;
489     }
490   }
491 }
492
493 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
494
495 void FGFCS::SetFeatherCmd(int engineNum, bool setting)
496 {
497   unsigned int ctr;
498
499   if (engineNum < (int)ThrottlePos.size()) {
500     if (engineNum < 0) {
501       for (ctr=0;ctr<PropFeatherCmd.size();ctr++) PropFeatherCmd[ctr] = setting;
502     } else {
503       PropFeatherCmd[engineNum] = setting;
504     }
505   }
506 }
507
508 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
509
510 void FGFCS::SetPropFeather(int engineNum, bool setting)
511 {
512   unsigned int ctr;
513
514   if (engineNum < (int)ThrottlePos.size()) {
515     if (engineNum < 0) {
516       for (ctr=0;ctr<=PropFeatherCmd.size();ctr++) PropFeather[ctr] = PropFeatherCmd[ctr];
517     } else {
518       PropFeather[engineNum] = setting;
519     }
520   }
521 }
522
523 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
524
525 bool FGFCS::Load(Element* el, SystemType systype)
526 {
527   string name, file, fname="", interface_property_string, parent_name;
528   vector <FGFCSComponent*> *Components;
529   Element *component_element, *property_element, *sensor_element;
530   Element *channel_element;
531
532   Components=0;
533
534   string separator = "/";
535
536 // ToDo: The handling of name and file attributes could be improved, here,
537 //       considering that a name can be in the external file, as well.
538
539   name = el->GetAttributeValue("name");
540
541   if (name.empty()) {
542     fname = el->GetAttributeValue("file");
543     if (systype == stSystem) {
544       file = FindSystemFullPathname(fname);
545     } else { 
546       file = FDMExec->GetFullAircraftPath() + separator + fname + ".xml";
547     }
548     if (fname.empty()) {
549       cerr << "FCS, Autopilot, or system does not appear to be defined inline nor in a file" << endl;
550       return false;
551     } else {
552       document = LoadXMLDocument(file);
553       if (!document) {
554         cerr << "Error loading file " << file << endl;
555         return false;
556       }
557       name = document->GetAttributeValue("name");
558     }
559   } else {
560     document = el;
561   }
562
563   if (document->GetName() == "autopilot") {
564     Components = &APComponents;
565     Name = "Autopilot: " + document->GetAttributeValue("name");
566   } else if (document->GetName() == "flight_control") {
567     Components = &FCSComponents;
568     Name = "FCS: " + document->GetAttributeValue("name");
569   } else if (document->GetName() == "system") {
570     Components = &Systems;
571     Name = "System: " + document->GetAttributeValue("name");
572   }
573   Debug(2);
574
575   if (document->GetName() == "flight_control") bindModel();
576
577   // Interface properties from any autopilot, flight control, or other system are
578   // all stored in the interface properties array.
579
580   property_element = document->FindElement("property");
581   if (property_element) cout << endl << "    Declared properties" << endl << endl;
582   while (property_element) {
583     interface_property_string = property_element->GetDataLine();
584     if (PropertyManager->HasNode(interface_property_string)) {
585       cout << "      Property " << interface_property_string << " is already defined." << endl;
586     } else {
587       double value=0.0;
588       if ( ! property_element->GetAttributeValue("value").empty())
589         value = property_element->GetAttributeValueAsNumber("value");
590       interface_properties.push_back(new double(value));
591       interface_property_string = property_element->GetDataLine();
592       PropertyManager->Tie(interface_property_string, interface_properties.back());
593       cout << "      " << interface_property_string << " (initial value: " << value << ")" << endl;
594     }
595     property_element = document->FindNextElement("property");
596   }
597
598   // After reading interface properties in a file, read properties in the local
599   // flight_control, autopilot, or system element. This allows general-purpose
600   // systems to be defined in a file, with overrides or initial loaded constants
601   // supplied in the relevant element of the aircraft configuration file.
602
603   if (!fname.empty()) {
604     property_element = el->FindElement("property");
605     if (property_element && debug_lvl > 0) cout << endl << "    Overriding properties" << endl << endl;
606     while (property_element) {
607       double value=0.0;
608       if ( ! property_element->GetAttributeValue("value").empty())
609         value = property_element->GetAttributeValueAsNumber("value");
610
611       interface_property_string = property_element->GetDataLine();
612       if (PropertyManager->HasNode(interface_property_string)) {
613         FGPropertyManager* node = PropertyManager->GetNode(interface_property_string);
614         cout << "      " << "Overriding value for property " << interface_property_string
615              << " (old value: " << node->getDoubleValue() << "  new value: " << value << ")" << endl;
616         node->setDoubleValue(value);
617       } else {
618         interface_properties.push_back(new double(value));
619         PropertyManager->Tie(interface_property_string, interface_properties.back());
620         if (debug_lvl > 0)
621           cout << "      " << interface_property_string << " (initial value: " << value << ")" << endl;
622       }
623       
624       property_element = el->FindNextElement("property");
625     }
626   }
627
628   // Any sensor elements that are outside of a channel (in either the autopilot
629   // or the flight_control, or even any possible "system") are placed into the global
630   // "sensors" array, and are executed prior to any autopilot, flight control, or
631   // system.
632
633   sensor_element = document->FindElement("sensor");
634   while (sensor_element) {
635     try {
636       sensors.push_back(new FGSensor(this, sensor_element));
637     } catch (string s) {
638       cerr << highint << fgred << endl << "  " << s << endl;
639       return false;
640     }
641     sensor_element = document->FindNextElement("sensor");
642   }
643
644   channel_element = document->FindElement("channel");
645   while (channel_element) {
646   
647     if (debug_lvl > 0)
648       cout << endl << highint << fgblue << "    Channel " 
649          << normint << channel_element->GetAttributeValue("name") << reset << endl;
650   
651     component_element = channel_element->GetElement();
652     while (component_element) {
653       try {
654         if ((component_element->GetName() == string("lag_filter")) ||
655             (component_element->GetName() == string("lead_lag_filter")) ||
656             (component_element->GetName() == string("washout_filter")) ||
657             (component_element->GetName() == string("second_order_filter")) ||
658             (component_element->GetName() == string("integrator")) )
659         {
660           Components->push_back(new FGFilter(this, component_element));
661         } else if ((component_element->GetName() == string("pure_gain")) ||
662                    (component_element->GetName() == string("scheduled_gain")) ||
663                    (component_element->GetName() == string("aerosurface_scale")))
664         {
665           Components->push_back(new FGGain(this, component_element));
666         } else if (component_element->GetName() == string("summer")) {
667           Components->push_back(new FGSummer(this, component_element));
668         } else if (component_element->GetName() == string("deadband")) {
669           Components->push_back(new FGDeadBand(this, component_element));
670         } else if (component_element->GetName() == string("switch")) {
671           Components->push_back(new FGSwitch(this, component_element));
672         } else if (component_element->GetName() == string("kinematic")) {
673           Components->push_back(new FGKinemat(this, component_element));
674         } else if (component_element->GetName() == string("fcs_function")) {
675           Components->push_back(new FGFCSFunction(this, component_element));
676         } else if (component_element->GetName() == string("pid")) {
677           Components->push_back(new FGPID(this, component_element));
678         } else if (component_element->GetName() == string("actuator")) {
679           Components->push_back(new FGActuator(this, component_element));
680         } else if (component_element->GetName() == string("sensor")) {
681           Components->push_back(new FGSensor(this, component_element));
682         } else {
683           cerr << "Unknown FCS component: " << component_element->GetName() << endl;
684         }
685       } catch(string s) {
686         cerr << highint << fgred << endl << "  " << s << endl;
687         cerr << reset << endl;
688         return false;
689       }
690       component_element = channel_element->GetNextElement();
691     }
692     channel_element = document->FindNextElement("channel");
693   }
694
695   ResetParser();
696
697   return true;
698 }
699
700 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
701
702 double FGFCS::GetBrake(FGLGear::BrakeGroup bg)
703 {
704   switch (bg) {
705   case FGLGear::bgLeft:
706     return LeftBrake;
707   case FGLGear::bgRight:
708     return RightBrake;
709   case FGLGear::bgCenter:
710     return CenterBrake;
711   default:
712     cerr << "GetBrake asked to return a bogus brake value" << endl;
713   }
714   return 0.0;
715 }
716
717 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
718
719 string FGFCS::FindSystemFullPathname(string system_filename)
720 {
721   string fullpath, localpath;
722   string systemPath = FDMExec->GetSystemsPath();
723   string aircraftPath = FDMExec->GetFullAircraftPath();
724   ifstream system_file;
725
726   string separator = "/";
727
728   fullpath = systemPath + separator;
729   localpath = aircraftPath + separator + "Systems" + separator;
730
731   system_file.open(string(fullpath + system_filename + ".xml").c_str());
732   if ( !system_file.is_open()) {
733     system_file.open(string(localpath + system_filename + ".xml").c_str());
734       if ( !system_file.is_open()) {
735         cerr << " Could not open system file: " << system_filename << " in path "
736              << fullpath << " or " << localpath << endl;
737         return string("");
738       } else {
739         return string(localpath + system_filename + ".xml");
740       }
741   }
742   return string(fullpath + system_filename + ".xml");
743 }
744
745 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
746
747 ifstream* FGFCS::FindSystemFile(string system_filename)
748 {
749   string fullpath, localpath;
750   string systemPath = FDMExec->GetSystemsPath();
751   string aircraftPath = FDMExec->GetFullAircraftPath();
752   ifstream* system_file = new ifstream();
753
754   string separator = "/";
755
756   fullpath = systemPath + separator;
757   localpath = aircraftPath + separator + "Systems" + separator;
758
759   system_file->open(string(fullpath + system_filename + ".xml").c_str());
760   if ( !system_file->is_open()) {
761     system_file->open(string(localpath + system_filename + ".xml").c_str());
762       if ( !system_file->is_open()) {
763         cerr << " Could not open system file: " << system_filename << " in path "
764              << fullpath << " or " << localpath << endl;
765       }
766   }
767   return system_file;
768 }
769
770 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
771
772 string FGFCS::GetComponentStrings(string delimeter)
773 {
774   unsigned int comp;
775   string CompStrings = "";
776   bool firstime = true;
777   int total_count=0;
778
779   for (unsigned int i=0; i<Systems.size(); i++) {
780     if (firstime) firstime = false;
781     else          CompStrings += delimeter;
782
783     CompStrings += Systems[i]->GetName();
784     total_count++;
785   }
786
787   for (comp = 0; comp < APComponents.size(); comp++)
788   {
789     if (firstime) firstime = false;
790     else          CompStrings += delimeter;
791
792     CompStrings += APComponents[comp]->GetName();
793     total_count++;
794   }
795
796   for (comp = 0; comp < FCSComponents.size(); comp++) {
797     if (firstime) firstime = false;
798     else          CompStrings += delimeter;
799
800     CompStrings += FCSComponents[comp]->GetName();
801     total_count++;
802   }
803
804   return CompStrings;
805 }
806
807 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
808
809 string FGFCS::GetComponentValues(string delimeter)
810 {
811   std::ostringstream buf;
812
813   unsigned int comp;
814   bool firstime = true;
815   int total_count=0;
816
817   for (unsigned int i=0; i<Systems.size(); i++) {
818     if (firstime) firstime = false;
819     else          buf << delimeter;
820
821     buf << setprecision(9) << Systems[i]->GetOutput();
822     total_count++;
823   }
824
825   for (comp = 0; comp < APComponents.size(); comp++) {
826     if (firstime) firstime = false;
827     else          buf << delimeter;
828
829     buf << setprecision(9) << APComponents[comp]->GetOutput();
830     total_count++;
831   }
832
833   for (comp = 0; comp < FCSComponents.size(); comp++) {
834     if (firstime) firstime = false;
835     else          buf << delimeter;
836
837     buf << setprecision(9) << FCSComponents[comp]->GetOutput();
838     total_count++;
839   }
840
841   return buf.str();
842 }
843
844 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
845
846 void FGFCS::AddThrottle(void)
847 {
848   ThrottleCmd.push_back(0.0);
849   ThrottlePos.push_back(0.0);
850   MixtureCmd.push_back(0.0);     // assume throttle and mixture are coupled
851   MixturePos.push_back(0.0);
852   PropAdvanceCmd.push_back(0.0); // assume throttle and prop pitch are coupled
853   PropAdvance.push_back(0.0);
854   PropFeatherCmd.push_back(false);
855   PropFeather.push_back(false);
856
857   unsigned int num = (unsigned int)ThrottleCmd.size()-1;
858   bindThrottle(num);
859 }
860
861 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
862
863 void FGFCS::AddGear(void)
864 {
865   SteerPosDeg.push_back(0.0);
866 }
867
868 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
869
870 double FGFCS::GetDt(void)
871 {
872   return FDMExec->GetDeltaT()*rate;
873 }
874
875 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
876
877 void FGFCS::bind(void)
878 {
879   PropertyManager->Tie("fcs/aileron-cmd-norm", this, &FGFCS::GetDaCmd, &FGFCS::SetDaCmd);
880   PropertyManager->Tie("fcs/elevator-cmd-norm", this, &FGFCS::GetDeCmd, &FGFCS::SetDeCmd);
881   PropertyManager->Tie("fcs/rudder-cmd-norm", this, &FGFCS::GetDrCmd, &FGFCS::SetDrCmd);
882   PropertyManager->Tie("fcs/flap-cmd-norm", this, &FGFCS::GetDfCmd, &FGFCS::SetDfCmd);
883   PropertyManager->Tie("fcs/speedbrake-cmd-norm", this, &FGFCS::GetDsbCmd, &FGFCS::SetDsbCmd);
884   PropertyManager->Tie("fcs/spoiler-cmd-norm", this, &FGFCS::GetDspCmd, &FGFCS::SetDspCmd);
885   PropertyManager->Tie("fcs/pitch-trim-cmd-norm", this, &FGFCS::GetPitchTrimCmd, &FGFCS::SetPitchTrimCmd);
886   PropertyManager->Tie("fcs/roll-trim-cmd-norm", this, &FGFCS::GetRollTrimCmd, &FGFCS::SetRollTrimCmd);
887   PropertyManager->Tie("fcs/yaw-trim-cmd-norm", this, &FGFCS::GetYawTrimCmd, &FGFCS::SetYawTrimCmd);
888
889   PropertyManager->Tie("fcs/left-aileron-pos-rad", this, ofRad, &FGFCS::GetDaLPos, &FGFCS::SetDaLPos);
890   PropertyManager->Tie("fcs/left-aileron-pos-deg", this, ofDeg, &FGFCS::GetDaLPos, &FGFCS::SetDaLPos);
891   PropertyManager->Tie("fcs/left-aileron-pos-norm", this, ofNorm, &FGFCS::GetDaLPos, &FGFCS::SetDaLPos);
892   PropertyManager->Tie("fcs/mag-left-aileron-pos-rad", this, ofMag, &FGFCS::GetDaLPos);
893
894   PropertyManager->Tie("fcs/right-aileron-pos-rad", this, ofRad, &FGFCS::GetDaRPos, &FGFCS::SetDaRPos);
895   PropertyManager->Tie("fcs/right-aileron-pos-deg", this, ofDeg, &FGFCS::GetDaRPos, &FGFCS::SetDaRPos);
896   PropertyManager->Tie("fcs/right-aileron-pos-norm", this, ofNorm, &FGFCS::GetDaRPos, &FGFCS::SetDaRPos);
897   PropertyManager->Tie("fcs/mag-right-aileron-pos-rad", this, ofMag, &FGFCS::GetDaRPos);
898
899   PropertyManager->Tie("fcs/elevator-pos-rad", this, ofRad, &FGFCS::GetDePos, &FGFCS::SetDePos);
900   PropertyManager->Tie("fcs/elevator-pos-deg", this, ofDeg, &FGFCS::GetDePos, &FGFCS::SetDePos);
901   PropertyManager->Tie("fcs/elevator-pos-norm", this, ofNorm, &FGFCS::GetDePos, &FGFCS::SetDePos);
902   PropertyManager->Tie("fcs/mag-elevator-pos-rad", this, ofMag, &FGFCS::GetDePos);
903
904   PropertyManager->Tie("fcs/rudder-pos-rad", this,ofRad, &FGFCS::GetDrPos, &FGFCS::SetDrPos);
905   PropertyManager->Tie("fcs/rudder-pos-deg", this,ofDeg, &FGFCS::GetDrPos, &FGFCS::SetDrPos);
906   PropertyManager->Tie("fcs/rudder-pos-norm", this,ofNorm, &FGFCS::GetDrPos, &FGFCS::SetDrPos);
907   PropertyManager->Tie("fcs/mag-rudder-pos-rad", this,ofMag, &FGFCS::GetDrPos);
908
909   PropertyManager->Tie("fcs/flap-pos-rad", this,ofRad, &FGFCS::GetDfPos, &FGFCS::SetDfPos);
910   PropertyManager->Tie("fcs/flap-pos-deg", this,ofDeg, &FGFCS::GetDfPos, &FGFCS::SetDfPos);
911   PropertyManager->Tie("fcs/flap-pos-norm", this,ofNorm, &FGFCS::GetDfPos, &FGFCS::SetDfPos);
912
913   PropertyManager->Tie("fcs/speedbrake-pos-rad", this,ofRad, &FGFCS::GetDsbPos, &FGFCS::SetDsbPos);
914   PropertyManager->Tie("fcs/speedbrake-pos-deg", this,ofDeg, &FGFCS::GetDsbPos, &FGFCS::SetDsbPos);
915   PropertyManager->Tie("fcs/speedbrake-pos-norm", this,ofNorm, &FGFCS::GetDsbPos, &FGFCS::SetDsbPos);
916   PropertyManager->Tie("fcs/mag-speedbrake-pos-rad", this,ofMag, &FGFCS::GetDsbPos);
917
918   PropertyManager->Tie("fcs/spoiler-pos-rad", this, ofRad, &FGFCS::GetDspPos, &FGFCS::SetDspPos);
919   PropertyManager->Tie("fcs/spoiler-pos-deg", this, ofDeg, &FGFCS::GetDspPos, &FGFCS::SetDspPos);
920   PropertyManager->Tie("fcs/spoiler-pos-norm", this, ofNorm, &FGFCS::GetDspPos, &FGFCS::SetDspPos);
921   PropertyManager->Tie("fcs/mag-spoiler-pos-rad", this, ofMag, &FGFCS::GetDspPos);
922
923   PropertyManager->Tie("gear/gear-pos-norm", this, &FGFCS::GetGearPos, &FGFCS::SetGearPos);
924   PropertyManager->Tie("gear/gear-cmd-norm", this, &FGFCS::GetGearCmd, &FGFCS::SetGearCmd);
925   PropertyManager->Tie("fcs/left-brake-cmd-norm", this, &FGFCS::GetLBrake, &FGFCS::SetLBrake);
926   PropertyManager->Tie("fcs/right-brake-cmd-norm", this, &FGFCS::GetRBrake, &FGFCS::SetRBrake);
927   PropertyManager->Tie("fcs/center-brake-cmd-norm", this, &FGFCS::GetCBrake, &FGFCS::SetCBrake);
928   PropertyManager->Tie("fcs/steer-cmd-norm", this, &FGFCS::GetDsCmd, &FGFCS::SetDsCmd);
929
930   PropertyManager->Tie("gear/tailhook-pos-norm", this, &FGFCS::GetTailhookPos, &FGFCS::SetTailhookPos);
931   PropertyManager->Tie("fcs/wing-fold-pos-norm", this, &FGFCS::GetWingFoldPos, &FGFCS::SetWingFoldPos);
932 }
933
934 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
935 // Technically, this function should probably bind propulsion type specific controls
936 // rather than mixture and prop-advance.
937
938 void FGFCS::bindThrottle(unsigned int num)
939 {
940   string tmp;
941
942   tmp = CreateIndexedPropertyName("fcs/throttle-cmd-norm", num);
943   PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetThrottleCmd,
944                                         &FGFCS::SetThrottleCmd);
945   tmp = CreateIndexedPropertyName("fcs/throttle-pos-norm", num);
946   PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetThrottlePos,
947                                         &FGFCS::SetThrottlePos);
948   tmp = CreateIndexedPropertyName("fcs/mixture-cmd-norm", num);
949   PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetMixtureCmd,
950                                         &FGFCS::SetMixtureCmd);
951   tmp = CreateIndexedPropertyName("fcs/mixture-pos-norm", num);
952   PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetMixturePos,
953                                         &FGFCS::SetMixturePos);
954   tmp = CreateIndexedPropertyName("fcs/advance-cmd-norm", num);
955   PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetPropAdvanceCmd,
956                                         &FGFCS::SetPropAdvanceCmd);
957   tmp = CreateIndexedPropertyName("fcs/advance-pos-norm", num);
958   PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetPropAdvance,
959                                         &FGFCS::SetPropAdvance);
960   tmp = CreateIndexedPropertyName("fcs/feather-cmd-norm", num);
961   PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetFeatherCmd,
962                                         &FGFCS::SetFeatherCmd);
963   tmp = CreateIndexedPropertyName("fcs/feather-pos-norm", num);
964   PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetPropFeather,
965                                         &FGFCS::SetPropFeather);
966 }
967
968 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
969
970 void FGFCS::bindModel(void)
971 {
972   unsigned int i;
973   string tmp;
974
975   for (i=0; i<SteerPosDeg.size(); i++) {
976     if (GroundReactions->GetGearUnit(i)->GetSteerable()) {
977       tmp = CreateIndexedPropertyName("fcs/steer-pos-deg", i);
978       PropertyManager->Tie( tmp.c_str(), this, i, &FGFCS::GetSteerPosDeg, &FGFCS::SetSteerPosDeg);
979     }
980   }
981 }
982
983 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
984 //    The bitmasked value choices are as follows:
985 //    unset: In this case (the default) JSBSim would only print
986 //       out the normally expected messages, essentially echoing
987 //       the config files as they are read. If the environment
988 //       variable is not set, debug_lvl is set to 1 internally
989 //    0: This requests JSBSim not to output any messages
990 //       whatsoever.
991 //    1: This value explicity requests the normal JSBSim
992 //       startup messages
993 //    2: This value asks for a message to be printed out when
994 //       a class is instantiated
995 //    4: When this value is set, a message is displayed when a
996 //       FGModel object executes its Run() method
997 //    8: When this value is set, various runtime state variables
998 //       are printed out periodically
999 //    16: When set various parameters are sanity checked and
1000 //       a message is printed out when they go out of bounds
1001
1002 void FGFCS::Debug(int from)
1003 {
1004   if (debug_lvl <= 0) return;
1005
1006   if (debug_lvl & 1) { // Standard console startup message output
1007     if (from == 2) { // Loader
1008       cout << endl << "  " << Name << endl;
1009     }
1010   }
1011   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
1012     if (from == 0) cout << "Instantiated: FGFCS" << endl;
1013     if (from == 1) cout << "Destroyed:    FGFCS" << endl;
1014   }
1015   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
1016   }
1017   if (debug_lvl & 8 ) { // Runtime state variables
1018   }
1019   if (debug_lvl & 16) { // Sanity checking
1020   }
1021   if (debug_lvl & 64) {
1022     if (from == 0) { // Constructor
1023       cout << IdSrc << endl;
1024       cout << IdHdr << endl;
1025     }
1026   }
1027 }
1028
1029 }