]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/FGFCS.cpp
Sync. with JSBSim CVS.
[flightgear.git] / src / FDM / JSBSim / FGFCS.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3  Module:       FGFCS.cpp
4  Author:       Jon Berndt
5  Date started: 12/12/98
6  Purpose:      Model the flight controls
7  Called by:    FDMExec
8
9  ------------- Copyright (C) 1999  Jon S. Berndt (jsb@hal-pc.org) -------------
10
11  This program is free software; you can redistribute it and/or modify it under
12  the terms of the GNU General Public License as published by the Free Software
13  Foundation; either version 2 of the License, or (at your option) any later
14  version.
15
16  This program is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
19  details.
20
21  You should have received a copy of the GNU General Public License along with
22  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
23  Place - Suite 330, Boston, MA  02111-1307, USA.
24
25  Further information about the GNU General Public License can also be found on
26  the world wide web at http://www.gnu.org.
27
28 FUNCTIONAL DESCRIPTION
29 --------------------------------------------------------------------------------
30 This class models the flight controls for a specific airplane
31
32 HISTORY
33 --------------------------------------------------------------------------------
34 12/12/98   JSB   Created
35
36 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37 INCLUDES
38 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
39
40 #include "FGFCS.h"
41 #include "FGFDMExec.h"
42 #include "FGPropertyManager.h"
43
44 #include "filtersjb/FGFilter.h"
45 #include "filtersjb/FGDeadBand.h"
46 #include "filtersjb/FGGain.h"
47 #include "filtersjb/FGGradient.h"
48 #include "filtersjb/FGSwitch.h"
49 #include "filtersjb/FGSummer.h"
50 #include "filtersjb/FGKinemat.h"
51
52 namespace JSBSim {
53
54 static const char *IdSrc = "$Id$";
55 static const char *IdHdr = ID_FCS;
56
57 #if defined(WIN32) && !defined(__CYGWIN__)
58 #define snprintf _snprintf
59 #endif
60
61 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62 CLASS IMPLEMENTATION
63 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
64
65 FGFCS::FGFCS(FGFDMExec* fdmex) : FGModel(fdmex)
66 {
67   int i;
68   Name = "FGFCS";
69
70   DaCmd = DeCmd = DrCmd = DfCmd = DsbCmd = DspCmd = 0.0;
71   AP_DaCmd = AP_DeCmd = AP_DrCmd = AP_ThrottleCmd = 0.0;
72   PTrimCmd = YTrimCmd = RTrimCmd = 0.0;
73   GearCmd = GearPos = 1; // default to gear down
74   LeftBrake = RightBrake = CenterBrake = 0.0;
75   APAttitudeSetPt = APAltitudeSetPt = APHeadingSetPt = APAirspeedSetPt = 0.0;
76   DoNormalize=true;
77
78   eMode = mNone;
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   for (i=0;i<NNorm;i++) { ToNormalize[i]=-1;}
87   Debug(0);
88 }
89
90 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91
92 FGFCS::~FGFCS()
93 {
94   unbind( PropertyManager->GetNode("fcs") );
95   unbind( PropertyManager->GetNode("ap") );
96   PropertyManager->Untie( "gear/gear-cmd-norm" );
97   PropertyManager->Untie( "gear/gear-pos-norm" );
98
99   ThrottleCmd.clear();
100   ThrottlePos.clear();
101   MixtureCmd.clear();
102   MixturePos.clear();
103   PropAdvanceCmd.clear();
104   PropAdvance.clear();
105
106   unsigned int i;
107
108   for (i=0;i<APComponents.size();i++) delete APComponents[i];
109   for (i=0;i<FCSComponents.size();i++) delete FCSComponents[i];
110
111   Debug(1);
112 }
113
114 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115
116 bool FGFCS::Run(void)
117 {
118   unsigned int i;
119
120   if (!FGModel::Run()) {
121     for (i=0; i<ThrottlePos.size(); i++) ThrottlePos[i] = ThrottleCmd[i];
122     for (i=0; i<MixturePos.size(); i++) MixturePos[i] = MixtureCmd[i];
123     for (i=0; i<PropAdvance.size(); i++) PropAdvance[i] = PropAdvanceCmd[i];
124     for (i=0; i<APComponents.size(); i++)  {
125       eMode = mAP;
126       APComponents[i]->Run();
127       eMode = mNone;
128     }
129     for (i=0; i<FCSComponents.size(); i++)  {
130       eMode = mFCS;
131       FCSComponents[i]->Run();
132       eMode = mNone;
133     }
134     if (DoNormalize) Normalize();
135
136     return false;
137   } else {
138     return true;
139   }
140 }
141
142 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
143
144 void FGFCS::SetThrottleCmd(int engineNum, double setting)
145 {
146   unsigned int ctr;
147
148   if (engineNum < (int)ThrottlePos.size()) {
149     if (engineNum < 0) {
150       for (ctr=0;ctr<ThrottleCmd.size();ctr++) ThrottleCmd[ctr] = setting;
151     } else {
152       ThrottleCmd[engineNum] = setting;
153     }
154   } else {
155     cerr << "Throttle " << engineNum << " does not exist! " << ThrottleCmd.size()
156          << " engines exist, but attempted throttle command is for engine "
157          << engineNum << endl;
158   }
159 }
160
161 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
162
163 void FGFCS::SetThrottlePos(int engineNum, double setting)
164 {
165   unsigned int ctr;
166
167   if (engineNum < (int)ThrottlePos.size()) {
168     if (engineNum < 0) {
169       for (ctr=0;ctr<ThrottlePos.size();ctr++) ThrottlePos[ctr] = setting;
170     } else {
171       ThrottlePos[engineNum] = setting;
172     }
173   } else {
174     cerr << "Throttle " << engineNum << " does not exist! " << ThrottlePos.size()
175          << " engines exist, but attempted throttle position setting is for engine "
176          << engineNum << endl;
177   }
178 }
179
180 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
181
182 double FGFCS::GetThrottleCmd(int engineNum) const
183 {
184   if (engineNum < (int)ThrottlePos.size()) {
185     if (engineNum < 0) {
186        cerr << "Cannot get throttle value for ALL engines" << endl;
187     } else {
188       return ThrottleCmd[engineNum];
189     }
190   } else {
191     cerr << "Throttle " << engineNum << " does not exist! " << ThrottleCmd.size()
192          << " engines exist, but throttle setting for engine " << engineNum
193          << " is selected" << endl;
194   }
195   return 0.0;
196 }
197
198 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
199
200 double FGFCS::GetThrottlePos(int engineNum) const
201 {
202   if (engineNum < (int)ThrottlePos.size()) {
203     if (engineNum < 0) {
204        cerr << "Cannot get throttle value for ALL engines" << endl;
205     } else {
206       return ThrottlePos[engineNum];
207     }
208   } else {
209     cerr << "Throttle " << engineNum << " does not exist! " << ThrottlePos.size()
210          << " engines exist, but attempted throttle position setting is for engine "
211          << engineNum << endl;
212   }
213   return 0.0;
214 }
215
216 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
217
218 void FGFCS::SetMixtureCmd(int engineNum, double setting)
219 {
220   unsigned int ctr;
221
222   if (engineNum < (int)ThrottlePos.size()) {
223     if (engineNum < 0) {
224       for (ctr=0;ctr<MixtureCmd.size();ctr++) MixtureCmd[ctr] = setting;
225     } else {
226       MixtureCmd[engineNum] = setting;
227     }
228   }
229 }
230
231 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
232
233 void FGFCS::SetMixturePos(int engineNum, double setting)
234 {
235   unsigned int ctr;
236
237   if (engineNum < (int)ThrottlePos.size()) {
238     if (engineNum < 0) {
239       for (ctr=0;ctr<=MixtureCmd.size();ctr++) MixturePos[ctr] = MixtureCmd[ctr];
240     } else {
241       MixturePos[engineNum] = setting;
242     }
243   }
244 }
245
246 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
247
248 void FGFCS::SetPropAdvanceCmd(int engineNum, double setting)
249 {
250   unsigned int ctr;
251
252   if (engineNum < (int)ThrottlePos.size()) {
253     if (engineNum < 0) {
254       for (ctr=0;ctr<PropAdvanceCmd.size();ctr++) PropAdvanceCmd[ctr] = setting;
255     } else {
256       PropAdvanceCmd[engineNum] = setting;
257     }
258   }
259 }
260
261 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
262
263 void FGFCS::SetPropAdvance(int engineNum, double setting)
264 {
265   unsigned int ctr;
266
267   if (engineNum < (int)ThrottlePos.size()) {
268     if (engineNum < 0) {
269       for (ctr=0;ctr<=PropAdvanceCmd.size();ctr++) PropAdvance[ctr] = PropAdvanceCmd[ctr];
270     } else {
271       PropAdvance[engineNum] = setting;
272     }
273   }
274 }
275
276 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
277
278 bool FGFCS::Load(FGConfigFile* AC_cfg)
279 {
280   string token, delimiter;
281   string name, file, fname;
282   unsigned i;
283   vector <FGFCSComponent*> *Components;
284   FGConfigFile *FCS_cfg;
285
286   Components=0;
287   // Determine if the FCS/Autopilot is defined inline in the aircraft configuration
288   // file or in a separate file. Set up the config file class as appropriate.
289
290   delimiter = AC_cfg->GetValue();
291   name  = AC_cfg->GetValue("NAME");
292   fname = AC_cfg->GetValue("FILE");
293
294   if ( AC_cfg->GetValue("NORMALIZE") == "FALSE") {
295     DoNormalize = false;
296     cout << "    Automatic Control Surface Normalization Disabled" << endl;
297   }
298
299 # ifndef macintosh
300   file = "control/" + fname + ".xml";
301 # else
302   file = "control;" + fname + ".xml";
303 # endif
304
305   if (name.empty()) {
306     name = fname;
307     if (file.empty()) {
308       cerr << "FCS/Autopilot does not appear to be defined inline nor in a file" << endl;
309     } else {
310       FCS_cfg = new FGConfigFile(file);
311       if (!FCS_cfg->IsOpen()) {
312         cerr << "Could not open " << delimiter << " file: " << file << endl;
313         return false;
314       } else {
315         AC_cfg = FCS_cfg; // set local config file object pointer to FCS config
316                           // file object pointer
317       }
318     }
319   } else {
320     AC_cfg->GetNextConfigLine();
321   }
322
323   if (delimiter == "AUTOPILOT") {
324     Components = &APComponents;
325     eMode = mAP;
326     Name = "Autopilot: " + name;
327   } else if (delimiter == "FLIGHT_CONTROL") {
328     Components = &FCSComponents;
329     eMode = mFCS;
330     Name = "FCS: " + name;
331   } else {
332     cerr << endl << "Unknown FCS delimiter" << endl << endl;
333   }
334
335   if (debug_lvl > 0) cout << "    Control System Name: " << Name << endl;
336
337   while ((token = AC_cfg->GetValue()) != string("/" + delimiter)) {
338     if (token == "COMPONENT") {
339       token = AC_cfg->GetValue("TYPE");
340       if (debug_lvl > 0) cout << endl << "    Loading Component \""
341                               << AC_cfg->GetValue("NAME")
342                               << "\" of type: " << token << endl;
343       if ((token == "LAG_FILTER") ||
344           (token == "LEAD_LAG_FILTER") ||
345           (token == "SECOND_ORDER_FILTER") ||
346           (token == "WASHOUT_FILTER") ||
347           (token == "INTEGRATOR") ) {
348         Components->push_back(new FGFilter(this, AC_cfg));
349       } else if ((token == "PURE_GAIN") ||
350                  (token == "SCHEDULED_GAIN") ||
351                  (token == "AEROSURFACE_SCALE") ) {
352
353         Components->push_back(new FGGain(this, AC_cfg));
354
355       } else if (token == "SUMMER") {
356         Components->push_back(new FGSummer(this, AC_cfg));
357       } else if (token == "DEADBAND") {
358         Components->push_back(new FGDeadBand(this, AC_cfg));
359       } else if (token == "GRADIENT") {
360         Components->push_back(new FGGradient(this, AC_cfg));
361       } else if (token == "SWITCH") {
362         Components->push_back(new FGSwitch(this, AC_cfg));
363       } else if (token == "KINEMAT") {
364         Components->push_back(new FGKinemat(this, AC_cfg));
365       } else {
366         cerr << "Unknown token [" << token << "] in FCS portion of config file" << endl;
367         return false;
368       }
369       if (AC_cfg->GetNextConfigLine() == "EOF") break;
370     }
371   }
372
373   //collect information for normalizing control surfaces
374
375   string nodeName;
376   for (i=0; i<Components->size(); i++) {
377
378     if ( (((*Components)[i])->GetType() == "AEROSURFACE_SCALE"
379           || ((*Components)[i])->GetType() == "KINEMAT")
380                     && ((*Components)[i])->GetOutputNode() ) {
381       nodeName = ((*Components)[i])->GetOutputNode()->GetName();
382       if ( nodeName == "elevator-pos-rad" ) {
383         ToNormalize[iDe]=i;
384       } else if ( nodeName  == "left-aileron-pos-rad"
385                    || nodeName == "aileron-pos-rad" ) {
386         ToNormalize[iDaL]=i;
387       } else if ( nodeName == "right-aileron-pos-rad" ) {
388         ToNormalize[iDaR]=i;
389       } else if ( nodeName == "rudder-pos-rad" ) {
390         ToNormalize[iDr]=i;
391       } else if ( nodeName == "speedbrake-pos-rad" ) {
392         ToNormalize[iDsb]=i;
393       } else if ( nodeName == "spoiler-pos-rad" ) {
394         ToNormalize[iDsp]=i;
395       } else if ( nodeName == "flap-pos-deg" ) {
396         ToNormalize[iDf]=i;
397       }
398     }
399   }
400
401   if (delimiter == "FLIGHT_CONTROL") bindModel();
402
403   eMode = mNone;
404
405   return true;
406 }
407
408 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
409
410 double FGFCS::GetComponentOutput(int idx)
411 {
412   switch (eMode) {
413   case mFCS:
414     return FCSComponents[idx]->GetOutput();
415   case mAP:
416     return APComponents[idx]->GetOutput();
417   case mNone:
418     cerr << "Unknown FCS mode" << endl;
419     break;
420   }
421   return 0.0;
422 }
423
424 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
425
426 string FGFCS::GetComponentName(int idx)
427 {
428   switch (eMode) {
429   case mFCS:
430     return FCSComponents[idx]->GetName();
431   case mAP:
432     return APComponents[idx]->GetName();
433   case mNone:
434     cerr << "Unknown FCS mode" << endl;
435     break;
436   }
437   return string("");
438 }
439
440 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441
442 double FGFCS::GetBrake(FGLGear::BrakeGroup bg)
443 {
444   switch (bg) {
445   case FGLGear::bgLeft:
446     return LeftBrake;
447   case FGLGear::bgRight:
448     return RightBrake;
449   case FGLGear::bgCenter:
450     return CenterBrake;
451   default:
452     cerr << "GetBrake asked to return a bogus brake value" << endl;
453   }
454   return 0.0;
455 }
456
457 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
458
459 string FGFCS::GetComponentStrings(void)
460 {
461   unsigned int comp;
462   string CompStrings = "";
463   bool firstime = true;
464
465   for (comp = 0; comp < FCSComponents.size(); comp++) {
466     if (firstime) firstime = false;
467     else          CompStrings += ", ";
468
469     CompStrings += FCSComponents[comp]->GetName();
470   }
471
472   for (comp = 0; comp < APComponents.size(); comp++)
473   {
474     CompStrings += ", ";
475     CompStrings += APComponents[comp]->GetName();
476   }
477
478   return CompStrings;
479 }
480
481 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
482
483 string FGFCS::GetComponentValues(void)
484 {
485   unsigned int comp;
486   string CompValues = "";
487   char buffer[10];
488   bool firstime = true;
489
490   for (comp = 0; comp < FCSComponents.size(); comp++) {
491     if (firstime) firstime = false;
492     else          CompValues += ", ";
493
494     sprintf(buffer, "%9.6f", FCSComponents[comp]->GetOutput());
495     CompValues += string(buffer);
496   }
497
498   for (comp = 0; comp < APComponents.size(); comp++) {
499     sprintf(buffer, ", %9.6f", APComponents[comp]->GetOutput());
500     CompValues += string(buffer);
501   }
502
503   return CompValues;
504 }
505
506 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
507
508 void FGFCS::AddThrottle(void)
509 {
510   ThrottleCmd.push_back(0.0);
511   ThrottlePos.push_back(0.0);
512   MixtureCmd.push_back(0.0);     // assume throttle and mixture are coupled
513   MixturePos.push_back(0.0);
514   PropAdvanceCmd.push_back(0.0); // assume throttle and prop pitch are coupled
515   PropAdvance.push_back(0.0);
516 }
517
518 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
519
520 void FGFCS::Normalize(void) {
521
522   //not all of these are guaranteed to be defined for every model
523   //those that are have an index >=0 in the ToNormalize array
524   //ToNormalize is filled in Load()
525
526   if ( ToNormalize[iDe] > -1 ) {
527     DePos[ofNorm] = FCSComponents[ToNormalize[iDe]]->GetOutputPct();
528   }
529
530   if ( ToNormalize[iDaL] > -1 ) {
531     DaLPos[ofNorm] = FCSComponents[ToNormalize[iDaL]]->GetOutputPct();
532   }
533
534   if ( ToNormalize[iDaR] > -1 ) {
535     DaRPos[ofNorm] = FCSComponents[ToNormalize[iDaR]]->GetOutputPct();
536   }
537
538   if ( ToNormalize[iDr] > -1 ) {
539     DrPos[ofNorm] = FCSComponents[ToNormalize[iDr]]->GetOutputPct();
540   }
541
542   if ( ToNormalize[iDsb] > -1 ) {
543     DsbPos[ofNorm] = FCSComponents[ToNormalize[iDsb]]->GetOutputPct();
544   }
545
546   if ( ToNormalize[iDsp] > -1 ) {
547     DspPos[ofNorm] = FCSComponents[ToNormalize[iDsp]]->GetOutputPct();
548   }
549
550   if ( ToNormalize[iDf] > -1 ) {
551     DfPos[ofNorm] = FCSComponents[ToNormalize[iDf]]->GetOutputPct();
552   }
553
554   DePos[ofMag]  = fabs(DePos[ofRad]);
555   DaLPos[ofMag] = fabs(DaLPos[ofRad]);
556   DaRPos[ofMag] = fabs(DaRPos[ofRad]);
557   DrPos[ofMag]  = fabs(DrPos[ofRad]);
558   DsbPos[ofMag] = fabs(DsbPos[ofRad]);
559   DspPos[ofMag] = fabs(DspPos[ofRad]);
560   DfPos[ofMag]  = fabs(DfPos[ofRad]);
561
562 }
563
564 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
565
566 void FGFCS::bind(void)
567 {
568   PropertyManager->Tie("fcs/aileron-cmd-norm", this,
569                        &FGFCS::GetDaCmd,
570                        &FGFCS::SetDaCmd,
571                        true);
572   PropertyManager->Tie("fcs/elevator-cmd-norm", this,
573                        &FGFCS::GetDeCmd,
574                        &FGFCS::SetDeCmd,
575                        true);
576   PropertyManager->Tie("fcs/rudder-cmd-norm", this,
577                        &FGFCS::GetDrCmd,
578                        &FGFCS::SetDrCmd,
579                        true);
580   PropertyManager->Tie("fcs/flap-cmd-norm", this,
581                        &FGFCS::GetDfCmd,
582                        &FGFCS::SetDfCmd,
583                        true);
584   PropertyManager->Tie("fcs/speedbrake-cmd-norm", this,
585                        &FGFCS::GetDsbCmd,
586                        &FGFCS::SetDsbCmd,
587                        true);
588   PropertyManager->Tie("fcs/spoiler-cmd-norm", this,
589                        &FGFCS::GetDspCmd,
590                        &FGFCS::SetDspCmd,
591                        true);
592   PropertyManager->Tie("fcs/pitch-trim-cmd-norm", this,
593                        &FGFCS::GetPitchTrimCmd,
594                        &FGFCS::SetPitchTrimCmd,
595                        true);
596   PropertyManager->Tie("fcs/roll-trim-cmd-norm", this,
597                        &FGFCS::GetRollTrimCmd,
598                        &FGFCS::SetRollTrimCmd,
599                        true);
600   PropertyManager->Tie("fcs/yaw-trim-cmd-norm", this,
601                        &FGFCS::GetYawTrimCmd,
602                        &FGFCS::SetYawTrimCmd,
603                        true);
604   PropertyManager->Tie("gear/gear-cmd-norm", this,
605                        &FGFCS::GetGearCmd,
606                        &FGFCS::SetGearCmd,
607                        true);
608
609   PropertyManager->Tie("fcs/left-aileron-pos-rad", this,ofRad,
610                        &FGFCS::GetDaLPos,
611                        &FGFCS::SetDaLPos,
612                        true);
613   PropertyManager->Tie("fcs/left-aileron-pos-norm", this,ofNorm,
614                        &FGFCS::GetDaLPos,
615                        &FGFCS::SetDaLPos,
616                        true);
617   PropertyManager->Tie("fcs/mag-left-aileron-pos-rad", this,ofMag,
618                        &FGFCS::GetDaLPos,
619                        &FGFCS::SetDaLPos,
620                        true);
621
622   PropertyManager->Tie("fcs/right-aileron-pos-rad", this,ofRad,
623                        &FGFCS::GetDaRPos,
624                        &FGFCS::SetDaRPos,
625                        true);
626   PropertyManager->Tie("fcs/right-aileron-pos-norm", this,ofNorm,
627                        &FGFCS::GetDaRPos,
628                        &FGFCS::SetDaRPos,
629                        true);
630   PropertyManager->Tie("fcs/mag-right-aileron-pos-rad", this,ofMag,
631                        &FGFCS::GetDaRPos,
632                        &FGFCS::SetDaRPos,
633                        true);
634
635   PropertyManager->Tie("fcs/elevator-pos-rad", this, ofRad,
636                        &FGFCS::GetDePos,
637                        &FGFCS::SetDePos,
638                        true );
639   PropertyManager->Tie("fcs/elevator-pos-norm", this,ofNorm,
640                        &FGFCS::GetDePos,
641                        &FGFCS::SetDePos,
642                        true );
643   PropertyManager->Tie("fcs/mag-elevator-pos-rad", this,ofMag,
644                        &FGFCS::GetDePos,
645                        &FGFCS::SetDePos,
646                        true );
647
648   PropertyManager->Tie("fcs/rudder-pos-rad", this,ofRad,
649                        &FGFCS::GetDrPos,
650                        &FGFCS::SetDrPos,
651                        true);
652   PropertyManager->Tie("fcs/rudder-pos-norm", this,ofNorm,
653                        &FGFCS::GetDrPos,
654                        &FGFCS::SetDrPos,
655                        true);
656   PropertyManager->Tie("fcs/mag-rudder-pos-rad", this,ofMag,
657                        &FGFCS::GetDrPos,
658                        &FGFCS::SetDrPos,
659                        true);
660
661   PropertyManager->Tie("fcs/flap-pos-deg", this,ofRad,
662                        &FGFCS::GetDfPos,
663                        &FGFCS::SetDfPos,
664                        true);
665   PropertyManager->Tie("fcs/flap-pos-norm", this,ofNorm,
666                        &FGFCS::GetDfPos,
667                        &FGFCS::SetDfPos,
668                        true);
669
670   PropertyManager->Tie("fcs/speedbrake-pos-rad", this,ofRad,
671                        &FGFCS::GetDsbPos,
672                        &FGFCS::SetDsbPos,
673                        true);
674   PropertyManager->Tie("fcs/speedbrake-pos-norm", this,ofNorm,
675                        &FGFCS::GetDsbPos,
676                        &FGFCS::SetDsbPos,
677                        true);
678   PropertyManager->Tie("fcs/mag-speedbrake-pos-rad", this,ofMag,
679                        &FGFCS::GetDsbPos,
680                        &FGFCS::SetDsbPos,
681                        true);
682
683   PropertyManager->Tie("fcs/spoiler-pos-rad", this,ofRad,
684                        &FGFCS::GetDspPos,
685                        &FGFCS::SetDspPos,
686                        true);
687   PropertyManager->Tie("fcs/spoiler-pos-norm", this,ofNorm,
688                        &FGFCS::GetDspPos,
689                        &FGFCS::SetDspPos,
690                        true);
691   PropertyManager->Tie("fcs/mag-spoiler-pos-rad", this,ofMag,
692                        &FGFCS::GetDspPos,
693                        &FGFCS::SetDspPos,
694                        true);
695
696   PropertyManager->Tie("gear/gear-pos-norm", this,
697                        &FGFCS::GetGearPos,
698                        &FGFCS::SetGearPos,
699                        true);
700
701   PropertyManager->Tie("ap/elevator_cmd", this,
702                        &FGFCS::GetAPDeCmd,
703                        &FGFCS::SetAPDeCmd,
704                        true);
705
706   PropertyManager->Tie("ap/aileron_cmd", this,
707                        &FGFCS::GetAPDaCmd,
708                        &FGFCS::SetAPDaCmd,
709                        true);
710
711   PropertyManager->Tie("ap/rudder_cmd", this,
712                        &FGFCS::GetAPDrCmd,
713                        &FGFCS::SetAPDrCmd,
714                        true);
715
716   PropertyManager->Tie("ap/throttle_cmd", this,
717                        &FGFCS::GetAPThrottleCmd,
718                        &FGFCS::SetAPThrottleCmd,
719                        true);
720
721   PropertyManager->Tie("ap/attitude_setpoint", this,
722                        &FGFCS::GetAPAttitudeSetPt,
723                        &FGFCS::SetAPAttitudeSetPt,
724                        true);
725
726   PropertyManager->Tie("ap/altitude_setpoint", this,
727                        &FGFCS::GetAPAltitudeSetPt,
728                        &FGFCS::SetAPAltitudeSetPt,
729                        true);
730
731   PropertyManager->Tie("ap/heading_setpoint", this,
732                        &FGFCS::GetAPHeadingSetPt,
733                        &FGFCS::SetAPHeadingSetPt,
734                        true);
735
736   PropertyManager->Tie("ap/airspeed_setpoint", this,
737                        &FGFCS::GetAPAirspeedSetPt,
738                        &FGFCS::SetAPAirspeedSetPt,
739                        true);
740
741   PropertyManager->Tie("ap/acquire_attitude", this,
742                        &FGFCS::GetAPAcquireAttitude,
743                        &FGFCS::SetAPAcquireAttitude,
744                        true);
745
746   PropertyManager->Tie("ap/acquire_altitude", this,
747                        &FGFCS::GetAPAcquireAltitude,
748                        &FGFCS::SetAPAcquireAltitude,
749                        true);
750
751   PropertyManager->Tie("ap/acquire_heading", this,
752                        &FGFCS::GetAPAcquireHeading,
753                        &FGFCS::SetAPAcquireHeading,
754                        true);
755
756   PropertyManager->Tie("ap/acquire_airspeed", this,
757                        &FGFCS::GetAPAcquireAirspeed,
758                        &FGFCS::SetAPAcquireAirspeed,
759                        true);
760
761   PropertyManager->Tie("ap/attitude_hold", this,
762                        &FGFCS::GetAPAttitudeHold,
763                        &FGFCS::SetAPAttitudeHold,
764                        true);
765
766   PropertyManager->Tie("ap/altitude_hold", this,
767                        &FGFCS::GetAPAltitudeHold,
768                        &FGFCS::SetAPAltitudeHold,
769                        true);
770
771   PropertyManager->Tie("ap/heading_hold", this,
772                        &FGFCS::GetAPHeadingHold,
773                        &FGFCS::SetAPHeadingHold,
774                        true);
775
776   PropertyManager->Tie("ap/airspeed_hold", this,
777                        &FGFCS::GetAPAirspeedHold,
778                        &FGFCS::SetAPAirspeedHold,
779                        true);
780
781   PropertyManager->Tie("ap/wingslevel_hold", this,
782                        &FGFCS::GetAPWingsLevelHold,
783                        &FGFCS::SetAPWingsLevelHold,
784                        true);
785 }
786
787 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
788
789 void FGFCS::bindModel(void)
790 {
791   unsigned i;
792   char tmp[80];
793
794
795   for (i=0; i<ThrottleCmd.size(); i++) {
796     snprintf(tmp,80,"fcs/throttle-cmd-norm[%u]",i);
797     PropertyManager->Tie( tmp,this,i,
798                           &FGFCS::GetThrottleCmd,
799                           &FGFCS::SetThrottleCmd,
800                           true );
801     snprintf(tmp,80,"fcs/throttle-pos-norm[%u]",i);
802     PropertyManager->Tie( tmp,this,i,
803                           &FGFCS::GetThrottlePos,
804                           &FGFCS::SetThrottlePos,
805                           true );
806     if ( MixtureCmd.size() > i ) {
807       snprintf(tmp,80,"fcs/mixture-cmd-norm[%u]",i);
808       PropertyManager->Tie( tmp,this,i,
809                             &FGFCS::GetMixtureCmd,
810                             &FGFCS::SetMixtureCmd,
811                             true );
812       snprintf(tmp,80,"fcs/mixture-pos-norm[%u]",i);
813       PropertyManager->Tie( tmp,this,i,
814                             &FGFCS::GetMixturePos,
815                             &FGFCS::SetMixturePos,
816                             true );
817     }
818     if ( PropAdvanceCmd.size() > i ) {
819       snprintf(tmp,80,"fcs/advance-cmd-norm[%u]",i);
820       PropertyManager->Tie( tmp,this,i,
821                             &FGFCS::GetPropAdvanceCmd,
822                             &FGFCS::SetPropAdvanceCmd,
823                             true );
824       snprintf(tmp,80,"fcs/advance-pos-norm[%u]",i);
825       PropertyManager->Tie( tmp,this,i,
826                             &FGFCS::GetPropAdvance,
827                             &FGFCS::SetPropAdvance,
828                             true );
829     }
830   }
831 }
832
833 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
834
835 void FGFCS::unbind(FGPropertyManager *node)
836 {
837   int N = node->nChildren();
838   for(int i=0;i<N;i++) {
839     if(node->getChild(i)->nChildren() ) {
840       unbind( (FGPropertyManager*)node->getChild(i) );
841     } else if( node->getChild(i)->isTied() ) {
842       node->getChild(i)->untie();
843     }
844   }
845 }
846
847 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
848 //    The bitmasked value choices are as follows:
849 //    unset: In this case (the default) JSBSim would only print
850 //       out the normally expected messages, essentially echoing
851 //       the config files as they are read. If the environment
852 //       variable is not set, debug_lvl is set to 1 internally
853 //    0: This requests JSBSim not to output any messages
854 //       whatsoever.
855 //    1: This value explicity requests the normal JSBSim
856 //       startup messages
857 //    2: This value asks for a message to be printed out when
858 //       a class is instantiated
859 //    4: When this value is set, a message is displayed when a
860 //       FGModel object executes its Run() method
861 //    8: When this value is set, various runtime state variables
862 //       are printed out periodically
863 //    16: When set various parameters are sanity checked and
864 //       a message is printed out when they go out of bounds
865
866 void FGFCS::Debug(int from)
867 {
868   if (debug_lvl <= 0) return;
869
870   if (debug_lvl & 1) { // Standard console startup message output
871     if (from == 0) { // Constructor
872
873     }
874   }
875   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
876     if (from == 0) cout << "Instantiated: FGFCS" << endl;
877     if (from == 1) cout << "Destroyed:    FGFCS" << endl;
878   }
879   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
880   }
881   if (debug_lvl & 8 ) { // Runtime state variables
882   }
883   if (debug_lvl & 16) { // Sanity checking
884   }
885   if (debug_lvl & 64) {
886     if (from == 0) { // Constructor
887       cout << IdSrc << endl;
888       cout << IdHdr << endl;
889     }
890   }
891 }
892
893 }