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>
47 #include <models/flight_control/FGFilter.h>
48 #include <models/flight_control/FGDeadBand.h>
49 #include <models/flight_control/FGGain.h>
50 #include <models/flight_control/FGPID.h>
51 #include <models/flight_control/FGSwitch.h>
52 #include <models/flight_control/FGSummer.h>
53 #include <models/flight_control/FGKinemat.h>
54 #include <models/flight_control/FGFCSFunction.h>
55 #include <models/flight_control/FGSensor.h>
56 #include <models/flight_control/FGActuator.h>
57 #include <models/flight_control/FGAccelerometer.h>
61 static const char *IdSrc = "$Id$";
62 static const char *IdHdr = ID_FCS;
64 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
68 FGFCS::FGFCS(FGFDMExec* fdmex) : FGModel(fdmex)
73 DaCmd = DeCmd = DrCmd = DsCmd = DfCmd = DsbCmd = DspCmd = 0;
74 PTrimCmd = YTrimCmd = RTrimCmd = 0.0;
75 GearCmd = GearPos = 1; // default to gear down
76 LeftBrake = RightBrake = CenterBrake = 0.0;
77 TailhookPos = WingFoldPos = 0.0;
80 for (i=0;i<NForms;i++) {
81 DePos[i] = DaLPos[i] = DaRPos[i] = DrPos[i] = 0.0;
82 DfPos[i] = DsbPos[i] = DspPos[i] = 0.0;
88 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
96 PropAdvanceCmd.clear();
99 PropFeatherCmd.clear();
104 for (i=0;i<sensors.size();i++) delete sensors[i];
106 for (i=0;i<APComponents.size();i++) delete APComponents[i];
107 APComponents.clear();
108 for (i=0;i<FCSComponents.size();i++) delete FCSComponents[i];
109 FCSComponents.clear();
110 for (i=0;i<Systems.size();i++) delete Systems[i];
113 for (unsigned int i=0; i<interface_properties.size(); i++) delete interface_properties[i];
114 interface_properties.clear();
119 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
121 bool FGFCS::InitModel(void)
125 if (!FGModel::InitModel()) return false;
127 for (i=0; i<ThrottlePos.size(); i++) ThrottlePos[i] = 0.0;
128 for (i=0; i<MixturePos.size(); i++) MixturePos[i] = 0.0;
129 for (i=0; i<ThrottleCmd.size(); i++) ThrottleCmd[i] = 0.0;
130 for (i=0; i<MixtureCmd.size(); i++) MixtureCmd[i] = 0.0;
131 for (i=0; i<PropAdvance.size(); i++) PropAdvance[i] = 0.0;
132 for (i=0; i<PropFeather.size(); i++) PropFeather[i] = 0.0;
134 DaCmd = DeCmd = DrCmd = DsCmd = DfCmd = DsbCmd = DspCmd = 0;
135 PTrimCmd = YTrimCmd = RTrimCmd = 0.0;
136 TailhookPos = WingFoldPos = 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;
143 for (unsigned int i=0; i<Systems.size(); i++) {
144 if (Systems[i]->GetType() == "LAG" ||
145 Systems[i]->GetType() == "LEAD_LAG" ||
146 Systems[i]->GetType() == "WASHOUT" ||
147 Systems[i]->GetType() == "SECOND_ORDER_FILTER" ||
148 Systems[i]->GetType() == "INTEGRATOR")
150 ((FGFilter*)Systems[i])->ResetPastStates();
151 } else if (Systems[i]->GetType() == "PID" ) {
152 ((FGPID*)Systems[i])->ResetPastStates();
156 for (unsigned int i=0; i<FCSComponents.size(); i++) {
157 if (FCSComponents[i]->GetType() == "LAG" ||
158 FCSComponents[i]->GetType() == "LEAD_LAG" ||
159 FCSComponents[i]->GetType() == "WASHOUT" ||
160 FCSComponents[i]->GetType() == "SECOND_ORDER_FILTER" ||
161 FCSComponents[i]->GetType() == "INTEGRATOR")
163 ((FGFilter*)FCSComponents[i])->ResetPastStates();
164 } else if (FCSComponents[i]->GetType() == "PID" ) {
165 ((FGPID*)FCSComponents[i])->ResetPastStates();
169 for (unsigned int i=0; i<APComponents.size(); i++) {
170 if (APComponents[i]->GetType() == "LAG" ||
171 APComponents[i]->GetType() == "LEAD_LAG" ||
172 APComponents[i]->GetType() == "WASHOUT" ||
173 APComponents[i]->GetType() == "SECOND_ORDER_FILTER" ||
174 APComponents[i]->GetType() == "INTEGRATOR")
176 ((FGFilter*)APComponents[i])->ResetPastStates();
177 } else if (APComponents[i]->GetType() == "PID" ) {
178 ((FGPID*)APComponents[i])->ResetPastStates();
185 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
186 // Notes: In this logic the default engine commands are set. This is simply a
187 // sort of safe-mode method in case the user has not defined control laws for
188 // throttle, mixture, and prop-advance. The throttle, mixture, and prop advance
189 // positions are set equal to the respective commands. Any control logic that is
190 // actually present in the flight_control or autopilot section will override
191 // these simple assignments.
193 bool FGFCS::Run(void)
197 if (FGModel::Run()) return true; // fast exit if nothing to do
198 if (FDMExec->Holding()) return false;
200 for (i=0; i<ThrottlePos.size(); i++) ThrottlePos[i] = ThrottleCmd[i];
201 for (i=0; i<MixturePos.size(); i++) MixturePos[i] = MixtureCmd[i];
202 for (i=0; i<PropAdvance.size(); i++) PropAdvance[i] = PropAdvanceCmd[i];
203 for (i=0; i<PropFeather.size(); i++) PropFeather[i] = PropFeatherCmd[i];
205 // Set the default steering angle
206 for (i=0; i<SteerPosDeg.size(); i++) {
207 FGLGear* gear = GroundReactions->GetGearUnit(i);
208 SteerPosDeg[i] = gear->GetDefaultSteerAngle( GetDsCmd() );
211 // Cycle through the sensor, systems, autopilot, and flight control components
213 for (i=0; i<sensors.size(); i++) sensors[i]->Run();
215 // Execute Systems in order
216 for (i=0; i<Systems.size(); i++) Systems[i]->Run();
219 for (i=0; i<APComponents.size(); i++) APComponents[i]->Run();
221 // Execute Flight Control System
222 for (i=0; i<FCSComponents.size(); i++) FCSComponents[i]->Run();
227 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
229 void FGFCS::SetDaLPos( int form , double pos )
234 DaLPos[ofDeg] = pos*radtodeg;
237 DaLPos[ofRad] = pos*degtorad;
241 DaLPos[ofNorm] = pos;
243 DaLPos[ofMag] = fabs(DaLPos[ofRad]);
246 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
248 void FGFCS::SetDaRPos( int form , double pos )
253 DaRPos[ofDeg] = pos*radtodeg;
256 DaRPos[ofRad] = pos*degtorad;
260 DaRPos[ofNorm] = pos;
262 DaRPos[ofMag] = fabs(DaRPos[ofRad]);
265 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
267 void FGFCS::SetDePos( int form , double pos )
272 DePos[ofDeg] = pos*radtodeg;
275 DePos[ofRad] = pos*degtorad;
281 DePos[ofMag] = fabs(DePos[ofRad]);
284 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
286 void FGFCS::SetDrPos( int form , double pos )
291 DrPos[ofDeg] = pos*radtodeg;
294 DrPos[ofRad] = pos*degtorad;
300 DrPos[ofMag] = fabs(DrPos[ofRad]);
303 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
305 void FGFCS::SetDfPos( int form , double pos )
310 DfPos[ofDeg] = pos*radtodeg;
313 DfPos[ofRad] = pos*degtorad;
319 DfPos[ofMag] = fabs(DfPos[ofRad]);
322 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
324 void FGFCS::SetDsbPos( int form , double pos )
329 DsbPos[ofDeg] = pos*radtodeg;
332 DsbPos[ofRad] = pos*degtorad;
336 DsbPos[ofNorm] = pos;
338 DsbPos[ofMag] = fabs(DsbPos[ofRad]);
341 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
343 void FGFCS::SetDspPos( int form , double pos )
348 DspPos[ofDeg] = pos*radtodeg;
351 DspPos[ofRad] = pos*degtorad;
355 DspPos[ofNorm] = pos;
357 DspPos[ofMag] = fabs(DspPos[ofRad]);
360 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
362 void FGFCS::SetThrottleCmd(int engineNum, double setting)
366 if (engineNum < (int)ThrottlePos.size()) {
368 for (ctr=0;ctr<ThrottleCmd.size();ctr++) ThrottleCmd[ctr] = setting;
370 ThrottleCmd[engineNum] = setting;
373 cerr << "Throttle " << engineNum << " does not exist! " << ThrottleCmd.size()
374 << " engines exist, but attempted throttle command is for engine "
375 << engineNum << endl;
379 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
381 void FGFCS::SetThrottlePos(int engineNum, double setting)
385 if (engineNum < (int)ThrottlePos.size()) {
387 for (ctr=0;ctr<ThrottlePos.size();ctr++) ThrottlePos[ctr] = setting;
389 ThrottlePos[engineNum] = setting;
392 cerr << "Throttle " << engineNum << " does not exist! " << ThrottlePos.size()
393 << " engines exist, but attempted throttle position setting is for engine "
394 << engineNum << endl;
398 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
400 double FGFCS::GetThrottleCmd(int engineNum) const
402 if (engineNum < (int)ThrottlePos.size()) {
404 cerr << "Cannot get throttle value for ALL engines" << endl;
406 return ThrottleCmd[engineNum];
409 cerr << "Throttle " << engineNum << " does not exist! " << ThrottleCmd.size()
410 << " engines exist, but throttle setting for engine " << engineNum
411 << " is selected" << endl;
416 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
418 double FGFCS::GetThrottlePos(int engineNum) const
420 if (engineNum < (int)ThrottlePos.size()) {
422 cerr << "Cannot get throttle value for ALL engines" << endl;
424 return ThrottlePos[engineNum];
427 cerr << "Throttle " << engineNum << " does not exist! " << ThrottlePos.size()
428 << " engines exist, but attempted throttle position setting is for engine "
429 << engineNum << endl;
434 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
436 void FGFCS::SetMixtureCmd(int engineNum, double setting)
440 if (engineNum < (int)ThrottlePos.size()) {
442 for (ctr=0;ctr<MixtureCmd.size();ctr++) MixtureCmd[ctr] = setting;
444 MixtureCmd[engineNum] = setting;
449 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
451 void FGFCS::SetMixturePos(int engineNum, double setting)
455 if (engineNum < (int)ThrottlePos.size()) {
457 for (ctr=0;ctr<MixtureCmd.size();ctr++) MixturePos[ctr] = MixtureCmd[ctr];
459 MixturePos[engineNum] = setting;
464 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
466 void FGFCS::SetPropAdvanceCmd(int engineNum, double setting)
470 if (engineNum < (int)ThrottlePos.size()) {
472 for (ctr=0;ctr<PropAdvanceCmd.size();ctr++) PropAdvanceCmd[ctr] = setting;
474 PropAdvanceCmd[engineNum] = setting;
479 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
481 void FGFCS::SetPropAdvance(int engineNum, double setting)
485 if (engineNum < (int)ThrottlePos.size()) {
487 for (ctr=0;ctr<PropAdvanceCmd.size();ctr++) PropAdvance[ctr] = PropAdvanceCmd[ctr];
489 PropAdvance[engineNum] = setting;
494 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
496 void FGFCS::SetFeatherCmd(int engineNum, bool setting)
500 if (engineNum < (int)ThrottlePos.size()) {
502 for (ctr=0;ctr<PropFeatherCmd.size();ctr++) PropFeatherCmd[ctr] = setting;
504 PropFeatherCmd[engineNum] = setting;
509 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
511 void FGFCS::SetPropFeather(int engineNum, bool setting)
515 if (engineNum < (int)ThrottlePos.size()) {
517 for (ctr=0;ctr<PropFeatherCmd.size();ctr++) PropFeather[ctr] = PropFeatherCmd[ctr];
519 PropFeather[engineNum] = setting;
524 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
526 bool FGFCS::Load(Element* el, SystemType systype)
528 string name, file, fname="", interface_property_string, parent_name;
529 vector <FGFCSComponent*> *Components;
530 Element *component_element, *property_element, *sensor_element;
531 Element *channel_element;
535 string separator = "/";
537 // ToDo: The handling of name and file attributes could be improved, here,
538 // considering that a name can be in the external file, as well.
540 name = el->GetAttributeValue("name");
543 fname = el->GetAttributeValue("file");
544 if (systype == stSystem) {
545 file = FindSystemFullPathname(fname);
547 file = FDMExec->GetFullAircraftPath() + separator + fname + ".xml";
550 cerr << "FCS, Autopilot, or system does not appear to be defined inline nor in a file" << endl;
553 document = LoadXMLDocument(file);
555 cerr << "Error loading file " << file << endl;
558 name = document->GetAttributeValue("name");
564 if (document->GetName() == "autopilot") {
565 Components = &APComponents;
566 Name = "Autopilot: " + document->GetAttributeValue("name");
567 } else if (document->GetName() == "flight_control") {
568 Components = &FCSComponents;
569 Name = "FCS: " + document->GetAttributeValue("name");
570 } else if (document->GetName() == "system") {
571 Components = &Systems;
572 Name = "System: " + document->GetAttributeValue("name");
576 if (document->GetName() == "flight_control") bindModel();
578 // Interface properties from any autopilot, flight control, or other system are
579 // all stored in the interface properties array.
581 property_element = document->FindElement("property");
582 if (property_element && debug_lvl > 0) cout << endl << " Declared properties" << endl << endl;
583 while (property_element) {
584 interface_property_string = property_element->GetDataLine();
585 if (PropertyManager->HasNode(interface_property_string)) {
586 cerr << " Property " << interface_property_string << " is already defined." << endl;
589 if ( ! property_element->GetAttributeValue("value").empty())
590 value = property_element->GetAttributeValueAsNumber("value");
591 interface_properties.push_back(new double(value));
592 interface_property_string = property_element->GetDataLine();
593 PropertyManager->Tie(interface_property_string, interface_properties.back());
595 cout << " " << interface_property_string << " (initial value: " << value << ")" << endl;
597 property_element = document->FindNextElement("property");
600 // After reading interface properties in a file, read properties in the local
601 // flight_control, autopilot, or system element. This allows general-purpose
602 // systems to be defined in a file, with overrides or initial loaded constants
603 // supplied in the relevant element of the aircraft configuration file.
605 if (!fname.empty()) {
606 property_element = el->FindElement("property");
607 if (property_element && debug_lvl > 0) cout << endl << " Overriding properties" << endl << endl;
608 while (property_element) {
610 if ( ! property_element->GetAttributeValue("value").empty())
611 value = property_element->GetAttributeValueAsNumber("value");
613 interface_property_string = property_element->GetDataLine();
614 if (PropertyManager->HasNode(interface_property_string)) {
615 FGPropertyManager* node = PropertyManager->GetNode(interface_property_string);
617 cout << " " << "Overriding value for property " << interface_property_string
618 << " (old value: " << node->getDoubleValue() << " new value: " << value << ")" << endl;
619 node->setDoubleValue(value);
621 interface_properties.push_back(new double(value));
622 PropertyManager->Tie(interface_property_string, interface_properties.back());
624 cout << " " << interface_property_string << " (initial value: " << value << ")" << endl;
627 property_element = el->FindNextElement("property");
631 // Any sensor elements that are outside of a channel (in either the autopilot
632 // or the flight_control, or even any possible "system") are placed into the global
633 // "sensors" array, and are executed prior to any autopilot, flight control, or
636 sensor_element = document->FindElement("sensor");
637 while (sensor_element) {
639 sensors.push_back(new FGSensor(this, sensor_element));
641 cerr << highint << fgred << endl << " " << s << endl;
644 sensor_element = document->FindNextElement("sensor");
647 channel_element = document->FindElement("channel");
648 while (channel_element) {
651 cout << endl << highint << fgblue << " Channel "
652 << normint << channel_element->GetAttributeValue("name") << reset << endl;
654 component_element = channel_element->GetElement();
655 while (component_element) {
657 if ((component_element->GetName() == string("lag_filter")) ||
658 (component_element->GetName() == string("lead_lag_filter")) ||
659 (component_element->GetName() == string("washout_filter")) ||
660 (component_element->GetName() == string("second_order_filter")) ||
661 (component_element->GetName() == string("integrator")) )
663 Components->push_back(new FGFilter(this, component_element));
664 } else if ((component_element->GetName() == string("pure_gain")) ||
665 (component_element->GetName() == string("scheduled_gain")) ||
666 (component_element->GetName() == string("aerosurface_scale")))
668 Components->push_back(new FGGain(this, component_element));
669 } else if (component_element->GetName() == string("summer")) {
670 Components->push_back(new FGSummer(this, component_element));
671 } else if (component_element->GetName() == string("deadband")) {
672 Components->push_back(new FGDeadBand(this, component_element));
673 } else if (component_element->GetName() == string("switch")) {
674 Components->push_back(new FGSwitch(this, component_element));
675 } else if (component_element->GetName() == string("kinematic")) {
676 Components->push_back(new FGKinemat(this, component_element));
677 } else if (component_element->GetName() == string("fcs_function")) {
678 Components->push_back(new FGFCSFunction(this, component_element));
679 } else if (component_element->GetName() == string("pid")) {
680 Components->push_back(new FGPID(this, component_element));
681 } else if (component_element->GetName() == string("actuator")) {
682 Components->push_back(new FGActuator(this, component_element));
683 } else if (component_element->GetName() == string("sensor")) {
684 Components->push_back(new FGSensor(this, component_element));
685 } else if (component_element->GetName() == string("accelerometer")) {
686 Components->push_back(new FGAccelerometer(this, component_element));
688 cerr << "Unknown FCS component: " << component_element->GetName() << endl;
691 cerr << highint << fgred << endl << " " << s << endl;
692 cerr << reset << endl;
695 component_element = channel_element->GetNextElement();
697 channel_element = document->FindNextElement("channel");
705 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
707 double FGFCS::GetBrake(FGLGear::BrakeGroup bg)
710 case FGLGear::bgLeft:
712 case FGLGear::bgRight:
714 case FGLGear::bgCenter:
717 cerr << "GetBrake asked to return a bogus brake value" << endl;
722 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
724 string FGFCS::FindSystemFullPathname(string system_filename)
726 string fullpath, localpath;
727 string systemPath = FDMExec->GetSystemsPath();
728 string aircraftPath = FDMExec->GetFullAircraftPath();
729 ifstream system_file;
731 string separator = "/";
733 fullpath = systemPath + separator;
734 localpath = aircraftPath + separator + "Systems" + separator;
736 system_file.open(string(fullpath + system_filename + ".xml").c_str());
737 if ( !system_file.is_open()) {
738 system_file.open(string(localpath + system_filename + ".xml").c_str());
739 if ( !system_file.is_open()) {
740 cerr << " Could not open system file: " << system_filename << " in path "
741 << fullpath << " or " << localpath << endl;
744 return string(localpath + system_filename + ".xml");
747 return string(fullpath + system_filename + ".xml");
750 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
752 ifstream* FGFCS::FindSystemFile(string system_filename)
754 string fullpath, localpath;
755 string systemPath = FDMExec->GetSystemsPath();
756 string aircraftPath = FDMExec->GetFullAircraftPath();
757 ifstream* system_file = new ifstream();
759 string separator = "/";
761 fullpath = systemPath + separator;
762 localpath = aircraftPath + separator + "Systems" + separator;
764 system_file->open(string(fullpath + system_filename + ".xml").c_str());
765 if ( !system_file->is_open()) {
766 system_file->open(string(localpath + system_filename + ".xml").c_str());
767 if ( !system_file->is_open()) {
768 cerr << " Could not open system file: " << system_filename << " in path "
769 << fullpath << " or " << localpath << endl;
775 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
777 string FGFCS::GetComponentStrings(string delimeter)
780 string CompStrings = "";
781 bool firstime = true;
784 for (unsigned int i=0; i<Systems.size(); i++) {
785 if (firstime) firstime = false;
786 else CompStrings += delimeter;
788 CompStrings += Systems[i]->GetName();
792 for (comp = 0; comp < APComponents.size(); comp++)
794 if (firstime) firstime = false;
795 else CompStrings += delimeter;
797 CompStrings += APComponents[comp]->GetName();
801 for (comp = 0; comp < FCSComponents.size(); comp++) {
802 if (firstime) firstime = false;
803 else CompStrings += delimeter;
805 CompStrings += FCSComponents[comp]->GetName();
812 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
814 string FGFCS::GetComponentValues(string delimeter)
816 std::ostringstream buf;
819 bool firstime = true;
822 for (unsigned int i=0; i<Systems.size(); i++) {
823 if (firstime) firstime = false;
824 else buf << delimeter;
826 buf << setprecision(9) << Systems[i]->GetOutput();
830 for (comp = 0; comp < APComponents.size(); comp++) {
831 if (firstime) firstime = false;
832 else buf << delimeter;
834 buf << setprecision(9) << APComponents[comp]->GetOutput();
838 for (comp = 0; comp < FCSComponents.size(); comp++) {
839 if (firstime) firstime = false;
840 else buf << delimeter;
842 buf << setprecision(9) << FCSComponents[comp]->GetOutput();
849 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
851 void FGFCS::AddThrottle(void)
853 ThrottleCmd.push_back(0.0);
854 ThrottlePos.push_back(0.0);
855 MixtureCmd.push_back(0.0); // assume throttle and mixture are coupled
856 MixturePos.push_back(0.0);
857 PropAdvanceCmd.push_back(0.0); // assume throttle and prop pitch are coupled
858 PropAdvance.push_back(0.0);
859 PropFeatherCmd.push_back(false);
860 PropFeather.push_back(false);
862 unsigned int num = (unsigned int)ThrottleCmd.size()-1;
866 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
868 void FGFCS::AddGear(void)
870 SteerPosDeg.push_back(0.0);
873 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
875 double FGFCS::GetDt(void)
877 return FDMExec->GetDeltaT()*rate;
880 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
882 void FGFCS::bind(void)
884 PropertyManager->Tie("fcs/aileron-cmd-norm", this, &FGFCS::GetDaCmd, &FGFCS::SetDaCmd);
885 PropertyManager->Tie("fcs/elevator-cmd-norm", this, &FGFCS::GetDeCmd, &FGFCS::SetDeCmd);
886 PropertyManager->Tie("fcs/rudder-cmd-norm", this, &FGFCS::GetDrCmd, &FGFCS::SetDrCmd);
887 PropertyManager->Tie("fcs/flap-cmd-norm", this, &FGFCS::GetDfCmd, &FGFCS::SetDfCmd);
888 PropertyManager->Tie("fcs/speedbrake-cmd-norm", this, &FGFCS::GetDsbCmd, &FGFCS::SetDsbCmd);
889 PropertyManager->Tie("fcs/spoiler-cmd-norm", this, &FGFCS::GetDspCmd, &FGFCS::SetDspCmd);
890 PropertyManager->Tie("fcs/pitch-trim-cmd-norm", this, &FGFCS::GetPitchTrimCmd, &FGFCS::SetPitchTrimCmd);
891 PropertyManager->Tie("fcs/roll-trim-cmd-norm", this, &FGFCS::GetRollTrimCmd, &FGFCS::SetRollTrimCmd);
892 PropertyManager->Tie("fcs/yaw-trim-cmd-norm", this, &FGFCS::GetYawTrimCmd, &FGFCS::SetYawTrimCmd);
894 PropertyManager->Tie("fcs/left-aileron-pos-rad", this, ofRad, &FGFCS::GetDaLPos, &FGFCS::SetDaLPos);
895 PropertyManager->Tie("fcs/left-aileron-pos-deg", this, ofDeg, &FGFCS::GetDaLPos, &FGFCS::SetDaLPos);
896 PropertyManager->Tie("fcs/left-aileron-pos-norm", this, ofNorm, &FGFCS::GetDaLPos, &FGFCS::SetDaLPos);
897 PropertyManager->Tie("fcs/mag-left-aileron-pos-rad", this, ofMag, &FGFCS::GetDaLPos);
899 PropertyManager->Tie("fcs/right-aileron-pos-rad", this, ofRad, &FGFCS::GetDaRPos, &FGFCS::SetDaRPos);
900 PropertyManager->Tie("fcs/right-aileron-pos-deg", this, ofDeg, &FGFCS::GetDaRPos, &FGFCS::SetDaRPos);
901 PropertyManager->Tie("fcs/right-aileron-pos-norm", this, ofNorm, &FGFCS::GetDaRPos, &FGFCS::SetDaRPos);
902 PropertyManager->Tie("fcs/mag-right-aileron-pos-rad", this, ofMag, &FGFCS::GetDaRPos);
904 PropertyManager->Tie("fcs/elevator-pos-rad", this, ofRad, &FGFCS::GetDePos, &FGFCS::SetDePos);
905 PropertyManager->Tie("fcs/elevator-pos-deg", this, ofDeg, &FGFCS::GetDePos, &FGFCS::SetDePos);
906 PropertyManager->Tie("fcs/elevator-pos-norm", this, ofNorm, &FGFCS::GetDePos, &FGFCS::SetDePos);
907 PropertyManager->Tie("fcs/mag-elevator-pos-rad", this, ofMag, &FGFCS::GetDePos);
909 PropertyManager->Tie("fcs/rudder-pos-rad", this,ofRad, &FGFCS::GetDrPos, &FGFCS::SetDrPos);
910 PropertyManager->Tie("fcs/rudder-pos-deg", this,ofDeg, &FGFCS::GetDrPos, &FGFCS::SetDrPos);
911 PropertyManager->Tie("fcs/rudder-pos-norm", this,ofNorm, &FGFCS::GetDrPos, &FGFCS::SetDrPos);
912 PropertyManager->Tie("fcs/mag-rudder-pos-rad", this,ofMag, &FGFCS::GetDrPos);
914 PropertyManager->Tie("fcs/flap-pos-rad", this,ofRad, &FGFCS::GetDfPos, &FGFCS::SetDfPos);
915 PropertyManager->Tie("fcs/flap-pos-deg", this,ofDeg, &FGFCS::GetDfPos, &FGFCS::SetDfPos);
916 PropertyManager->Tie("fcs/flap-pos-norm", this,ofNorm, &FGFCS::GetDfPos, &FGFCS::SetDfPos);
918 PropertyManager->Tie("fcs/speedbrake-pos-rad", this,ofRad, &FGFCS::GetDsbPos, &FGFCS::SetDsbPos);
919 PropertyManager->Tie("fcs/speedbrake-pos-deg", this,ofDeg, &FGFCS::GetDsbPos, &FGFCS::SetDsbPos);
920 PropertyManager->Tie("fcs/speedbrake-pos-norm", this,ofNorm, &FGFCS::GetDsbPos, &FGFCS::SetDsbPos);
921 PropertyManager->Tie("fcs/mag-speedbrake-pos-rad", this,ofMag, &FGFCS::GetDsbPos);
923 PropertyManager->Tie("fcs/spoiler-pos-rad", this, ofRad, &FGFCS::GetDspPos, &FGFCS::SetDspPos);
924 PropertyManager->Tie("fcs/spoiler-pos-deg", this, ofDeg, &FGFCS::GetDspPos, &FGFCS::SetDspPos);
925 PropertyManager->Tie("fcs/spoiler-pos-norm", this, ofNorm, &FGFCS::GetDspPos, &FGFCS::SetDspPos);
926 PropertyManager->Tie("fcs/mag-spoiler-pos-rad", this, ofMag, &FGFCS::GetDspPos);
928 PropertyManager->Tie("gear/gear-pos-norm", this, &FGFCS::GetGearPos, &FGFCS::SetGearPos);
929 PropertyManager->Tie("gear/gear-cmd-norm", this, &FGFCS::GetGearCmd, &FGFCS::SetGearCmd);
930 PropertyManager->Tie("fcs/left-brake-cmd-norm", this, &FGFCS::GetLBrake, &FGFCS::SetLBrake);
931 PropertyManager->Tie("fcs/right-brake-cmd-norm", this, &FGFCS::GetRBrake, &FGFCS::SetRBrake);
932 PropertyManager->Tie("fcs/center-brake-cmd-norm", this, &FGFCS::GetCBrake, &FGFCS::SetCBrake);
933 PropertyManager->Tie("fcs/steer-cmd-norm", this, &FGFCS::GetDsCmd, &FGFCS::SetDsCmd);
935 PropertyManager->Tie("gear/tailhook-pos-norm", this, &FGFCS::GetTailhookPos, &FGFCS::SetTailhookPos);
936 PropertyManager->Tie("fcs/wing-fold-pos-norm", this, &FGFCS::GetWingFoldPos, &FGFCS::SetWingFoldPos);
939 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
940 // Technically, this function should probably bind propulsion type specific controls
941 // rather than mixture and prop-advance.
943 void FGFCS::bindThrottle(unsigned int num)
947 tmp = CreateIndexedPropertyName("fcs/throttle-cmd-norm", num);
948 PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetThrottleCmd,
949 &FGFCS::SetThrottleCmd);
950 tmp = CreateIndexedPropertyName("fcs/throttle-pos-norm", num);
951 PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetThrottlePos,
952 &FGFCS::SetThrottlePos);
953 tmp = CreateIndexedPropertyName("fcs/mixture-cmd-norm", num);
954 PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetMixtureCmd,
955 &FGFCS::SetMixtureCmd);
956 tmp = CreateIndexedPropertyName("fcs/mixture-pos-norm", num);
957 PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetMixturePos,
958 &FGFCS::SetMixturePos);
959 tmp = CreateIndexedPropertyName("fcs/advance-cmd-norm", num);
960 PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetPropAdvanceCmd,
961 &FGFCS::SetPropAdvanceCmd);
962 tmp = CreateIndexedPropertyName("fcs/advance-pos-norm", num);
963 PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetPropAdvance,
964 &FGFCS::SetPropAdvance);
965 tmp = CreateIndexedPropertyName("fcs/feather-cmd-norm", num);
966 PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetFeatherCmd,
967 &FGFCS::SetFeatherCmd);
968 tmp = CreateIndexedPropertyName("fcs/feather-pos-norm", num);
969 PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetPropFeather,
970 &FGFCS::SetPropFeather);
973 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
975 void FGFCS::bindModel(void)
980 for (i=0; i<SteerPosDeg.size(); i++) {
981 if (GroundReactions->GetGearUnit(i)->GetSteerable()) {
982 tmp = CreateIndexedPropertyName("fcs/steer-pos-deg", i);
983 PropertyManager->Tie( tmp.c_str(), this, i, &FGFCS::GetSteerPosDeg, &FGFCS::SetSteerPosDeg);
988 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
989 // The bitmasked value choices are as follows:
990 // unset: In this case (the default) JSBSim would only print
991 // out the normally expected messages, essentially echoing
992 // the config files as they are read. If the environment
993 // variable is not set, debug_lvl is set to 1 internally
994 // 0: This requests JSBSim not to output any messages
996 // 1: This value explicity requests the normal JSBSim
998 // 2: This value asks for a message to be printed out when
999 // a class is instantiated
1000 // 4: When this value is set, a message is displayed when a
1001 // FGModel object executes its Run() method
1002 // 8: When this value is set, various runtime state variables
1003 // are printed out periodically
1004 // 16: When set various parameters are sanity checked and
1005 // a message is printed out when they go out of bounds
1007 void FGFCS::Debug(int from)
1009 if (debug_lvl <= 0) return;
1011 if (debug_lvl & 1) { // Standard console startup message output
1012 if (from == 2) { // Loader
1013 cout << endl << " " << Name << endl;
1016 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
1017 if (from == 0) cout << "Instantiated: FGFCS" << endl;
1018 if (from == 1) cout << "Destroyed: FGFCS" << endl;
1020 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
1022 if (debug_lvl & 8 ) { // Runtime state variables
1024 if (debug_lvl & 16) { // Sanity checking
1026 if (debug_lvl & 64) {
1027 if (from == 0) { // Constructor
1028 cout << IdSrc << endl;
1029 cout << IdHdr << endl;