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