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