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