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