1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 Purpose: Model the flight controls
9 ------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) -------------
11 This program is free software; you can redistribute it and/or modify it under
12 the terms of the GNU Lesser General Public License as published by the Free Software
13 Foundation; either version 2 of the License, or (at your option) any later
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
21 You should have received a copy of the GNU Lesser General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
23 Place - Suite 330, Boston, MA 02111-1307, USA.
25 Further information about the GNU Lesser General Public License can also be found on
26 the world wide web at http://www.gnu.org.
28 FUNCTIONAL DESCRIPTION
29 --------------------------------------------------------------------------------
30 This class models the flight controls for a specific airplane
33 --------------------------------------------------------------------------------
36 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
38 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
41 #include <FGFDMExec.h>
42 #include <input_output/FGPropertyManager.h>
45 #include <models/flight_control/FGFilter.h>
46 #include <models/flight_control/FGDeadBand.h>
47 #include <models/flight_control/FGGain.h>
48 #include <models/flight_control/FGPID.h>
49 #include <models/flight_control/FGGradient.h>
50 #include <models/flight_control/FGSwitch.h>
51 #include <models/flight_control/FGSummer.h>
52 #include <models/flight_control/FGKinemat.h>
53 #include <models/flight_control/FGFCSFunction.h>
54 #include <models/flight_control/FGSensor.h>
55 #include <models/flight_control/FGActuator.h>
59 static const char *IdSrc = "$Id$";
60 static const char *IdHdr = ID_FCS;
62 #if defined(WIN32) && !defined(__CYGWIN__)
63 #define snprintf _snprintf
66 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
68 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
70 FGFCS::FGFCS(FGFDMExec* fdmex) : FGModel(fdmex)
75 DaCmd = DeCmd = DrCmd = DsCmd = DfCmd = DsbCmd = DspCmd = 0;
76 PTrimCmd = YTrimCmd = RTrimCmd = 0.0;
77 GearCmd = GearPos = 1; // default to gear down
78 LeftBrake = RightBrake = CenterBrake = 0.0;
81 for (i=0;i<NForms;i++) {
82 DePos[i] = DaLPos[i] = DaRPos[i] = DrPos[i] = 0.0;
83 DfPos[i] = DsbPos[i] = DspPos[i] = 0.0;
89 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97 PropAdvanceCmd.clear();
100 PropFeatherCmd.clear();
105 for (i=0;i<sensors.size();i++) delete sensors[i];
107 for (i=0;i<APComponents.size();i++) delete APComponents[i];
108 APComponents.clear();
109 for (i=0;i<FCSComponents.size();i++) delete FCSComponents[i];
110 FCSComponents.clear();
111 for (i=0;i<Systems.size();i++) delete Systems[i];
114 for (unsigned int i=0; i<interface_properties.size(); i++) delete interface_properties[i];
115 interface_properties.clear();
120 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
122 bool FGFCS::InitModel(void)
126 if (!FGModel::InitModel()) return false;
128 for (i=0; i<ThrottlePos.size(); i++) ThrottlePos[i] = 0.0;
129 for (i=0; i<MixturePos.size(); i++) MixturePos[i] = 0.0;
130 for (i=0; i<ThrottleCmd.size(); i++) ThrottleCmd[i] = 0.0;
131 for (i=0; i<MixtureCmd.size(); i++) MixtureCmd[i] = 0.0;
132 for (i=0; i<PropAdvance.size(); i++) PropAdvance[i] = 0.0;
133 for (i=0; i<PropFeather.size(); i++) PropFeather[i] = 0.0;
135 DaCmd = DeCmd = DrCmd = DsCmd = DfCmd = DsbCmd = DspCmd = 0;
136 PTrimCmd = YTrimCmd = RTrimCmd = 0.0;
138 for (i=0;i<NForms;i++) {
139 DePos[i] = DaLPos[i] = DaRPos[i] = DrPos[i] = 0.0;
140 DfPos[i] = DsbPos[i] = DspPos[i] = 0.0;
146 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
147 // Notes: In this logic the default engine commands are set. This is simply a
148 // sort of safe-mode method in case the user has not defined control laws for
149 // throttle, mixture, and prop-advance. The throttle, mixture, and prop advance
150 // positions are set equal to the respective commands. Any control logic that is
151 // actually present in the flight_control or autopilot section will override
152 // these simple assignments.
154 bool FGFCS::Run(void)
158 if (FGModel::Run()) return true; // fast exit if nothing to do
159 if (FDMExec->Holding()) return false;
161 for (i=0; i<ThrottlePos.size(); i++) ThrottlePos[i] = ThrottleCmd[i];
162 for (i=0; i<MixturePos.size(); i++) MixturePos[i] = MixtureCmd[i];
163 for (i=0; i<PropAdvance.size(); i++) PropAdvance[i] = PropAdvanceCmd[i];
164 for (i=0; i<PropFeather.size(); i++) PropFeather[i] = PropFeatherCmd[i];
166 // Set the default steering angle
167 for (i=0; i<SteerPosDeg.size(); i++) {
168 FGLGear* gear = GroundReactions->GetGearUnit(i);
169 SteerPosDeg[i] = gear->GetDefaultSteerAngle( GetDsCmd() );
172 // Cycle through the sensor, systems, autopilot, and flight control components
174 for (i=0; i<sensors.size(); i++) sensors[i]->Run();
176 // Execute Systems in order
177 for (i=0; i<Systems.size(); i++) Systems[i]->Run();
180 for (i=0; i<APComponents.size(); i++) APComponents[i]->Run();
182 // Execute Flight Control System
183 for (i=0; i<FCSComponents.size(); i++) FCSComponents[i]->Run();
188 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
190 void FGFCS::SetDaLPos( int form , double pos )
195 DaLPos[ofDeg] = pos*radtodeg;
198 DaLPos[ofRad] = pos*degtorad;
202 DaLPos[ofNorm] = pos;
204 DaLPos[ofMag] = fabs(DaLPos[ofRad]);
207 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
209 void FGFCS::SetDaRPos( int form , double pos )
214 DaRPos[ofDeg] = pos*radtodeg;
217 DaRPos[ofRad] = pos*degtorad;
221 DaRPos[ofNorm] = pos;
223 DaRPos[ofMag] = fabs(DaRPos[ofRad]);
226 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
228 void FGFCS::SetDePos( int form , double pos )
233 DePos[ofDeg] = pos*radtodeg;
236 DePos[ofRad] = pos*degtorad;
242 DePos[ofMag] = fabs(DePos[ofRad]);
245 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
247 void FGFCS::SetDrPos( int form , double pos )
252 DrPos[ofDeg] = pos*radtodeg;
255 DrPos[ofRad] = pos*degtorad;
261 DrPos[ofMag] = fabs(DrPos[ofRad]);
264 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
266 void FGFCS::SetDfPos( int form , double pos )
271 DfPos[ofDeg] = pos*radtodeg;
274 DfPos[ofRad] = pos*degtorad;
280 DfPos[ofMag] = fabs(DfPos[ofRad]);
283 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
285 void FGFCS::SetDsbPos( int form , double pos )
290 DsbPos[ofDeg] = pos*radtodeg;
293 DsbPos[ofRad] = pos*degtorad;
297 DsbPos[ofNorm] = pos;
299 DsbPos[ofMag] = fabs(DsbPos[ofRad]);
302 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
304 void FGFCS::SetDspPos( int form , double pos )
309 DspPos[ofDeg] = pos*radtodeg;
312 DspPos[ofRad] = pos*degtorad;
316 DspPos[ofNorm] = pos;
318 DspPos[ofMag] = fabs(DspPos[ofRad]);
321 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
323 void FGFCS::SetThrottleCmd(int engineNum, double setting)
327 if (engineNum < (int)ThrottlePos.size()) {
329 for (ctr=0;ctr<ThrottleCmd.size();ctr++) ThrottleCmd[ctr] = setting;
331 ThrottleCmd[engineNum] = setting;
334 cerr << "Throttle " << engineNum << " does not exist! " << ThrottleCmd.size()
335 << " engines exist, but attempted throttle command is for engine "
336 << engineNum << endl;
340 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
342 void FGFCS::SetThrottlePos(int engineNum, double setting)
346 if (engineNum < (int)ThrottlePos.size()) {
348 for (ctr=0;ctr<ThrottlePos.size();ctr++) ThrottlePos[ctr] = setting;
350 ThrottlePos[engineNum] = setting;
353 cerr << "Throttle " << engineNum << " does not exist! " << ThrottlePos.size()
354 << " engines exist, but attempted throttle position setting is for engine "
355 << engineNum << endl;
359 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
361 double FGFCS::GetThrottleCmd(int engineNum) const
363 if (engineNum < (int)ThrottlePos.size()) {
365 cerr << "Cannot get throttle value for ALL engines" << endl;
367 return ThrottleCmd[engineNum];
370 cerr << "Throttle " << engineNum << " does not exist! " << ThrottleCmd.size()
371 << " engines exist, but throttle setting for engine " << engineNum
372 << " is selected" << endl;
377 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
379 double FGFCS::GetThrottlePos(int engineNum) const
381 if (engineNum < (int)ThrottlePos.size()) {
383 cerr << "Cannot get throttle value for ALL engines" << endl;
385 return ThrottlePos[engineNum];
388 cerr << "Throttle " << engineNum << " does not exist! " << ThrottlePos.size()
389 << " engines exist, but attempted throttle position setting is for engine "
390 << engineNum << endl;
395 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
397 void FGFCS::SetMixtureCmd(int engineNum, double setting)
401 if (engineNum < (int)ThrottlePos.size()) {
403 for (ctr=0;ctr<MixtureCmd.size();ctr++) MixtureCmd[ctr] = setting;
405 MixtureCmd[engineNum] = setting;
410 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
412 void FGFCS::SetMixturePos(int engineNum, double setting)
416 if (engineNum < (int)ThrottlePos.size()) {
418 for (ctr=0;ctr<=MixtureCmd.size();ctr++) MixturePos[ctr] = MixtureCmd[ctr];
420 MixturePos[engineNum] = setting;
425 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
427 void FGFCS::SetPropAdvanceCmd(int engineNum, double setting)
431 if (engineNum < (int)ThrottlePos.size()) {
433 for (ctr=0;ctr<PropAdvanceCmd.size();ctr++) PropAdvanceCmd[ctr] = setting;
435 PropAdvanceCmd[engineNum] = setting;
440 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
442 void FGFCS::SetPropAdvance(int engineNum, double setting)
446 if (engineNum < (int)ThrottlePos.size()) {
448 for (ctr=0;ctr<=PropAdvanceCmd.size();ctr++) PropAdvance[ctr] = PropAdvanceCmd[ctr];
450 PropAdvance[engineNum] = setting;
455 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
457 void FGFCS::SetFeatherCmd(int engineNum, bool setting)
461 if (engineNum < (int)ThrottlePos.size()) {
463 for (ctr=0;ctr<PropFeatherCmd.size();ctr++) PropFeatherCmd[ctr] = setting;
465 PropFeatherCmd[engineNum] = setting;
470 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
472 void FGFCS::SetPropFeather(int engineNum, bool setting)
476 if (engineNum < (int)ThrottlePos.size()) {
478 for (ctr=0;ctr<=PropFeatherCmd.size();ctr++) PropFeather[ctr] = PropFeatherCmd[ctr];
480 PropFeather[engineNum] = setting;
485 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
487 bool FGFCS::Load(Element* el, SystemType systype)
489 string name, file, fname, interface_property_string, parent_name;
490 vector <FGFCSComponent*> *Components;
491 Element *component_element, *property_element, *sensor_element;
492 Element *channel_element;
496 string separator = "/";
498 // ToDo: The handling of name and file attributes could be improved, here,
499 // considering that a name can be in the external file, as well.
501 name = el->GetAttributeValue("name");
504 fname = el->GetAttributeValue("file");
505 if (systype == stSystem) {
506 file = FindSystemFullPathname(fname);
508 file = FDMExec->GetFullAircraftPath() + separator + fname + ".xml";
511 cerr << "FCS, Autopilot, or system does not appear to be defined inline nor in a file" << endl;
514 document = LoadXMLDocument(file);
515 name = document->GetAttributeValue("name");
521 if (document->GetName() == "autopilot") {
522 Components = &APComponents;
523 Name = "Autopilot: " + document->GetAttributeValue("name");
524 } else if (document->GetName() == "flight_control") {
525 Components = &FCSComponents;
526 Name = "FCS: " + document->GetAttributeValue("name");
527 } else if (document->GetName() == "system") {
528 Components = &Systems;
529 Name = "System: " + document->GetAttributeValue("name");
533 // ToDo: How do these get untied?
534 // ToDo: Consider having INPUT and OUTPUT interface properties. Would then
535 // have to duplicate this block of code after channel read code.
536 // Input properties could be write only (nah), and output could be read
539 if (document->GetName() == "flight_control") bindModel();
541 // Interface properties from any autopilot, flight control, or other system are
542 // all stored in the interface properties array.
544 property_element = document->FindElement("property");
545 while (property_element) {
547 if ( ! property_element->GetAttributeValue("value").empty())
548 value = property_element->GetAttributeValueAsNumber("value");
549 interface_properties.push_back(new double(value));
550 interface_property_string = property_element->GetDataLine();
551 PropertyManager->Tie(interface_property_string, interface_properties.back());
552 property_element = document->FindNextElement("property");
555 // Any sensor elements that are outside of a channel (in either the autopilot
556 // or the flight_control, or even any possible "system") are placed into the global
557 // "sensors" array, and are executed prior to any autopilot, flight control, or
560 sensor_element = document->FindElement("sensor");
561 while (sensor_element) {
563 sensors.push_back(new FGSensor(this, sensor_element));
565 cerr << highint << fgred << endl << " " << s << endl;
568 sensor_element = document->FindNextElement("sensor");
571 channel_element = document->FindElement("channel");
572 while (channel_element) {
573 component_element = channel_element->GetElement();
574 while (component_element) {
576 if ((component_element->GetName() == string("lag_filter")) ||
577 (component_element->GetName() == string("lead_lag_filter")) ||
578 (component_element->GetName() == string("washout_filter")) ||
579 (component_element->GetName() == string("second_order_filter")) ||
580 (component_element->GetName() == string("integrator")) )
582 Components->push_back(new FGFilter(this, component_element));
583 } else if ((component_element->GetName() == string("pure_gain")) ||
584 (component_element->GetName() == string("scheduled_gain")) ||
585 (component_element->GetName() == string("aerosurface_scale")))
587 Components->push_back(new FGGain(this, component_element));
588 } else if (component_element->GetName() == string("summer")) {
589 Components->push_back(new FGSummer(this, component_element));
590 } else if (component_element->GetName() == string("deadband")) {
591 Components->push_back(new FGDeadBand(this, component_element));
592 } else if (component_element->GetName() == string("switch")) {
593 Components->push_back(new FGSwitch(this, component_element));
594 } else if (component_element->GetName() == string("kinematic")) {
595 Components->push_back(new FGKinemat(this, component_element));
596 } else if (component_element->GetName() == string("fcs_function")) {
597 Components->push_back(new FGFCSFunction(this, component_element));
598 } else if (component_element->GetName() == string("pid")) {
599 Components->push_back(new FGPID(this, component_element));
600 } else if (component_element->GetName() == string("actuator")) {
601 Components->push_back(new FGActuator(this, component_element));
602 } else if (component_element->GetName() == string("sensor")) {
603 Components->push_back(new FGSensor(this, component_element));
605 cerr << "Unknown FCS component: " << component_element->GetName() << endl;
608 cerr << highint << fgred << endl << " " << s << endl;
609 cerr << reset << endl;
612 component_element = channel_element->GetNextElement();
614 channel_element = document->FindNextElement("channel");
622 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
624 double FGFCS::GetBrake(FGLGear::BrakeGroup bg)
627 case FGLGear::bgLeft:
629 case FGLGear::bgRight:
631 case FGLGear::bgCenter:
634 cerr << "GetBrake asked to return a bogus brake value" << endl;
639 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
641 string FGFCS::FindSystemFullPathname(string system_filename)
643 string fullpath, localpath;
644 string systemPath = FDMExec->GetSystemsPath();
645 string aircraftPath = FDMExec->GetFullAircraftPath();
646 ifstream system_file;
648 string separator = "/";
650 fullpath = systemPath + separator;
651 localpath = aircraftPath + separator + "Systems" + separator;
653 system_file.open(string(fullpath + system_filename + ".xml").c_str());
654 if ( !system_file.is_open()) {
655 system_file.open(string(localpath + system_filename + ".xml").c_str());
656 if ( !system_file.is_open()) {
657 cerr << " Could not open system file: " << system_filename << " in path "
658 << fullpath << " or " << localpath << endl;
661 return string(localpath + system_filename + ".xml");
664 return string(fullpath + system_filename + ".xml");
667 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
669 ifstream* FGFCS::FindSystemFile(string system_filename)
671 string fullpath, localpath;
672 string systemPath = FDMExec->GetSystemsPath();
673 string aircraftPath = FDMExec->GetFullAircraftPath();
674 ifstream* system_file = new ifstream();
676 string separator = "/";
678 fullpath = systemPath + separator;
679 localpath = aircraftPath + separator + "Systems" + separator;
681 system_file->open(string(fullpath + system_filename + ".xml").c_str());
682 if ( !system_file->is_open()) {
683 system_file->open(string(localpath + system_filename + ".xml").c_str());
684 if ( !system_file->is_open()) {
685 cerr << " Could not open system file: " << system_filename << " in path "
686 << fullpath << " or " << localpath << endl;
692 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
694 string FGFCS::GetComponentStrings(string delimeter)
697 string CompStrings = "";
698 bool firstime = true;
701 for (unsigned int i=0; i<Systems.size(); i++) {
702 if (firstime) firstime = false;
703 else CompStrings += delimeter;
705 CompStrings += Systems[i]->GetName();
709 for (comp = 0; comp < APComponents.size(); comp++)
711 if (firstime) firstime = false;
712 else CompStrings += delimeter;
714 CompStrings += APComponents[comp]->GetName();
718 for (comp = 0; comp < FCSComponents.size(); comp++) {
719 if (firstime) firstime = false;
720 else CompStrings += delimeter;
722 CompStrings += FCSComponents[comp]->GetName();
729 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
731 string FGFCS::GetComponentValues(string delimeter)
734 string CompValues = "";
736 bool firstime = true;
739 for (unsigned int i=0; i<Systems.size(); i++) {
740 if (firstime) firstime = false;
741 else CompValues += delimeter;
743 sprintf(buffer, "%9.6f", Systems[i]->GetOutput());
744 CompValues += string(buffer);
748 for (comp = 0; comp < APComponents.size(); comp++) {
749 if (firstime) firstime = false;
750 else CompValues += delimeter;
752 sprintf(buffer, "%9.6f", APComponents[comp]->GetOutput());
753 CompValues += string(buffer);
757 for (comp = 0; comp < FCSComponents.size(); comp++) {
758 if (firstime) firstime = false;
759 else CompValues += delimeter;
761 sprintf(buffer, "%9.6f", FCSComponents[comp]->GetOutput());
762 CompValues += string(buffer);
770 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
772 void FGFCS::AddThrottle(void)
774 ThrottleCmd.push_back(0.0);
775 ThrottlePos.push_back(0.0);
776 MixtureCmd.push_back(0.0); // assume throttle and mixture are coupled
777 MixturePos.push_back(0.0);
778 PropAdvanceCmd.push_back(0.0); // assume throttle and prop pitch are coupled
779 PropAdvance.push_back(0.0);
780 PropFeatherCmd.push_back(false);
781 PropFeather.push_back(false);
783 unsigned int num = (unsigned int)ThrottleCmd.size()-1;
787 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
789 void FGFCS::AddGear(void)
791 SteerPosDeg.push_back(0.0);
794 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
796 double FGFCS::GetDt(void)
798 return FDMExec->GetDeltaT()*rate;
801 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
803 void FGFCS::bind(void)
805 PropertyManager->Tie("fcs/aileron-cmd-norm", this, &FGFCS::GetDaCmd, &FGFCS::SetDaCmd);
806 PropertyManager->Tie("fcs/elevator-cmd-norm", this, &FGFCS::GetDeCmd, &FGFCS::SetDeCmd);
807 PropertyManager->Tie("fcs/rudder-cmd-norm", this, &FGFCS::GetDrCmd, &FGFCS::SetDrCmd);
808 PropertyManager->Tie("fcs/flap-cmd-norm", this, &FGFCS::GetDfCmd, &FGFCS::SetDfCmd);
809 PropertyManager->Tie("fcs/speedbrake-cmd-norm", this, &FGFCS::GetDsbCmd, &FGFCS::SetDsbCmd);
810 PropertyManager->Tie("fcs/spoiler-cmd-norm", this, &FGFCS::GetDspCmd, &FGFCS::SetDspCmd);
811 PropertyManager->Tie("fcs/pitch-trim-cmd-norm", this, &FGFCS::GetPitchTrimCmd, &FGFCS::SetPitchTrimCmd);
812 PropertyManager->Tie("fcs/roll-trim-cmd-norm", this, &FGFCS::GetRollTrimCmd, &FGFCS::SetRollTrimCmd);
813 PropertyManager->Tie("fcs/yaw-trim-cmd-norm", this, &FGFCS::GetYawTrimCmd, &FGFCS::SetYawTrimCmd);
815 PropertyManager->Tie("fcs/left-aileron-pos-rad", this, ofRad, &FGFCS::GetDaLPos, &FGFCS::SetDaLPos);
816 PropertyManager->Tie("fcs/left-aileron-pos-deg", this, ofDeg, &FGFCS::GetDaLPos, &FGFCS::SetDaLPos);
817 PropertyManager->Tie("fcs/left-aileron-pos-norm", this, ofNorm, &FGFCS::GetDaLPos, &FGFCS::SetDaLPos);
818 PropertyManager->Tie("fcs/mag-left-aileron-pos-rad", this, ofMag, &FGFCS::GetDaLPos);
820 PropertyManager->Tie("fcs/right-aileron-pos-rad", this, ofRad, &FGFCS::GetDaRPos, &FGFCS::SetDaRPos);
821 PropertyManager->Tie("fcs/right-aileron-pos-deg", this, ofDeg, &FGFCS::GetDaRPos, &FGFCS::SetDaRPos);
822 PropertyManager->Tie("fcs/right-aileron-pos-norm", this, ofNorm, &FGFCS::GetDaRPos, &FGFCS::SetDaRPos);
823 PropertyManager->Tie("fcs/mag-right-aileron-pos-rad", this, ofMag, &FGFCS::GetDaRPos);
825 PropertyManager->Tie("fcs/elevator-pos-rad", this, ofRad, &FGFCS::GetDePos, &FGFCS::SetDePos);
826 PropertyManager->Tie("fcs/elevator-pos-deg", this, ofDeg, &FGFCS::GetDePos, &FGFCS::SetDePos);
827 PropertyManager->Tie("fcs/elevator-pos-norm", this, ofNorm, &FGFCS::GetDePos, &FGFCS::SetDePos);
828 PropertyManager->Tie("fcs/mag-elevator-pos-rad", this, ofMag, &FGFCS::GetDePos);
830 PropertyManager->Tie("fcs/rudder-pos-rad", this,ofRad, &FGFCS::GetDrPos, &FGFCS::SetDrPos);
831 PropertyManager->Tie("fcs/rudder-pos-deg", this,ofDeg, &FGFCS::GetDrPos, &FGFCS::SetDrPos);
832 PropertyManager->Tie("fcs/rudder-pos-norm", this,ofNorm, &FGFCS::GetDrPos, &FGFCS::SetDrPos);
833 PropertyManager->Tie("fcs/mag-rudder-pos-rad", this,ofMag, &FGFCS::GetDrPos);
835 PropertyManager->Tie("fcs/flap-pos-rad", this,ofRad, &FGFCS::GetDfPos, &FGFCS::SetDfPos);
836 PropertyManager->Tie("fcs/flap-pos-deg", this,ofDeg, &FGFCS::GetDfPos, &FGFCS::SetDfPos);
837 PropertyManager->Tie("fcs/flap-pos-norm", this,ofNorm, &FGFCS::GetDfPos, &FGFCS::SetDfPos);
839 PropertyManager->Tie("fcs/speedbrake-pos-rad", this,ofRad, &FGFCS::GetDsbPos, &FGFCS::SetDsbPos);
840 PropertyManager->Tie("fcs/speedbrake-pos-deg", this,ofDeg, &FGFCS::GetDsbPos, &FGFCS::SetDsbPos);
841 PropertyManager->Tie("fcs/speedbrake-pos-norm", this,ofNorm, &FGFCS::GetDsbPos, &FGFCS::SetDsbPos);
842 PropertyManager->Tie("fcs/mag-speedbrake-pos-rad", this,ofMag, &FGFCS::GetDsbPos);
844 PropertyManager->Tie("fcs/spoiler-pos-rad", this, ofRad, &FGFCS::GetDspPos, &FGFCS::SetDspPos);
845 PropertyManager->Tie("fcs/spoiler-pos-deg", this, ofDeg, &FGFCS::GetDspPos, &FGFCS::SetDspPos);
846 PropertyManager->Tie("fcs/spoiler-pos-norm", this, ofNorm, &FGFCS::GetDspPos, &FGFCS::SetDspPos);
847 PropertyManager->Tie("fcs/mag-spoiler-pos-rad", this, ofMag, &FGFCS::GetDspPos);
849 PropertyManager->Tie("gear/gear-pos-norm", this, &FGFCS::GetGearPos, &FGFCS::SetGearPos);
850 PropertyManager->Tie("gear/gear-cmd-norm", this, &FGFCS::GetGearCmd, &FGFCS::SetGearCmd);
851 PropertyManager->Tie("fcs/left-brake-cmd-norm", this, &FGFCS::GetLBrake, &FGFCS::SetLBrake);
852 PropertyManager->Tie("fcs/right-brake-cmd-norm", this, &FGFCS::GetRBrake, &FGFCS::SetRBrake);
853 PropertyManager->Tie("fcs/center-brake-cmd-norm", this, &FGFCS::GetCBrake, &FGFCS::SetCBrake);
854 PropertyManager->Tie("fcs/steer-cmd-norm", this, &FGFCS::GetDsCmd, &FGFCS::SetDsCmd);
857 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
858 // Technically, this function should probably bind propulsion type specific controls
859 // rather than mixture and prop-advance.
862 void FGFCS::bindThrottle(unsigned int num)
866 snprintf(tmp, 80, "fcs/throttle-cmd-norm[%u]",num);
867 PropertyManager->Tie( tmp, this, num, &FGFCS::GetThrottleCmd,
868 &FGFCS::SetThrottleCmd);
869 snprintf(tmp, 80, "fcs/throttle-pos-norm[%u]",num);
870 PropertyManager->Tie( tmp, this, num, &FGFCS::GetThrottlePos,
871 &FGFCS::SetThrottlePos);
872 snprintf(tmp, 80, "fcs/mixture-cmd-norm[%u]",num);
873 PropertyManager->Tie( tmp, this, num, &FGFCS::GetMixtureCmd,
874 &FGFCS::SetMixtureCmd);
875 snprintf(tmp, 80, "fcs/mixture-pos-norm[%u]",num);
876 PropertyManager->Tie( tmp, this, num, &FGFCS::GetMixturePos,
877 &FGFCS::SetMixturePos);
878 snprintf(tmp, 80, "fcs/advance-cmd-norm[%u]",num);
879 PropertyManager->Tie( tmp, this, num, &FGFCS::GetPropAdvanceCmd,
880 &FGFCS::SetPropAdvanceCmd);
881 snprintf(tmp, 80, "fcs/advance-pos-norm[%u]", num);
882 PropertyManager->Tie( tmp, this, num, &FGFCS::GetPropAdvance,
883 &FGFCS::SetPropAdvance);
884 snprintf(tmp, 80, "fcs/feather-cmd-norm[%u]", num);
885 PropertyManager->Tie( tmp, this, num, &FGFCS::GetFeatherCmd,
886 &FGFCS::SetFeatherCmd);
887 snprintf(tmp, 80, "fcs/feather-pos-norm[%u]", num);
888 PropertyManager->Tie( tmp, this, num, &FGFCS::GetPropFeather,
889 &FGFCS::SetPropFeather);
892 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
894 void FGFCS::bindModel(void)
899 for (i=0; i<SteerPosDeg.size(); i++) {
900 if (GroundReactions->GetGearUnit(i)->GetSteerable()) {
901 snprintf(tmp,80,"fcs/steer-pos-deg[%u]",i);
902 PropertyManager->Tie( tmp, this, i, &FGFCS::GetSteerPosDeg, &FGFCS::SetSteerPosDeg);
907 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
908 // The bitmasked value choices are as follows:
909 // unset: In this case (the default) JSBSim would only print
910 // out the normally expected messages, essentially echoing
911 // the config files as they are read. If the environment
912 // variable is not set, debug_lvl is set to 1 internally
913 // 0: This requests JSBSim not to output any messages
915 // 1: This value explicity requests the normal JSBSim
917 // 2: This value asks for a message to be printed out when
918 // a class is instantiated
919 // 4: When this value is set, a message is displayed when a
920 // FGModel object executes its Run() method
921 // 8: When this value is set, various runtime state variables
922 // are printed out periodically
923 // 16: When set various parameters are sanity checked and
924 // a message is printed out when they go out of bounds
926 void FGFCS::Debug(int from)
928 if (debug_lvl <= 0) return;
930 if (debug_lvl & 1) { // Standard console startup message output
931 if (from == 2) { // Loader
932 cout << endl << " Flight Control (" << Name << ")" << endl;
935 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
936 if (from == 0) cout << "Instantiated: FGFCS" << endl;
937 if (from == 1) cout << "Destroyed: FGFCS" << endl;
939 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
941 if (debug_lvl & 8 ) { // Runtime state variables
943 if (debug_lvl & 16) { // Sanity checking
945 if (debug_lvl & 64) {
946 if (from == 0) { // Constructor
947 cout << IdSrc << endl;
948 cout << IdHdr << endl;