]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/FGFCS.cpp
d14fbe3221b36b3efee77773abd19e8526da8444
[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 "FGState.h"
42 #include "FGFDMExec.h"
43 #include "FGAtmosphere.h"
44 #include "FGAircraft.h"
45 #include "FGTranslation.h"
46 #include "FGRotation.h"
47 #include "FGPosition.h"
48 #include "FGAuxiliary.h"
49 #include "FGOutput.h"
50 #include "FGPropertyManager.h"
51
52 #include "filtersjb/FGFilter.h"
53 #include "filtersjb/FGDeadBand.h"
54 #include "filtersjb/FGGain.h"
55 #include "filtersjb/FGGradient.h"
56 #include "filtersjb/FGSwitch.h"
57 #include "filtersjb/FGSummer.h"
58 #include "filtersjb/FGKinemat.h"
59
60 static const char *IdSrc = "$Id$";
61 static const char *IdHdr = ID_FCS;
62
63 #if defined(WIN32) && !defined(__CYGWIN__)
64 #define snprintf _snprintf
65 #endif
66
67 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
68 CLASS IMPLEMENTATION
69 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
70
71 FGFCS::FGFCS(FGFDMExec* fdmex) : FGModel(fdmex)
72 {
73   int i;
74   Name = "FGFCS";
75
76   DaCmd = DeCmd = DrCmd = DfCmd = DsbCmd = DspCmd = 0.0;
77   PTrimCmd = YTrimCmd = RTrimCmd = 0.0;
78   GearCmd = GearPos = 1; // default to gear down
79   LeftBrake = RightBrake = CenterBrake = 0.0;
80   DoNormalize=true;
81   
82   bind();
83   for (i=0;i<=NForms;i++) {
84     DePos[i] = DaLPos[i] = DaRPos[i] = DrPos[i] = 0.0;
85     DfPos[i] = DsbPos[i] = DspPos[i] = 0.0;
86   }
87     
88   for (i=0;i<NNorm;i++) { ToNormalize[i]=-1;}
89   Debug(0);
90 }
91
92 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93
94 FGFCS::~FGFCS()
95 {
96   ThrottleCmd.clear();
97   ThrottlePos.clear();
98   MixtureCmd.clear();
99   MixturePos.clear();
100   PropAdvanceCmd.clear();
101   PropAdvance.clear();
102
103   unsigned int i;
104   
105   unbind();
106
107   for (i=0;i<Components.size();i++) delete Components[i];
108   Debug(1);
109 }
110
111 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
112
113 bool FGFCS::Run(void)
114 {
115   unsigned int i;
116
117   if (!FGModel::Run()) {
118     for (i=0; i<ThrottlePos.size(); i++) ThrottlePos[i] = ThrottleCmd[i];
119     for (i=0; i<MixturePos.size(); i++) MixturePos[i] = MixtureCmd[i];
120     for (i=0; i<PropAdvance.size(); i++) PropAdvance[i] = PropAdvanceCmd[i];
121     for (i=0; i<Components.size(); i++)  Components[i]->Run();
122     if (DoNormalize) Normalize();
123
124         return false;
125   } else {
126         return true;
127   }
128 }
129
130 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
131
132 void FGFCS::SetThrottleCmd(int engineNum, double setting)
133 {
134   unsigned int ctr;
135
136   if (engineNum < (int)ThrottlePos.size()) {
137     if (engineNum < 0) {
138       for (ctr=0;ctr<ThrottleCmd.size();ctr++) ThrottleCmd[ctr] = setting;
139     } else {
140       ThrottleCmd[engineNum] = setting;
141     }
142   } else {
143     cerr << "Throttle " << engineNum << " does not exist! " << ThrottleCmd.size()
144          << " engines exist, but attempted throttle command is for engine "
145          << engineNum << endl;
146   }
147 }
148
149 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
150
151 void FGFCS::SetThrottlePos(int engineNum, double setting)
152 {
153   unsigned int ctr;
154
155   if (engineNum < (int)ThrottlePos.size()) {
156     if (engineNum < 0) {
157       for (ctr=0;ctr<ThrottlePos.size();ctr++) ThrottlePos[ctr] = setting;
158     } else {
159       ThrottlePos[engineNum] = setting;
160     }
161   } else {
162     cerr << "Throttle " << engineNum << " does not exist! " << ThrottlePos.size()
163          << " engines exist, but attempted throttle position setting is for engine "
164          << engineNum << endl;
165   }
166 }
167
168 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
169
170 double FGFCS::GetThrottleCmd(int engineNum) const
171 {
172   if (engineNum < (int)ThrottlePos.size()) {
173     if (engineNum < 0) {
174        cerr << "Cannot get throttle value for ALL engines" << endl;
175     } else {
176       return ThrottleCmd[engineNum];
177     }
178   } else {
179     cerr << "Throttle " << engineNum << " does not exist! " << ThrottleCmd.size()
180          << " engines exist, but throttle setting for engine " << engineNum
181          << " is selected" << endl;
182   }
183   return 0.0;
184 }
185
186 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
187
188 double FGFCS::GetThrottlePos(int engineNum) const
189 {
190   if (engineNum < (int)ThrottlePos.size()) {
191     if (engineNum < 0) {
192        cerr << "Cannot get throttle value for ALL engines" << endl;
193     } else {
194       return ThrottlePos[engineNum];
195     }
196   } else {
197     cerr << "Throttle " << engineNum << " does not exist! " << ThrottlePos.size()
198          << " engines exist, but attempted throttle position setting is for engine "
199          << engineNum << endl;
200   }
201   return 0.0; 
202 }
203
204 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
205
206 void FGFCS::SetMixtureCmd(int engineNum, double setting)
207 {
208   unsigned int ctr;
209
210   if (engineNum < (int)ThrottlePos.size()) {
211     if (engineNum < 0) {
212       for (ctr=0;ctr<MixtureCmd.size();ctr++) MixtureCmd[ctr] = setting;
213     } else {
214       MixtureCmd[engineNum] = setting;
215     }
216   }
217 }
218
219 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
220
221 void FGFCS::SetMixturePos(int engineNum, double setting)
222 {
223   unsigned int ctr;
224
225   if (engineNum < (int)ThrottlePos.size()) {
226     if (engineNum < 0) {
227       for (ctr=0;ctr<=MixtureCmd.size();ctr++) MixturePos[ctr] = MixtureCmd[ctr];
228     } else {
229       MixturePos[engineNum] = setting;
230     }
231   }
232 }
233
234 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
235
236 void FGFCS::SetPropAdvanceCmd(int engineNum, double setting)
237 {
238   unsigned int ctr;
239
240   if (engineNum < (int)ThrottlePos.size()) {
241     if (engineNum < 0) {
242       for (ctr=0;ctr<PropAdvanceCmd.size();ctr++) PropAdvanceCmd[ctr] = setting;
243     } else {
244       PropAdvanceCmd[engineNum] = setting;
245     }
246   }
247 }
248
249 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
250
251 void FGFCS::SetPropAdvance(int engineNum, double setting)
252 {
253   unsigned int ctr;
254
255   if (engineNum < (int)ThrottlePos.size()) {
256     if (engineNum < 0) {
257       for (ctr=0;ctr<=PropAdvanceCmd.size();ctr++) PropAdvance[ctr] = PropAdvanceCmd[ctr];
258     } else {
259       PropAdvance[engineNum] = setting;
260     }
261   }
262 }
263
264 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
265
266 bool FGFCS::Load(FGConfigFile* AC_cfg)
267 {
268   string token;
269   unsigned i;
270   
271   Name = Name + ":" + AC_cfg->GetValue("NAME");
272   if (debug_lvl > 0) cout << "    Control System Name: " << Name << endl;
273   if ( AC_cfg->GetValue("NORMALIZE") == "FALSE") {
274       DoNormalize=false;
275       cout << "    Automatic Control Surface Normalization Disabled" << endl;
276   }    
277   AC_cfg->GetNextConfigLine();
278   while ((token = AC_cfg->GetValue()) != string("/FLIGHT_CONTROL")) {
279     if (token == "COMPONENT") {
280       token = AC_cfg->GetValue("TYPE");
281       if (debug_lvl > 0) cout << endl << "    Loading Component \""
282                               << AC_cfg->GetValue("NAME")
283                               << "\" of type: " << token << endl;
284       if ((token == "LAG_FILTER") ||
285           (token == "LEAD_LAG_FILTER") ||
286           (token == "SECOND_ORDER_FILTER") ||
287           (token == "WASHOUT_FILTER") ||
288           (token == "INTEGRATOR") ) {
289         Components.push_back(new FGFilter(this, AC_cfg));
290       } else if ((token == "PURE_GAIN") ||
291                  (token == "SCHEDULED_GAIN") ||
292                  (token == "AEROSURFACE_SCALE") ) {
293
294         Components.push_back(new FGGain(this, AC_cfg));
295
296       } else if (token == "SUMMER") {
297         Components.push_back(new FGSummer(this, AC_cfg));
298       } else if (token == "DEADBAND") {
299         Components.push_back(new FGDeadBand(this, AC_cfg));
300       } else if (token == "GRADIENT") {
301         Components.push_back(new FGGradient(this, AC_cfg));
302       } else if (token == "SWITCH") {
303         Components.push_back(new FGSwitch(this, AC_cfg));
304       } else if (token == "KINEMAT") {
305         Components.push_back(new FGKinemat(this, AC_cfg));
306       } else {
307         cerr << "Unknown token [" << token << "] in FCS portion of config file" << endl;
308         return false;
309       }
310       AC_cfg->GetNextConfigLine();
311     }
312   }
313   //collect information for normalizing control surfaces
314   string nodeName;
315   for (i=0;i<Components.size();i++) {
316     
317     if ( (Components[i]->GetType() == "AEROSURFACE_SCALE" 
318           || Components[i]->GetType() == "KINEMAT")  
319                     && Components[i]->GetOutputNode() ) { 
320       nodeName= Components[i]->GetOutputNode()->GetName();  
321       if ( nodeName == "elevator-pos-rad" ) {
322         ToNormalize[iDe]=i;
323       } else if ( nodeName  == "left-aileron-pos-rad" 
324                    || nodeName == "aileron-pos-rad" ) {
325         ToNormalize[iDaL]=i;
326       } else if ( nodeName == "right-aileron-pos-rad" ) {
327         ToNormalize[iDaR]=i;
328       } else if ( nodeName == "rudder-pos-rad" ) {
329         ToNormalize[iDr]=i;
330       } else if ( nodeName == "speedbrake-pos-rad" ) {
331         ToNormalize[iDsb]=i;
332       } else if ( nodeName == "spoiler-pos-rad" ) {
333         ToNormalize[iDsp]=i;
334       } else if ( nodeName == "flap-pos-deg" ) {
335         ToNormalize[iDf]=i;
336       }
337     }
338   }     
339   
340   bindModel();
341   
342   return true;
343 }
344
345 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
346
347 double FGFCS::GetComponentOutput(int idx) {
348   return Components[idx]->GetOutput();
349 }
350
351 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
352
353 string FGFCS::GetComponentName(int idx) {
354   return Components[idx]->GetName();
355 }
356
357 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
358
359 double FGFCS::GetBrake(FGLGear::BrakeGroup bg)
360 {
361   switch (bg) {
362   case FGLGear::bgLeft:
363     return LeftBrake;
364   case FGLGear::bgRight:
365     return RightBrake;
366   case FGLGear::bgCenter:
367     return CenterBrake;
368   default:
369     cerr << "GetBrake asked to return a bogus brake value" << endl;
370   }
371   return 0.0;
372 }
373
374 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
375
376 string FGFCS::GetComponentStrings(void)
377 {
378   unsigned int comp;
379   string CompStrings = "";
380   bool firstime = true;
381
382   for (comp = 0; comp < Components.size(); comp++) {
383     if (firstime) firstime = false;
384     else          CompStrings += ", ";
385
386     CompStrings += Components[comp]->GetName();
387   }
388
389   return CompStrings;
390 }
391
392 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
393
394 string FGFCS::GetComponentValues(void)
395 {
396   unsigned int comp;
397   string CompValues = "";
398   char buffer[10];
399   bool firstime = true;
400
401   for (comp = 0; comp < Components.size(); comp++) {
402     if (firstime) firstime = false;
403     else          CompValues += ", ";
404
405     sprintf(buffer, "%9.6f", Components[comp]->GetOutput());
406     CompValues += string(buffer);
407   }
408
409   return CompValues;
410 }
411
412 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
413
414 void FGFCS::AddThrottle(void)
415 {
416   ThrottleCmd.push_back(0.0);
417   ThrottlePos.push_back(0.0);
418   MixtureCmd.push_back(0.0);     // assume throttle and mixture are coupled
419   MixturePos.push_back(0.0);
420   PropAdvanceCmd.push_back(0.0); // assume throttle and prop pitch are coupled
421   PropAdvance.push_back(0.0);
422 }
423
424 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
425
426 void FGFCS::Normalize(void) {
427   
428   //not all of these are guaranteed to be defined for every model
429   //those that are have an index >=0 in the ToNormalize array
430   //ToNormalize is filled in Load()
431   
432   if ( ToNormalize[iDe] > -1 ) {
433     DePos[ofNorm] = Components[ToNormalize[iDe]]->GetOutputPct();
434   }
435   
436   if ( ToNormalize[iDaL] > -1 ) {
437     DaLPos[ofNorm] = Components[ToNormalize[iDaL]]->GetOutputPct();
438   }
439   
440   if ( ToNormalize[iDaR] > -1 ) {
441     DaRPos[ofNorm] = Components[ToNormalize[iDaR]]->GetOutputPct();
442   }
443
444   if ( ToNormalize[iDr] > -1 ) {
445     DrPos[ofNorm] = Components[ToNormalize[iDr]]->GetOutputPct();
446   }
447        
448   if ( ToNormalize[iDsb] > -1 ) { 
449     DsbPos[ofNorm] = Components[ToNormalize[iDsb]]->GetOutputPct();
450   }
451   
452   if ( ToNormalize[iDsp] > -1 ) {
453     DspPos[ofNorm] = Components[ToNormalize[iDsp]]->GetOutputPct();
454   }
455   
456   if ( ToNormalize[iDf] > -1 ) {
457     DfPos[ofNorm] = Components[ToNormalize[iDf]]->GetOutputPct();
458   }
459   
460   DePos[ofMag]  = fabs(DePos[ofRad]);
461   DaLPos[ofMag] = fabs(DaLPos[ofRad]);
462   DaRPos[ofMag] = fabs(DaRPos[ofRad]);
463   DrPos[ofMag]  = fabs(DrPos[ofRad]);
464   DsbPos[ofMag] = fabs(DsbPos[ofRad]);
465   DspPos[ofMag] = fabs(DspPos[ofRad]);
466   DfPos[ofMag]  = fabs(DfPos[ofRad]);
467    
468 }  
469     
470 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
471
472 void FGFCS::bind(void)
473 {
474   PropertyManager->Tie("fcs/aileron-cmd-norm", this,
475                        &FGFCS::GetDaCmd,
476                        &FGFCS::SetDaCmd,
477                        true);
478   PropertyManager->Tie("fcs/elevator-cmd-norm", this,
479                        &FGFCS::GetDeCmd,
480                        &FGFCS::SetDeCmd,
481                        true);
482   PropertyManager->Tie("fcs/rudder-cmd-norm", this,
483                        &FGFCS::GetDrCmd,
484                        &FGFCS::SetDrCmd,
485                        true);
486   PropertyManager->Tie("fcs/flap-cmd-norm", this,
487                        &FGFCS::GetDfCmd,
488                        &FGFCS::SetDfCmd,
489                        true);
490   PropertyManager->Tie("fcs/speedbrake-cmd-norm", this,
491                        &FGFCS::GetDsbCmd,
492                        &FGFCS::SetDsbCmd,
493                        true);
494   PropertyManager->Tie("fcs/spoiler-cmd-norm", this,
495                        &FGFCS::GetDspCmd,
496                        &FGFCS::SetDspCmd,
497                        true);
498   PropertyManager->Tie("fcs/pitch-trim-cmd-norm", this,
499                        &FGFCS::GetPitchTrimCmd,
500                        &FGFCS::SetPitchTrimCmd,
501                        true);
502   PropertyManager->Tie("fcs/roll-trim-cmd-norm", this,
503                        &FGFCS::GetYawTrimCmd,
504                        &FGFCS::SetYawTrimCmd,
505                        true);
506   PropertyManager->Tie("fcs/yaw-trim-cmd-norm", this,
507                        &FGFCS::GetRollTrimCmd,
508                        &FGFCS::SetRollTrimCmd,
509                        true);
510   PropertyManager->Tie("gear/gear-cmd-norm", this,
511                        &FGFCS::GetGearCmd,
512                        &FGFCS::SetGearCmd,
513                        true);
514   
515   PropertyManager->Tie("fcs/left-aileron-pos-rad", this,ofRad,
516                        &FGFCS::GetDaLPos,
517                        &FGFCS::SetDaLPos,
518                        true);
519   PropertyManager->Tie("fcs/left-aileron-pos-norm", this,ofNorm,
520                        &FGFCS::GetDaLPos,
521                        &FGFCS::SetDaLPos,
522                        true);
523   PropertyManager->Tie("fcs/mag-left-aileron-pos-rad", this,ofMag,
524                        &FGFCS::GetDaLPos,
525                        &FGFCS::SetDaLPos,
526                        true);
527  
528   PropertyManager->Tie("fcs/right-aileron-pos-rad", this,ofRad,
529                        &FGFCS::GetDaRPos,
530                        &FGFCS::SetDaRPos,
531                        true);
532   PropertyManager->Tie("fcs/right-aileron-pos-norm", this,ofNorm,
533                        &FGFCS::GetDaRPos,
534                        &FGFCS::SetDaRPos,
535                        true);
536   PropertyManager->Tie("fcs/mag-right-aileron-pos-rad", this,ofMag,
537                        &FGFCS::GetDaRPos,
538                        &FGFCS::SetDaRPos,
539                        true);
540   
541   PropertyManager->Tie("fcs/elevator-pos-rad", this, ofRad,
542                        &FGFCS::GetDePos,
543                        &FGFCS::SetDePos,
544                        true );
545   PropertyManager->Tie("fcs/elevator-pos-norm", this,ofNorm,
546                        &FGFCS::GetDePos,                       
547                        &FGFCS::SetDePos,
548                        true );
549   PropertyManager->Tie("fcs/mag-elevator-pos-rad", this,ofMag,
550                        &FGFCS::GetDePos,
551                        &FGFCS::SetDePos,
552                        true );
553   
554   PropertyManager->Tie("fcs/rudder-pos-rad", this,ofRad,
555                        &FGFCS::GetDrPos,
556                        &FGFCS::SetDrPos,
557                        true);
558   PropertyManager->Tie("fcs/rudder-pos-norm", this,ofNorm,
559                        &FGFCS::GetDrPos,
560                        &FGFCS::SetDrPos,
561                        true);
562   PropertyManager->Tie("fcs/mag-rudder-pos-rad", this,ofMag,
563                        &FGFCS::GetDrPos,
564                        &FGFCS::SetDrPos,
565                        true);
566                        
567   PropertyManager->Tie("fcs/flap-pos-deg", this,ofRad,
568                        &FGFCS::GetDfPos,
569                        &FGFCS::SetDfPos,
570                        true);
571   PropertyManager->Tie("fcs/flap-pos-norm", this,ofNorm,
572                        &FGFCS::GetDfPos,
573                        &FGFCS::SetDfPos,
574                        true);
575   
576   PropertyManager->Tie("fcs/speedbrake-pos-rad", this,ofRad,
577                        &FGFCS::GetDsbPos,
578                        &FGFCS::SetDsbPos,
579                        true);
580   PropertyManager->Tie("fcs/speedbrake-pos-norm", this,ofNorm,
581                        &FGFCS::GetDsbPos,
582                        &FGFCS::SetDsbPos,
583                        true);
584   PropertyManager->Tie("fcs/mag-speedbrake-pos-rad", this,ofMag,
585                        &FGFCS::GetDsbPos,
586                        &FGFCS::SetDsbPos,
587                        true);
588                        
589   PropertyManager->Tie("fcs/spoiler-pos-rad", this,ofRad,
590                        &FGFCS::GetDspPos,
591                        &FGFCS::SetDspPos,
592                        true);
593   PropertyManager->Tie("fcs/spoiler-pos-norm", this,ofNorm,
594                        &FGFCS::GetDspPos,
595                        &FGFCS::SetDspPos,
596                        true);
597   PropertyManager->Tie("fcs/mag-spoiler-pos-rad", this,ofMag,
598                        &FGFCS::GetDspPos,
599                        &FGFCS::SetDspPos,
600                        true);
601                        
602   PropertyManager->Tie("gear/gear-pos-norm", this,
603                        &FGFCS::GetGearPos,
604                        &FGFCS::SetGearPos,
605                        true);
606 }
607
608 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
609
610 void FGFCS::bindModel(void)
611 {
612   unsigned i;
613   char tmp[80];
614   
615   for (i=0; i<ThrottleCmd.size(); i++) {
616     snprintf(tmp,80,"fcs/throttle-cmd-norm[%u]",i);
617     PropertyManager->Tie( tmp,this,i,
618                           &FGFCS::GetThrottleCmd,
619                           &FGFCS::SetThrottleCmd,
620                           true );
621     snprintf(tmp,80,"fcs/throttle-pos-norm[%u]",i);                      
622     PropertyManager->Tie( tmp,this,i,
623                           &FGFCS::GetThrottlePos,
624                           &FGFCS::SetThrottlePos,
625                           true );
626     if ( MixtureCmd.size() > i ) {
627       snprintf(tmp,80,"fcs/mixture-cmd-norm[%u]",i); 
628       PropertyManager->Tie( tmp,this,i,
629                             &FGFCS::GetMixtureCmd,
630                             &FGFCS::SetMixtureCmd,
631                             true );
632       snprintf(tmp,80,"fcs/mixture-pos-norm[%u]",i);                    
633       PropertyManager->Tie( tmp,this,i,
634                             &FGFCS::GetMixturePos,
635                             &FGFCS::SetMixturePos,
636                             true );
637     }
638     if ( PropAdvanceCmd.size() > i ) {
639       snprintf(tmp,80,"fcs/advance-cmd-norm[%u]",i); 
640       PropertyManager->Tie( tmp,this,i,
641                             &FGFCS::GetPropAdvanceCmd,
642                             &FGFCS::SetPropAdvanceCmd,
643                             true );
644       snprintf(tmp,80,"fcs/advance-pos-norm[%u]",i);                       
645       PropertyManager->Tie( tmp,this,i,
646                             &FGFCS::GetPropAdvance,
647                             &FGFCS::SetPropAdvance,
648                             true );
649     }
650   }
651 }                            
652                           
653 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
654
655 void FGFCS::unbind(void)
656 {
657   PropertyManager->Untie("fcs/aileron-cmd-norm");
658   PropertyManager->Untie("fcs/elevator-cmd-norm");
659   PropertyManager->Untie("fcs/rudder-cmd-norm");
660   PropertyManager->Untie("fcs/flap-cmd-norm");
661   PropertyManager->Untie("fcs/speedbrake-cmd-norm");
662   PropertyManager->Untie("fcs/spoiler-cmd-norm");
663   PropertyManager->Untie("fcs/pitch-trim-cmd-norm");
664   PropertyManager->Untie("fcs/roll-trim-cmd-norm");
665   PropertyManager->Untie("fcs/yaw-trim-cmd-norm");
666   PropertyManager->Untie("gear/gear-cmd-norm");
667   PropertyManager->Untie("fcs/left-aileron-pos-rad");
668   PropertyManager->Untie("fcs/mag-left-aileron-pos-rad");
669   PropertyManager->Untie("fcs/left-aileron-pos-norm");
670   PropertyManager->Untie("fcs/right-aileron-pos-rad");
671   PropertyManager->Untie("fcs/mag-right-aileron-pos-rad");
672   PropertyManager->Untie("fcs/right-aileron-pos-norm");
673   PropertyManager->Untie("fcs/elevator-pos-rad");
674   PropertyManager->Untie("fcs/mag-elevator-pos-rad");
675   PropertyManager->Untie("fcs/elevator-pos-norm");
676   PropertyManager->Untie("fcs/rudder-pos-rad");
677   PropertyManager->Untie("fcs/mag-rudder-pos-rad");
678   PropertyManager->Untie("fcs/rudder-pos-norm");
679   PropertyManager->Untie("fcs/flap-pos-deg");
680   PropertyManager->Untie("fcs/flap-pos-norm");
681   PropertyManager->Untie("fcs/speedbrake-pos-rad");
682   PropertyManager->Untie("fcs/mag-speedbrake-pos-rad");
683   PropertyManager->Untie("fcs/speedbrake-pos-norm");
684   PropertyManager->Untie("fcs/spoiler-pos-rad");
685   PropertyManager->Untie("fcs/mag-spoiler-pos-rad");
686   PropertyManager->Untie("fcs/spoiler-pos-norm");
687   PropertyManager->Untie("gear/gear-pos-norm");
688 }
689
690 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
691 //    The bitmasked value choices are as follows:
692 //    unset: In this case (the default) JSBSim would only print
693 //       out the normally expected messages, essentially echoing
694 //       the config files as they are read. If the environment
695 //       variable is not set, debug_lvl is set to 1 internally
696 //    0: This requests JSBSim not to output any messages
697 //       whatsoever.
698 //    1: This value explicity requests the normal JSBSim
699 //       startup messages
700 //    2: This value asks for a message to be printed out when
701 //       a class is instantiated
702 //    4: When this value is set, a message is displayed when a
703 //       FGModel object executes its Run() method
704 //    8: When this value is set, various runtime state variables
705 //       are printed out periodically
706 //    16: When set various parameters are sanity checked and
707 //       a message is printed out when they go out of bounds
708
709 void FGFCS::Debug(int from)
710 {
711   if (debug_lvl <= 0) return;
712
713   if (debug_lvl & 1) { // Standard console startup message output
714     if (from == 0) { // Constructor
715
716     }
717   }
718   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
719     if (from == 0) cout << "Instantiated: FGFCS" << endl;
720     if (from == 1) cout << "Destroyed:    FGFCS" << endl;
721   }
722   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
723   }
724   if (debug_lvl & 8 ) { // Runtime state variables
725   }
726   if (debug_lvl & 16) { // Sanity checking
727   }
728   if (debug_lvl & 64) {
729     if (from == 0) { // Constructor
730       cout << IdSrc << endl;
731       cout << IdHdr << endl;
732     }
733   }
734 }
735