]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/FGFCS.cpp
Make yasim accept the launchbar and hook properties. They are not tied to anything...
[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   bind();
79   for (i=0;i<=NForms;i++) {
80     DePos[i] = DaLPos[i] = DaRPos[i] = DrPos[i] = 0.0;
81     DfPos[i] = DsbPos[i] = DspPos[i] = 0.0;
82   }
83
84   for (i=0;i<NNorm;i++) { ToNormalize[i]=-1;}
85   Debug(0);
86 }
87
88 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89
90 FGFCS::~FGFCS()
91 {
92   unbind( PropertyManager->GetNode("fcs") );
93   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
105   unsigned int i;
106
107   for (i=0;i<APComponents.size();i++) delete APComponents[i];
108   for (i=0;i<FCSComponents.size();i++) delete FCSComponents[i];
109
110   Debug(1);
111 }
112
113 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114
115 bool FGFCS::Run(void)
116 {
117   unsigned int i;
118
119   if (FGModel::Run()) return true; // fast exit if nothing to do
120
121   // Set the default engine commands
122   for (i=0; i<ThrottlePos.size(); i++) ThrottlePos[i] = ThrottleCmd[i];
123   for (i=0; i<MixturePos.size(); i++) MixturePos[i] = MixtureCmd[i];
124   for (i=0; i<PropAdvance.size(); i++) PropAdvance[i] = PropAdvanceCmd[i];
125
126   // Set the default steering angle
127   for (i=0; i<SteerPosDeg.size(); i++) {
128     FGLGear* gear = GroundReactions->GetGearUnit(i);
129     SteerPosDeg[i] = gear->GetDefaultSteerAngle( GetDsCmd() );
130   }
131
132   for (i=0; i<APComponents.size(); i++) APComponents[i]->Run(); // cycle AP components
133   for (i=0; i<FCSComponents.size(); i++) FCSComponents[i]->Run(); // cycle FCS components
134
135   if (DoNormalize) Normalize();
136
137   return false;
138 }
139
140 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
141
142 void FGFCS::SetThrottleCmd(int engineNum, double setting)
143 {
144   unsigned int ctr;
145
146   if (engineNum < (int)ThrottlePos.size()) {
147     if (engineNum < 0) {
148       for (ctr=0;ctr<ThrottleCmd.size();ctr++) ThrottleCmd[ctr] = setting;
149     } else {
150       ThrottleCmd[engineNum] = setting;
151     }
152   } else {
153     cerr << "Throttle " << engineNum << " does not exist! " << ThrottleCmd.size()
154          << " engines exist, but attempted throttle command is for engine "
155          << engineNum << endl;
156   }
157 }
158
159 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
160
161 void FGFCS::SetThrottlePos(int engineNum, double setting)
162 {
163   unsigned int ctr;
164
165   if (engineNum < (int)ThrottlePos.size()) {
166     if (engineNum < 0) {
167       for (ctr=0;ctr<ThrottlePos.size();ctr++) ThrottlePos[ctr] = setting;
168     } else {
169       ThrottlePos[engineNum] = setting;
170     }
171   } else {
172     cerr << "Throttle " << engineNum << " does not exist! " << ThrottlePos.size()
173          << " engines exist, but attempted throttle position setting is for engine "
174          << engineNum << endl;
175   }
176 }
177
178 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
179
180 double FGFCS::GetThrottleCmd(int engineNum) const
181 {
182   if (engineNum < (int)ThrottlePos.size()) {
183     if (engineNum < 0) {
184        cerr << "Cannot get throttle value for ALL engines" << endl;
185     } else {
186       return ThrottleCmd[engineNum];
187     }
188   } else {
189     cerr << "Throttle " << engineNum << " does not exist! " << ThrottleCmd.size()
190          << " engines exist, but throttle setting for engine " << engineNum
191          << " is selected" << endl;
192   }
193   return 0.0;
194 }
195
196 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
197
198 double FGFCS::GetThrottlePos(int engineNum) const
199 {
200   if (engineNum < (int)ThrottlePos.size()) {
201     if (engineNum < 0) {
202        cerr << "Cannot get throttle value for ALL engines" << endl;
203     } else {
204       return ThrottlePos[engineNum];
205     }
206   } else {
207     cerr << "Throttle " << engineNum << " does not exist! " << ThrottlePos.size()
208          << " engines exist, but attempted throttle position setting is for engine "
209          << engineNum << endl;
210   }
211   return 0.0;
212 }
213
214 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
215
216 void FGFCS::SetMixtureCmd(int engineNum, double setting)
217 {
218   unsigned int ctr;
219
220   if (engineNum < (int)ThrottlePos.size()) {
221     if (engineNum < 0) {
222       for (ctr=0;ctr<MixtureCmd.size();ctr++) MixtureCmd[ctr] = setting;
223     } else {
224       MixtureCmd[engineNum] = setting;
225     }
226   }
227 }
228
229 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
230
231 void FGFCS::SetMixturePos(int engineNum, double setting)
232 {
233   unsigned int ctr;
234
235   if (engineNum < (int)ThrottlePos.size()) {
236     if (engineNum < 0) {
237       for (ctr=0;ctr<=MixtureCmd.size();ctr++) MixturePos[ctr] = MixtureCmd[ctr];
238     } else {
239       MixturePos[engineNum] = setting;
240     }
241   }
242 }
243
244 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
245
246 void FGFCS::SetPropAdvanceCmd(int engineNum, double setting)
247 {
248   unsigned int ctr;
249
250   if (engineNum < (int)ThrottlePos.size()) {
251     if (engineNum < 0) {
252       for (ctr=0;ctr<PropAdvanceCmd.size();ctr++) PropAdvanceCmd[ctr] = setting;
253     } else {
254       PropAdvanceCmd[engineNum] = setting;
255     }
256   }
257 }
258
259 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
260
261 void FGFCS::SetPropAdvance(int engineNum, double setting)
262 {
263   unsigned int ctr;
264
265   if (engineNum < (int)ThrottlePos.size()) {
266     if (engineNum < 0) {
267       for (ctr=0;ctr<=PropAdvanceCmd.size();ctr++) PropAdvance[ctr] = PropAdvanceCmd[ctr];
268     } else {
269       PropAdvance[engineNum] = setting;
270     }
271   }
272 }
273
274 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
275
276 bool FGFCS::Load(FGConfigFile* AC_cfg)
277 {
278   string token, delimiter;
279   string name, file, fname;
280   unsigned i;
281   vector <FGFCSComponent*> *Components;
282   FGConfigFile *FCS_cfg;
283
284   Components=0;
285   // Determine if the FCS/Autopilot is defined inline in the aircraft configuration
286   // file or in a separate file. Set up the config file class as appropriate.
287
288   delimiter = AC_cfg->GetValue();
289   name  = AC_cfg->GetValue("NAME");
290   fname = AC_cfg->GetValue("FILE");
291
292   if ( AC_cfg->GetValue("NORMALIZE") == "FALSE") {
293     DoNormalize = false;
294     cout << "    Automatic Control Surface Normalization Disabled" << endl;
295   }
296
297 # ifndef macintosh
298 //  file = "control/" + fname + ".xml";
299   file = FDMExec->GetAircraftPath() + "/" + FDMExec->GetModelName() + "/" + fname + ".xml";
300 # else
301 //  file = "control;" + fname + ".xml";
302   file = FDMExec->GetAircraftPath() + ";" + FDMExec->GetModelName() + ";" + 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     Name = "Autopilot: " + name;
326   } else if (delimiter == "FLIGHT_CONTROL") {
327     Components = &FCSComponents;
328     Name = "FCS: " + name;
329   } else {
330     cerr << endl << "Unknown FCS delimiter" << endl << endl;
331   }
332
333   if (debug_lvl > 0) cout << "    Control System Name: " << Name << endl;
334
335   while ((token = AC_cfg->GetValue()) != string("/" + delimiter)) {
336     if (token == "COMPONENT") {
337       token = AC_cfg->GetValue("TYPE");
338       if (debug_lvl > 0) cout << endl << "    Loading Component \""
339                               << AC_cfg->GetValue("NAME")
340                               << "\" of type: " << token << endl;
341       if ((token == "LAG_FILTER") ||
342           (token == "LEAD_LAG_FILTER") ||
343           (token == "SECOND_ORDER_FILTER") ||
344           (token == "WASHOUT_FILTER") ||
345           (token == "INTEGRATOR") ) {
346         Components->push_back(new FGFilter(this, AC_cfg));
347       } else if ((token == "PURE_GAIN") ||
348                  (token == "SCHEDULED_GAIN") ||
349                  (token == "AEROSURFACE_SCALE") ) {
350
351         Components->push_back(new FGGain(this, AC_cfg));
352
353       } else if (token == "SUMMER") {
354         Components->push_back(new FGSummer(this, AC_cfg));
355       } else if (token == "DEADBAND") {
356         Components->push_back(new FGDeadBand(this, AC_cfg));
357       } else if (token == "GRADIENT") {
358         Components->push_back(new FGGradient(this, AC_cfg));
359       } else if (token == "SWITCH") {
360         Components->push_back(new FGSwitch(this, AC_cfg));
361       } else if (token == "KINEMAT") {
362         Components->push_back(new FGKinemat(this, AC_cfg));
363       } else {
364         cerr << "Unknown token [" << token << "] in FCS portion of config file" << endl;
365         return false;
366       }
367       if (AC_cfg->GetNextConfigLine() == "EOF") break;
368     }
369   }
370
371   //collect information for normalizing control surfaces
372
373   string nodeName;
374   for (i=0; i<Components->size(); i++) {
375
376     if ( (((*Components)[i])->GetType() == "AEROSURFACE_SCALE"
377           || ((*Components)[i])->GetType() == "KINEMAT")
378                     && ((*Components)[i])->GetOutputNode() ) {
379       nodeName = ((*Components)[i])->GetOutputNode()->GetName();
380       if ( nodeName == "elevator-pos-rad" ) {
381         ToNormalize[iDe]=i;
382       } else if ( nodeName  == "left-aileron-pos-rad"
383                    || nodeName == "aileron-pos-rad" ) {
384         ToNormalize[iDaL]=i;
385       } else if ( nodeName == "right-aileron-pos-rad" ) {
386         ToNormalize[iDaR]=i;
387       } else if ( nodeName == "rudder-pos-rad" ) {
388         ToNormalize[iDr]=i;
389       } else if ( nodeName == "speedbrake-pos-rad" ) {
390         ToNormalize[iDsb]=i;
391       } else if ( nodeName == "spoiler-pos-rad" ) {
392         ToNormalize[iDsp]=i;
393       } else if ( nodeName == "flap-pos-deg" ) {
394         ToNormalize[iDf]=i;
395       }
396     }
397   }
398
399   if (delimiter == "FLIGHT_CONTROL") bindModel();
400
401   return true;
402 }
403
404 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
405
406 double FGFCS::GetBrake(FGLGear::BrakeGroup bg)
407 {
408   switch (bg) {
409   case FGLGear::bgLeft:
410     return LeftBrake;
411   case FGLGear::bgRight:
412     return RightBrake;
413   case FGLGear::bgCenter:
414     return CenterBrake;
415   default:
416     cerr << "GetBrake asked to return a bogus brake value" << endl;
417   }
418   return 0.0;
419 }
420
421 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
422
423 string FGFCS::GetComponentStrings(string delimeter)
424 {
425   unsigned int comp;
426   string CompStrings = "";
427   bool firstime = true;
428
429   for (comp = 0; comp < FCSComponents.size(); comp++) {
430     if (firstime) firstime = false;
431     else          CompStrings += delimeter;
432
433     CompStrings += FCSComponents[comp]->GetName();
434   }
435
436   for (comp = 0; comp < APComponents.size(); comp++)
437   {
438     CompStrings += delimeter;
439     CompStrings += APComponents[comp]->GetName();
440   }
441
442   return CompStrings;
443 }
444
445 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
446
447 string FGFCS::GetComponentValues(string delimeter)
448 {
449   unsigned int comp;
450   string CompValues = "";
451   char buffer[12];
452   bool firstime = true;
453
454   for (comp = 0; comp < FCSComponents.size(); comp++) {
455     if (firstime) firstime = false;
456     else          CompValues += delimeter;
457
458     sprintf(buffer, "%9.6f", FCSComponents[comp]->GetOutput());
459     CompValues += string(buffer);
460   }
461
462   for (comp = 0; comp < APComponents.size(); comp++) {
463     sprintf(buffer, "%s%9.6f", delimeter.c_str(), APComponents[comp]->GetOutput());
464     CompValues += string(buffer);
465   }
466
467   return CompValues;
468 }
469
470 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
471
472 void FGFCS::AddThrottle(void)
473 {
474   ThrottleCmd.push_back(0.0);
475   ThrottlePos.push_back(0.0);
476   MixtureCmd.push_back(0.0);     // assume throttle and mixture are coupled
477   MixturePos.push_back(0.0);
478   PropAdvanceCmd.push_back(0.0); // assume throttle and prop pitch are coupled
479   PropAdvance.push_back(0.0);
480 }
481
482 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
483
484 void FGFCS::AddGear(void)
485 {
486   SteerPosDeg.push_back(0.0);
487 }
488
489 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
490
491 void FGFCS::Normalize(void) {
492
493   //not all of these are guaranteed to be defined for every model
494   //those that are have an index >=0 in the ToNormalize array
495   //ToNormalize is filled in Load()
496
497   if ( ToNormalize[iDe] > -1 ) {
498     DePos[ofNorm] = FCSComponents[ToNormalize[iDe]]->GetOutputPct();
499   }
500
501   if ( ToNormalize[iDaL] > -1 ) {
502     DaLPos[ofNorm] = FCSComponents[ToNormalize[iDaL]]->GetOutputPct();
503   }
504
505   if ( ToNormalize[iDaR] > -1 ) {
506     DaRPos[ofNorm] = FCSComponents[ToNormalize[iDaR]]->GetOutputPct();
507   }
508
509   if ( ToNormalize[iDr] > -1 ) {
510     DrPos[ofNorm] = FCSComponents[ToNormalize[iDr]]->GetOutputPct();
511   }
512
513   if ( ToNormalize[iDsb] > -1 ) {
514     DsbPos[ofNorm] = FCSComponents[ToNormalize[iDsb]]->GetOutputPct();
515   }
516
517   if ( ToNormalize[iDsp] > -1 ) {
518     DspPos[ofNorm] = FCSComponents[ToNormalize[iDsp]]->GetOutputPct();
519   }
520
521   if ( ToNormalize[iDf] > -1 ) {
522     DfPos[ofNorm] = FCSComponents[ToNormalize[iDf]]->GetOutputPct();
523   }
524
525   DePos[ofMag]  = fabs(DePos[ofRad]);
526   DaLPos[ofMag] = fabs(DaLPos[ofRad]);
527   DaRPos[ofMag] = fabs(DaRPos[ofRad]);
528   DrPos[ofMag]  = fabs(DrPos[ofRad]);
529   DsbPos[ofMag] = fabs(DsbPos[ofRad]);
530   DspPos[ofMag] = fabs(DspPos[ofRad]);
531   DfPos[ofMag]  = fabs(DfPos[ofRad]);
532
533 }
534
535 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
536
537 void FGFCS::bind(void)
538 {
539   PropertyManager->Tie("fcs/aileron-cmd-norm", this,
540                        &FGFCS::GetDaCmd,
541                        &FGFCS::SetDaCmd,
542                        true);
543   PropertyManager->Tie("fcs/elevator-cmd-norm", this,
544                        &FGFCS::GetDeCmd,
545                        &FGFCS::SetDeCmd,
546                        true);
547   PropertyManager->Tie("fcs/rudder-cmd-norm", this,
548                        &FGFCS::GetDrCmd,
549                        &FGFCS::SetDrCmd,
550                        true);
551   PropertyManager->Tie("fcs/steer-cmd-norm", this,
552                        &FGFCS::GetDsCmd,
553                        &FGFCS::SetDsCmd,
554                        true);
555   PropertyManager->Tie("fcs/flap-cmd-norm", this,
556                        &FGFCS::GetDfCmd,
557                        &FGFCS::SetDfCmd,
558                        true);
559   PropertyManager->Tie("fcs/speedbrake-cmd-norm", this,
560                        &FGFCS::GetDsbCmd,
561                        &FGFCS::SetDsbCmd,
562                        true);
563   PropertyManager->Tie("fcs/spoiler-cmd-norm", this,
564                        &FGFCS::GetDspCmd,
565                        &FGFCS::SetDspCmd,
566                        true);
567   PropertyManager->Tie("fcs/pitch-trim-cmd-norm", this,
568                        &FGFCS::GetPitchTrimCmd,
569                        &FGFCS::SetPitchTrimCmd,
570                        true);
571   PropertyManager->Tie("fcs/roll-trim-cmd-norm", this,
572                        &FGFCS::GetRollTrimCmd,
573                        &FGFCS::SetRollTrimCmd,
574                        true);
575   PropertyManager->Tie("fcs/yaw-trim-cmd-norm", this,
576                        &FGFCS::GetYawTrimCmd,
577                        &FGFCS::SetYawTrimCmd,
578                        true);
579   PropertyManager->Tie("gear/gear-cmd-norm", this,
580                        &FGFCS::GetGearCmd,
581                        &FGFCS::SetGearCmd,
582                        true);
583
584   PropertyManager->Tie("fcs/left-aileron-pos-rad", this,ofRad,
585                        &FGFCS::GetDaLPos,
586                        &FGFCS::SetDaLPos,
587                        true);
588   PropertyManager->Tie("fcs/left-aileron-pos-norm", this,ofNorm,
589                        &FGFCS::GetDaLPos,
590                        &FGFCS::SetDaLPos,
591                        true);
592   PropertyManager->Tie("fcs/mag-left-aileron-pos-rad", this,ofMag,
593                        &FGFCS::GetDaLPos,
594                        &FGFCS::SetDaLPos,
595                        true);
596
597   PropertyManager->Tie("fcs/right-aileron-pos-rad", this,ofRad,
598                        &FGFCS::GetDaRPos,
599                        &FGFCS::SetDaRPos,
600                        true);
601   PropertyManager->Tie("fcs/right-aileron-pos-norm", this,ofNorm,
602                        &FGFCS::GetDaRPos,
603                        &FGFCS::SetDaRPos,
604                        true);
605   PropertyManager->Tie("fcs/mag-right-aileron-pos-rad", this,ofMag,
606                        &FGFCS::GetDaRPos,
607                        &FGFCS::SetDaRPos,
608                        true);
609
610   PropertyManager->Tie("fcs/elevator-pos-rad", this, ofRad,
611                        &FGFCS::GetDePos,
612                        &FGFCS::SetDePos,
613                        true );
614   PropertyManager->Tie("fcs/elevator-pos-norm", this,ofNorm,
615                        &FGFCS::GetDePos,
616                        &FGFCS::SetDePos,
617                        true );
618   PropertyManager->Tie("fcs/mag-elevator-pos-rad", this,ofMag,
619                        &FGFCS::GetDePos,
620                        &FGFCS::SetDePos,
621                        true );
622
623   PropertyManager->Tie("fcs/rudder-pos-rad", this,ofRad,
624                        &FGFCS::GetDrPos,
625                        &FGFCS::SetDrPos,
626                        true);
627   PropertyManager->Tie("fcs/rudder-pos-norm", this,ofNorm,
628                        &FGFCS::GetDrPos,
629                        &FGFCS::SetDrPos,
630                        true);
631   PropertyManager->Tie("fcs/mag-rudder-pos-rad", this,ofMag,
632                        &FGFCS::GetDrPos,
633                        &FGFCS::SetDrPos,
634                        true);
635
636   PropertyManager->Tie("fcs/flap-pos-deg", this,ofRad,
637                        &FGFCS::GetDfPos,
638                        &FGFCS::SetDfPos,
639                        true);
640   PropertyManager->Tie("fcs/flap-pos-norm", this,ofNorm,
641                        &FGFCS::GetDfPos,
642                        &FGFCS::SetDfPos,
643                        true);
644
645   PropertyManager->Tie("fcs/speedbrake-pos-rad", this,ofRad,
646                        &FGFCS::GetDsbPos,
647                        &FGFCS::SetDsbPos,
648                        true);
649   PropertyManager->Tie("fcs/speedbrake-pos-norm", this,ofNorm,
650                        &FGFCS::GetDsbPos,
651                        &FGFCS::SetDsbPos,
652                        true);
653   PropertyManager->Tie("fcs/mag-speedbrake-pos-rad", this,ofMag,
654                        &FGFCS::GetDsbPos,
655                        &FGFCS::SetDsbPos,
656                        true);
657
658   PropertyManager->Tie("fcs/spoiler-pos-rad", this,ofRad,
659                        &FGFCS::GetDspPos,
660                        &FGFCS::SetDspPos,
661                        true);
662   PropertyManager->Tie("fcs/spoiler-pos-norm", this,ofNorm,
663                        &FGFCS::GetDspPos,
664                        &FGFCS::SetDspPos,
665                        true);
666   PropertyManager->Tie("fcs/mag-spoiler-pos-rad", this,ofMag,
667                        &FGFCS::GetDspPos,
668                        &FGFCS::SetDspPos,
669                        true);
670
671   PropertyManager->Tie("gear/gear-pos-norm", this,
672                        &FGFCS::GetGearPos,
673                        &FGFCS::SetGearPos,
674                        true);
675
676   PropertyManager->Tie("ap/elevator_cmd", this,
677                        &FGFCS::GetAPDeCmd,
678                        &FGFCS::SetAPDeCmd,
679                        true);
680
681   PropertyManager->Tie("ap/aileron_cmd", this,
682                        &FGFCS::GetAPDaCmd,
683                        &FGFCS::SetAPDaCmd,
684                        true);
685
686   PropertyManager->Tie("ap/rudder_cmd", this,
687                        &FGFCS::GetAPDrCmd,
688                        &FGFCS::SetAPDrCmd,
689                        true);
690
691   PropertyManager->Tie("ap/throttle_cmd", this,
692                        &FGFCS::GetAPThrottleCmd,
693                        &FGFCS::SetAPThrottleCmd,
694                        true);
695
696   PropertyManager->Tie("ap/attitude_setpoint", this,
697                        &FGFCS::GetAPAttitudeSetPt,
698                        &FGFCS::SetAPAttitudeSetPt,
699                        true);
700
701   PropertyManager->Tie("ap/altitude_setpoint", this,
702                        &FGFCS::GetAPAltitudeSetPt,
703                        &FGFCS::SetAPAltitudeSetPt,
704                        true);
705
706   PropertyManager->Tie("ap/heading_setpoint", this,
707                        &FGFCS::GetAPHeadingSetPt,
708                        &FGFCS::SetAPHeadingSetPt,
709                        true);
710
711   PropertyManager->Tie("ap/airspeed_setpoint", this,
712                        &FGFCS::GetAPAirspeedSetPt,
713                        &FGFCS::SetAPAirspeedSetPt,
714                        true);
715
716   PropertyManager->Tie("ap/acquire_attitude", this,
717                        &FGFCS::GetAPAcquireAttitude,
718                        &FGFCS::SetAPAcquireAttitude,
719                        true);
720
721   PropertyManager->Tie("ap/acquire_altitude", this,
722                        &FGFCS::GetAPAcquireAltitude,
723                        &FGFCS::SetAPAcquireAltitude,
724                        true);
725
726   PropertyManager->Tie("ap/acquire_heading", this,
727                        &FGFCS::GetAPAcquireHeading,
728                        &FGFCS::SetAPAcquireHeading,
729                        true);
730
731   PropertyManager->Tie("ap/acquire_airspeed", this,
732                        &FGFCS::GetAPAcquireAirspeed,
733                        &FGFCS::SetAPAcquireAirspeed,
734                        true);
735
736   PropertyManager->Tie("ap/attitude_hold", this,
737                        &FGFCS::GetAPAttitudeHold,
738                        &FGFCS::SetAPAttitudeHold,
739                        true);
740
741   PropertyManager->Tie("ap/altitude_hold", this,
742                        &FGFCS::GetAPAltitudeHold,
743                        &FGFCS::SetAPAltitudeHold,
744                        true);
745
746   PropertyManager->Tie("ap/heading_hold", this,
747                        &FGFCS::GetAPHeadingHold,
748                        &FGFCS::SetAPHeadingHold,
749                        true);
750
751   PropertyManager->Tie("ap/airspeed_hold", this,
752                        &FGFCS::GetAPAirspeedHold,
753                        &FGFCS::SetAPAirspeedHold,
754                        true);
755
756   PropertyManager->Tie("ap/wingslevel_hold", this,
757                        &FGFCS::GetAPWingsLevelHold,
758                        &FGFCS::SetAPWingsLevelHold,
759                        true);
760 }
761
762 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
763
764 void FGFCS::bindModel(void)
765 {
766   unsigned i;
767   char tmp[80];
768
769   for (i=0; i<ThrottleCmd.size(); i++) {
770     snprintf(tmp,80,"fcs/throttle-cmd-norm[%u]",i);
771     PropertyManager->Tie( tmp,this,i,
772                           &FGFCS::GetThrottleCmd,
773                           &FGFCS::SetThrottleCmd,
774                           true );
775     snprintf(tmp,80,"fcs/throttle-pos-norm[%u]",i);
776     PropertyManager->Tie( tmp,this,i,
777                           &FGFCS::GetThrottlePos,
778                           &FGFCS::SetThrottlePos,
779                           true );
780     if ( MixtureCmd.size() > i ) {
781       snprintf(tmp,80,"fcs/mixture-cmd-norm[%u]",i);
782       PropertyManager->Tie( tmp,this,i,
783                             &FGFCS::GetMixtureCmd,
784                             &FGFCS::SetMixtureCmd,
785                             true );
786       snprintf(tmp,80,"fcs/mixture-pos-norm[%u]",i);
787       PropertyManager->Tie( tmp,this,i,
788                             &FGFCS::GetMixturePos,
789                             &FGFCS::SetMixturePos,
790                             true );
791     }
792     if ( PropAdvanceCmd.size() > i ) {
793       snprintf(tmp,80,"fcs/advance-cmd-norm[%u]",i);
794       PropertyManager->Tie( tmp,this,i,
795                             &FGFCS::GetPropAdvanceCmd,
796                             &FGFCS::SetPropAdvanceCmd,
797                             true );
798       snprintf(tmp,80,"fcs/advance-pos-norm[%u]",i);
799       PropertyManager->Tie( tmp,this,i,
800                             &FGFCS::GetPropAdvance,
801                             &FGFCS::SetPropAdvance,
802                             true );
803     }
804   }
805
806   for (i=0; i<SteerPosDeg.size(); i++) {
807     if (GroundReactions->GetGearUnit(i)->GetSteerable()) {
808       snprintf(tmp,80,"fcs/steer-pos-deg[%u]",i);
809       PropertyManager->Tie( tmp, this, i,
810                             &FGFCS::GetSteerPosDeg,
811                             &FGFCS::SetSteerPosDeg,
812                             true );
813     }
814   }
815 }
816
817 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
818
819 void FGFCS::unbind(FGPropertyManager *node)
820 {
821   int N = node->nChildren();
822   for(int i=0;i<N;i++) {
823     if(node->getChild(i)->nChildren() ) {
824       unbind( (FGPropertyManager*)node->getChild(i) );
825     } else if( node->getChild(i)->isTied() ) {
826       node->getChild(i)->untie();
827     }
828   }
829 }
830
831 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
832 //    The bitmasked value choices are as follows:
833 //    unset: In this case (the default) JSBSim would only print
834 //       out the normally expected messages, essentially echoing
835 //       the config files as they are read. If the environment
836 //       variable is not set, debug_lvl is set to 1 internally
837 //    0: This requests JSBSim not to output any messages
838 //       whatsoever.
839 //    1: This value explicity requests the normal JSBSim
840 //       startup messages
841 //    2: This value asks for a message to be printed out when
842 //       a class is instantiated
843 //    4: When this value is set, a message is displayed when a
844 //       FGModel object executes its Run() method
845 //    8: When this value is set, various runtime state variables
846 //       are printed out periodically
847 //    16: When set various parameters are sanity checked and
848 //       a message is printed out when they go out of bounds
849
850 void FGFCS::Debug(int from)
851 {
852   if (debug_lvl <= 0) return;
853
854   if (debug_lvl & 1) { // Standard console startup message output
855     if (from == 0) { // Constructor
856
857     }
858   }
859   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
860     if (from == 0) cout << "Instantiated: FGFCS" << endl;
861     if (from == 1) cout << "Destroyed:    FGFCS" << endl;
862   }
863   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
864   }
865   if (debug_lvl & 8 ) { // Runtime state variables
866   }
867   if (debug_lvl & 16) { // Sanity checking
868   }
869   if (debug_lvl & 64) {
870     if (from == 0) { // Constructor
871       cout << IdSrc << endl;
872       cout << IdHdr << endl;
873     }
874   }
875 }
876
877 }