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