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