]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/models/FGFCS.cpp
0262e0872ed26698d379215667e58c9e2e95b0bb
[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
498 // ToDo: The handling of name and file attributes could be improved, here,
499 //       considering that a name can be in the external file, as well.
500
501   name = el->GetAttributeValue("name");
502
503   if (name.empty()) {
504     fname = el->GetAttributeValue("file");
505     if (systype == stSystem) {
506       file = FindSystemFullPathname(fname);
507     } else { 
508     file = FDMExec->GetFullAircraftPath() + separator + fname + ".xml";
509     }
510     if (fname.empty()) {
511       cerr << "FCS, Autopilot, or system does not appear to be defined inline nor in a file" << endl;
512       return false;
513     } else {
514       document = LoadXMLDocument(file);
515       name = document->GetAttributeValue("name");
516     }
517   } else {
518     document = el;
519   }
520
521   if (document->GetName() == "autopilot") {
522     Components = &APComponents;
523     Name = "Autopilot: " + document->GetAttributeValue("name");
524   } else if (document->GetName() == "flight_control") {
525     Components = &FCSComponents;
526     Name = "FCS: " + document->GetAttributeValue("name");
527   } else if (document->GetName() == "system") {
528     Components = &Systems;
529     Name = "System: " + document->GetAttributeValue("name");
530   }
531   Debug(2);
532
533   // ToDo: How do these get untied?
534   // ToDo: Consider having INPUT and OUTPUT interface properties. Would then
535   //       have to duplicate this block of code after channel read code.
536   //       Input properties could be write only (nah), and output could be read
537   //       only.
538
539   if (document->GetName() == "flight_control") bindModel();
540
541   // Interface properties from any autopilot, flight control, or other system are
542   // all stored in the interface properties array.
543
544   property_element = document->FindElement("property");
545   while (property_element) {
546     double value=0.0;
547     if ( ! property_element->GetAttributeValue("value").empty())
548       value = property_element->GetAttributeValueAsNumber("value");
549     interface_properties.push_back(new double(value));
550     interface_property_string = property_element->GetDataLine();
551     PropertyManager->Tie(interface_property_string, interface_properties.back());
552     property_element = document->FindNextElement("property");
553   }
554
555   // Any sensor elements that are outside of a channel (in either the autopilot
556   // or the flight_control, or even any possible "system") are placed into the global
557   // "sensors" array, and are executed prior to any autopilot, flight control, or
558   // system.
559
560   sensor_element = document->FindElement("sensor");
561   while (sensor_element) {
562     try {
563       sensors.push_back(new FGSensor(this, sensor_element));
564     } catch (string s) {
565       cerr << highint << fgred << endl << "  " << s << endl;
566       return false;
567     }
568     sensor_element = document->FindNextElement("sensor");
569   }
570
571   channel_element = document->FindElement("channel");
572   while (channel_element) {
573     component_element = channel_element->GetElement();
574     while (component_element) {
575       try {
576         if ((component_element->GetName() == string("lag_filter")) ||
577             (component_element->GetName() == string("lead_lag_filter")) ||
578             (component_element->GetName() == string("washout_filter")) ||
579             (component_element->GetName() == string("second_order_filter")) ||
580             (component_element->GetName() == string("integrator")) )
581         {
582           Components->push_back(new FGFilter(this, component_element));
583         } else if ((component_element->GetName() == string("pure_gain")) ||
584                    (component_element->GetName() == string("scheduled_gain")) ||
585                    (component_element->GetName() == string("aerosurface_scale")))
586         {
587           Components->push_back(new FGGain(this, component_element));
588         } else if (component_element->GetName() == string("summer")) {
589           Components->push_back(new FGSummer(this, component_element));
590         } else if (component_element->GetName() == string("deadband")) {
591           Components->push_back(new FGDeadBand(this, component_element));
592         } else if (component_element->GetName() == string("switch")) {
593           Components->push_back(new FGSwitch(this, component_element));
594         } else if (component_element->GetName() == string("kinematic")) {
595           Components->push_back(new FGKinemat(this, component_element));
596         } else if (component_element->GetName() == string("fcs_function")) {
597           Components->push_back(new FGFCSFunction(this, component_element));
598         } else if (component_element->GetName() == string("pid")) {
599           Components->push_back(new FGPID(this, component_element));
600         } else if (component_element->GetName() == string("actuator")) {
601           Components->push_back(new FGActuator(this, component_element));
602         } else if (component_element->GetName() == string("sensor")) {
603           Components->push_back(new FGSensor(this, component_element));
604         } else {
605           cerr << "Unknown FCS component: " << component_element->GetName() << endl;
606         }
607       } catch(string s) {
608         cerr << highint << fgred << endl << "  " << s << endl;
609         cerr << reset << endl;
610         return false;
611       }
612       component_element = channel_element->GetNextElement();
613     }
614     channel_element = document->FindNextElement("channel");
615   }
616
617   ResetParser();
618
619   return true;
620 }
621
622 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
623
624 double FGFCS::GetBrake(FGLGear::BrakeGroup bg)
625 {
626   switch (bg) {
627   case FGLGear::bgLeft:
628     return LeftBrake;
629   case FGLGear::bgRight:
630     return RightBrake;
631   case FGLGear::bgCenter:
632     return CenterBrake;
633   default:
634     cerr << "GetBrake asked to return a bogus brake value" << endl;
635   }
636   return 0.0;
637 }
638
639 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
640
641 string FGFCS::FindSystemFullPathname(string system_filename)
642 {
643   string fullpath, localpath;
644   string systemPath = FDMExec->GetSystemsPath();
645   string aircraftPath = FDMExec->GetFullAircraftPath();
646   ifstream system_file;
647
648   string separator = "/";
649
650   fullpath = systemPath + separator;
651   localpath = aircraftPath + separator + "Systems" + separator;
652
653   system_file.open(string(fullpath + system_filename + ".xml").c_str());
654   if ( !system_file.is_open()) {
655     system_file.open(string(localpath + system_filename + ".xml").c_str());
656       if ( !system_file.is_open()) {
657         cerr << " Could not open system file: " << system_filename << " in path "
658              << fullpath << " or " << localpath << endl;
659         return string("");
660       } else {
661         return string(localpath + system_filename + ".xml");
662       }
663   }
664   return string(fullpath + system_filename + ".xml");
665 }
666
667 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
668
669 ifstream* FGFCS::FindSystemFile(string system_filename)
670 {
671   string fullpath, localpath;
672   string systemPath = FDMExec->GetSystemsPath();
673   string aircraftPath = FDMExec->GetFullAircraftPath();
674   ifstream* system_file = new ifstream();
675
676   string separator = "/";
677
678   fullpath = systemPath + separator;
679   localpath = aircraftPath + separator + "Systems" + separator;
680
681   system_file->open(string(fullpath + system_filename + ".xml").c_str());
682   if ( !system_file->is_open()) {
683     system_file->open(string(localpath + system_filename + ".xml").c_str());
684       if ( !system_file->is_open()) {
685         cerr << " Could not open system file: " << system_filename << " in path "
686              << fullpath << " or " << localpath << endl;
687       }
688   }
689   return system_file;
690 }
691
692 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
693
694 string FGFCS::GetComponentStrings(string delimeter)
695 {
696   unsigned int comp;
697   string CompStrings = "";
698   bool firstime = true;
699   int total_count=0;
700
701   for (unsigned int i=0; i<Systems.size(); i++) {
702     if (firstime) firstime = false;
703     else          CompStrings += delimeter;
704
705     CompStrings += Systems[i]->GetName();
706     total_count++;
707   }
708
709   for (comp = 0; comp < APComponents.size(); comp++)
710   {
711     if (firstime) firstime = false;
712     else          CompStrings += delimeter;
713
714     CompStrings += APComponents[comp]->GetName();
715     total_count++;
716   }
717
718   for (comp = 0; comp < FCSComponents.size(); comp++) {
719     if (firstime) firstime = false;
720     else          CompStrings += delimeter;
721
722     CompStrings += FCSComponents[comp]->GetName();
723     total_count++;
724   }
725
726   return CompStrings;
727 }
728
729 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
730
731 string FGFCS::GetComponentValues(string delimeter)
732 {
733   unsigned int comp;
734   string CompValues = "";
735   char buffer[20];
736   bool firstime = true;
737   int total_count=0;
738
739   for (unsigned int i=0; i<Systems.size(); i++) {
740     if (firstime) firstime = false;
741     else          CompValues += delimeter;
742
743     sprintf(buffer, "%9.6f", Systems[i]->GetOutput());
744     CompValues += string(buffer);
745     total_count++;
746   }
747
748   for (comp = 0; comp < APComponents.size(); comp++) {
749     if (firstime) firstime = false;
750     else          CompValues += delimeter;
751
752     sprintf(buffer, "%9.6f", APComponents[comp]->GetOutput());
753     CompValues += string(buffer);
754     total_count++;
755   }
756
757   for (comp = 0; comp < FCSComponents.size(); comp++) {
758     if (firstime) firstime = false;
759     else          CompValues += delimeter;
760
761     sprintf(buffer, "%9.6f", FCSComponents[comp]->GetOutput());
762     CompValues += string(buffer);
763     total_count++;
764   }
765
766   CompValues += "\0";
767   return CompValues;
768 }
769
770 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
771
772 void FGFCS::AddThrottle(void)
773 {
774   ThrottleCmd.push_back(0.0);
775   ThrottlePos.push_back(0.0);
776   MixtureCmd.push_back(0.0);     // assume throttle and mixture are coupled
777   MixturePos.push_back(0.0);
778   PropAdvanceCmd.push_back(0.0); // assume throttle and prop pitch are coupled
779   PropAdvance.push_back(0.0);
780   PropFeatherCmd.push_back(false);
781   PropFeather.push_back(false);
782
783   unsigned int num = (unsigned int)ThrottleCmd.size()-1;
784   bindThrottle(num);
785 }
786
787 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
788
789 void FGFCS::AddGear(void)
790 {
791   SteerPosDeg.push_back(0.0);
792 }
793
794 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
795
796 double FGFCS::GetDt(void)
797 {
798   return FDMExec->GetDeltaT()*rate;
799 }
800
801 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
802
803 void FGFCS::bind(void)
804 {
805   PropertyManager->Tie("fcs/aileron-cmd-norm", this, &FGFCS::GetDaCmd, &FGFCS::SetDaCmd);
806   PropertyManager->Tie("fcs/elevator-cmd-norm", this, &FGFCS::GetDeCmd, &FGFCS::SetDeCmd);
807   PropertyManager->Tie("fcs/rudder-cmd-norm", this, &FGFCS::GetDrCmd, &FGFCS::SetDrCmd);
808   PropertyManager->Tie("fcs/flap-cmd-norm", this, &FGFCS::GetDfCmd, &FGFCS::SetDfCmd);
809   PropertyManager->Tie("fcs/speedbrake-cmd-norm", this, &FGFCS::GetDsbCmd, &FGFCS::SetDsbCmd);
810   PropertyManager->Tie("fcs/spoiler-cmd-norm", this, &FGFCS::GetDspCmd, &FGFCS::SetDspCmd);
811   PropertyManager->Tie("fcs/pitch-trim-cmd-norm", this, &FGFCS::GetPitchTrimCmd, &FGFCS::SetPitchTrimCmd);
812   PropertyManager->Tie("fcs/roll-trim-cmd-norm", this, &FGFCS::GetRollTrimCmd, &FGFCS::SetRollTrimCmd);
813   PropertyManager->Tie("fcs/yaw-trim-cmd-norm", this, &FGFCS::GetYawTrimCmd, &FGFCS::SetYawTrimCmd);
814
815   PropertyManager->Tie("fcs/left-aileron-pos-rad", this, ofRad, &FGFCS::GetDaLPos, &FGFCS::SetDaLPos);
816   PropertyManager->Tie("fcs/left-aileron-pos-deg", this, ofDeg, &FGFCS::GetDaLPos, &FGFCS::SetDaLPos);
817   PropertyManager->Tie("fcs/left-aileron-pos-norm", this, ofNorm, &FGFCS::GetDaLPos, &FGFCS::SetDaLPos);
818   PropertyManager->Tie("fcs/mag-left-aileron-pos-rad", this, ofMag, &FGFCS::GetDaLPos);
819
820   PropertyManager->Tie("fcs/right-aileron-pos-rad", this, ofRad, &FGFCS::GetDaRPos, &FGFCS::SetDaRPos);
821   PropertyManager->Tie("fcs/right-aileron-pos-deg", this, ofDeg, &FGFCS::GetDaRPos, &FGFCS::SetDaRPos);
822   PropertyManager->Tie("fcs/right-aileron-pos-norm", this, ofNorm, &FGFCS::GetDaRPos, &FGFCS::SetDaRPos);
823   PropertyManager->Tie("fcs/mag-right-aileron-pos-rad", this, ofMag, &FGFCS::GetDaRPos);
824
825   PropertyManager->Tie("fcs/elevator-pos-rad", this, ofRad, &FGFCS::GetDePos, &FGFCS::SetDePos);
826   PropertyManager->Tie("fcs/elevator-pos-deg", this, ofDeg, &FGFCS::GetDePos, &FGFCS::SetDePos);
827   PropertyManager->Tie("fcs/elevator-pos-norm", this, ofNorm, &FGFCS::GetDePos, &FGFCS::SetDePos);
828   PropertyManager->Tie("fcs/mag-elevator-pos-rad", this, ofMag, &FGFCS::GetDePos);
829
830   PropertyManager->Tie("fcs/rudder-pos-rad", this,ofRad, &FGFCS::GetDrPos, &FGFCS::SetDrPos);
831   PropertyManager->Tie("fcs/rudder-pos-deg", this,ofDeg, &FGFCS::GetDrPos, &FGFCS::SetDrPos);
832   PropertyManager->Tie("fcs/rudder-pos-norm", this,ofNorm, &FGFCS::GetDrPos, &FGFCS::SetDrPos);
833   PropertyManager->Tie("fcs/mag-rudder-pos-rad", this,ofMag, &FGFCS::GetDrPos);
834
835   PropertyManager->Tie("fcs/flap-pos-rad", this,ofRad, &FGFCS::GetDfPos, &FGFCS::SetDfPos);
836   PropertyManager->Tie("fcs/flap-pos-deg", this,ofDeg, &FGFCS::GetDfPos, &FGFCS::SetDfPos);
837   PropertyManager->Tie("fcs/flap-pos-norm", this,ofNorm, &FGFCS::GetDfPos, &FGFCS::SetDfPos);
838
839   PropertyManager->Tie("fcs/speedbrake-pos-rad", this,ofRad, &FGFCS::GetDsbPos, &FGFCS::SetDsbPos);
840   PropertyManager->Tie("fcs/speedbrake-pos-deg", this,ofDeg, &FGFCS::GetDsbPos, &FGFCS::SetDsbPos);
841   PropertyManager->Tie("fcs/speedbrake-pos-norm", this,ofNorm, &FGFCS::GetDsbPos, &FGFCS::SetDsbPos);
842   PropertyManager->Tie("fcs/mag-speedbrake-pos-rad", this,ofMag, &FGFCS::GetDsbPos);
843
844   PropertyManager->Tie("fcs/spoiler-pos-rad", this, ofRad, &FGFCS::GetDspPos, &FGFCS::SetDspPos);
845   PropertyManager->Tie("fcs/spoiler-pos-deg", this, ofDeg, &FGFCS::GetDspPos, &FGFCS::SetDspPos);
846   PropertyManager->Tie("fcs/spoiler-pos-norm", this, ofNorm, &FGFCS::GetDspPos, &FGFCS::SetDspPos);
847   PropertyManager->Tie("fcs/mag-spoiler-pos-rad", this, ofMag, &FGFCS::GetDspPos);
848
849   PropertyManager->Tie("gear/gear-pos-norm", this, &FGFCS::GetGearPos, &FGFCS::SetGearPos);
850   PropertyManager->Tie("gear/gear-cmd-norm", this, &FGFCS::GetGearCmd, &FGFCS::SetGearCmd);
851   PropertyManager->Tie("fcs/left-brake-cmd-norm", this, &FGFCS::GetLBrake, &FGFCS::SetLBrake);
852   PropertyManager->Tie("fcs/right-brake-cmd-norm", this, &FGFCS::GetRBrake, &FGFCS::SetRBrake);
853   PropertyManager->Tie("fcs/center-brake-cmd-norm", this, &FGFCS::GetCBrake, &FGFCS::SetCBrake);
854   PropertyManager->Tie("fcs/steer-cmd-norm", this, &FGFCS::GetDsCmd, &FGFCS::SetDsCmd);
855 }
856
857 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
858 // Technically, this function should probably bind propulsion type specific controls
859 // rather than mixture and prop-advance.
860 //
861
862 void FGFCS::bindThrottle(unsigned int num)
863 {
864   char tmp[80];
865
866   snprintf(tmp, 80, "fcs/throttle-cmd-norm[%u]",num);
867   PropertyManager->Tie( tmp, this, num, &FGFCS::GetThrottleCmd,
868                                         &FGFCS::SetThrottleCmd);
869   snprintf(tmp, 80, "fcs/throttle-pos-norm[%u]",num);
870   PropertyManager->Tie( tmp, this, num, &FGFCS::GetThrottlePos,
871                                         &FGFCS::SetThrottlePos);
872   snprintf(tmp, 80, "fcs/mixture-cmd-norm[%u]",num);
873   PropertyManager->Tie( tmp, this, num, &FGFCS::GetMixtureCmd,
874                                         &FGFCS::SetMixtureCmd);
875   snprintf(tmp, 80, "fcs/mixture-pos-norm[%u]",num);
876   PropertyManager->Tie( tmp, this, num, &FGFCS::GetMixturePos,
877                                         &FGFCS::SetMixturePos);
878   snprintf(tmp, 80, "fcs/advance-cmd-norm[%u]",num);
879   PropertyManager->Tie( tmp, this, num, &FGFCS::GetPropAdvanceCmd,
880                                         &FGFCS::SetPropAdvanceCmd);
881   snprintf(tmp, 80, "fcs/advance-pos-norm[%u]", num);
882   PropertyManager->Tie( tmp, this, num, &FGFCS::GetPropAdvance,
883                                         &FGFCS::SetPropAdvance);
884   snprintf(tmp, 80, "fcs/feather-cmd-norm[%u]", num);
885   PropertyManager->Tie( tmp, this, num, &FGFCS::GetFeatherCmd,
886                                         &FGFCS::SetFeatherCmd);
887   snprintf(tmp, 80, "fcs/feather-pos-norm[%u]", num);
888   PropertyManager->Tie( tmp, this, num, &FGFCS::GetPropFeather,
889                                         &FGFCS::SetPropFeather);
890 }
891
892 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
893
894 void FGFCS::bindModel(void)
895 {
896   unsigned int i;
897   char tmp[80];
898
899   for (i=0; i<SteerPosDeg.size(); i++) {
900     if (GroundReactions->GetGearUnit(i)->GetSteerable()) {
901       snprintf(tmp,80,"fcs/steer-pos-deg[%u]",i);
902       PropertyManager->Tie( tmp, this, i, &FGFCS::GetSteerPosDeg, &FGFCS::SetSteerPosDeg);
903     }
904   }
905 }
906
907 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
908 //    The bitmasked value choices are as follows:
909 //    unset: In this case (the default) JSBSim would only print
910 //       out the normally expected messages, essentially echoing
911 //       the config files as they are read. If the environment
912 //       variable is not set, debug_lvl is set to 1 internally
913 //    0: This requests JSBSim not to output any messages
914 //       whatsoever.
915 //    1: This value explicity requests the normal JSBSim
916 //       startup messages
917 //    2: This value asks for a message to be printed out when
918 //       a class is instantiated
919 //    4: When this value is set, a message is displayed when a
920 //       FGModel object executes its Run() method
921 //    8: When this value is set, various runtime state variables
922 //       are printed out periodically
923 //    16: When set various parameters are sanity checked and
924 //       a message is printed out when they go out of bounds
925
926 void FGFCS::Debug(int from)
927 {
928   if (debug_lvl <= 0) return;
929
930   if (debug_lvl & 1) { // Standard console startup message output
931     if (from == 2) { // Loader
932       cout << endl << "  Flight Control (" << Name << ")" << endl;
933     }
934   }
935   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
936     if (from == 0) cout << "Instantiated: FGFCS" << endl;
937     if (from == 1) cout << "Destroyed:    FGFCS" << endl;
938   }
939   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
940   }
941   if (debug_lvl & 8 ) { // Runtime state variables
942   }
943   if (debug_lvl & 16) { // Sanity checking
944   }
945   if (debug_lvl & 64) {
946     if (from == 0) { // Constructor
947       cout << IdSrc << endl;
948       cout << IdHdr << endl;
949     }
950   }
951 }
952
953 }