1 // autopilotgroup.cxx - an even more flexible, generic way to build autopilots
3 // Written by Torsten Dreyer
4 // Based heavily on work created by Curtis Olson, started January 2004.
6 // Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
7 // Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License as
11 // published by the Free Software Foundation; either version 2 of the
12 // License, or (at your option) any later version.
14 // This program is distributed in the hope that it will be useful, but
15 // WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // General Public License for more details.
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 #include "autopilot.hxx"
29 #include "autopilotgroup.hxx"
31 #include <Main/fg_props.hxx>
32 #include <Main/globals.hxx>
33 #include <Main/util.hxx>
35 #include <simgear/misc/sg_path.hxx>
36 #include <simgear/props/props_io.hxx>
37 #include <simgear/structure/SGExpression.hxx>
42 using simgear::PropertyList;
44 FGXMLAutopilotGroup::FGXMLAutopilotGroup() :
46 #ifdef XMLAUTO_USEHELPER
47 ,average(0.0), // average/filtered prediction
48 v_last(0.0), // last velocity
49 last_static_pressure(0.0),
50 vel(fgGetNode( "/velocities/airspeed-kt", true )),
51 // Estimate speed in 5,10 seconds
52 lookahead5(fgGetNode( "/autopilot/internal/lookahead-5-sec-airspeed-kt", true )),
53 lookahead10(fgGetNode( "/autopilot/internal/lookahead-10-sec-airspeed-kt", true )),
54 bug(fgGetNode( "/autopilot/settings/heading-bug-deg", true )),
55 mag_hdg(fgGetNode( "/orientation/heading-magnetic-deg", true )),
56 bug_error(fgGetNode( "/autopilot/internal/heading-bug-error-deg", true )),
57 fdm_bug_error(fgGetNode( "/autopilot/internal/fdm-heading-bug-error-deg", true )),
58 target_true(fgGetNode( "/autopilot/settings/true-heading-deg", true )),
59 true_hdg(fgGetNode( "/orientation/heading-deg", true )),
60 true_error(fgGetNode( "/autopilot/internal/true-heading-error-deg", true )),
61 target_nav1(fgGetNode( "/instrumentation/nav[0]/radials/target-auto-hdg-deg", true )),
62 true_nav1(fgGetNode( "/autopilot/internal/nav1-heading-error-deg", true )),
63 true_track_nav1(fgGetNode( "/autopilot/internal/nav1-track-error-deg", true )),
64 nav1_course_error(fgGetNode( "/autopilot/internal/nav1-course-error", true )),
65 nav1_selected_course(fgGetNode( "/instrumentation/nav[0]/radials/selected-deg", true )),
66 vs_fps(fgGetNode( "/velocities/vertical-speed-fps", true )),
67 vs_fpm(fgGetNode( "/autopilot/internal/vert-speed-fpm", true )),
68 static_pressure(fgGetNode( "/systems/static[0]/pressure-inhg", true )),
69 pressure_rate(fgGetNode( "/autopilot/internal/pressure-rate", true )),
70 track(fgGetNode( "/orientation/track-deg", true ))
75 void FGXMLAutopilotGroup::update( double dt )
77 // update all configured autopilots
78 SGSubsystemGroup::update( dt );
79 #ifdef XMLAUTO_USEHELPER
80 // update helper values
81 double v = vel->getDoubleValue();
84 a = (v - v_last) / dt;
87 average = (1.0 - dt) * average + dt * a;
92 lookahead5->setDoubleValue( v + average * 5.0 );
93 lookahead10->setDoubleValue( v + average * 10.0 );
97 // Calculate heading bug error normalized to +/- 180.0
98 double diff = bug->getDoubleValue() - mag_hdg->getDoubleValue();
99 SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
100 bug_error->setDoubleValue( diff );
102 fdm_bug_error->setDoubleValue( diff );
104 // Calculate true heading error normalized to +/- 180.0
105 diff = target_true->getDoubleValue() - true_hdg->getDoubleValue();
106 SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
107 true_error->setDoubleValue( diff );
109 // Calculate nav1 target heading error normalized to +/- 180.0
110 diff = target_nav1->getDoubleValue() - true_hdg->getDoubleValue();
111 SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
112 true_nav1->setDoubleValue( diff );
114 // Calculate true groundtrack
115 diff = target_nav1->getDoubleValue() - track->getDoubleValue();
116 SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
117 true_track_nav1->setDoubleValue( diff );
119 // Calculate nav1 selected course error normalized to +/- 180.0
120 diff = nav1_selected_course->getDoubleValue() - mag_hdg->getDoubleValue();
121 SG_NORMALIZE_RANGE( diff, -180.0, 180.0 );
122 nav1_course_error->setDoubleValue( diff );
124 // Calculate vertical speed in fpm
125 vs_fpm->setDoubleValue( vs_fps->getDoubleValue() * 60.0 );
128 // Calculate static port pressure rate in [inhg/s].
129 // Used to determine vertical speed.
131 double current_static_pressure = static_pressure->getDoubleValue();
132 double current_pressure_rate =
133 ( current_static_pressure - last_static_pressure ) / dt;
135 pressure_rate->setDoubleValue(current_pressure_rate);
136 last_static_pressure = current_static_pressure;
141 void FGXMLAutopilotGroup::reinit()
143 SGSubsystemGroup::unbind();
145 for( vector<string>::size_type i = 0; i < _autopilotNames.size(); i++ ) {
146 FGXMLAutopilot::Autopilot * ap = (FGXMLAutopilot::Autopilot*)get_subsystem( _autopilotNames[i] );
147 if( ap == NULL ) continue; // ?
148 remove_subsystem( _autopilotNames[i] );
151 _autopilotNames.clear();
155 void FGXMLAutopilotGroup::init()
157 PropertyList autopilotNodes = fgGetNode( "/sim/systems", true )->getChildren("autopilot");
158 if( autopilotNodes.size() == 0 ) {
159 SG_LOG( SG_ALL, SG_WARN, "No autopilot configuration specified for this model!");
163 for( PropertyList::size_type i = 0; i < autopilotNodes.size(); i++ ) {
164 SGPropertyNode_ptr pathNode = autopilotNodes[i]->getNode( "path" );
165 if( pathNode == NULL ) {
166 SG_LOG( SG_ALL, SG_WARN, "No autopilot configuration file specified for this autopilot!");
171 SGPropertyNode_ptr nameNode = autopilotNodes[i]->getNode( "name" );
172 if( nameNode != NULL ) {
173 apName = nameNode->getStringValue();
175 std::ostringstream buf;
176 buf << "unnamed_autopilot_" << i;
181 // check for duplicate names
182 string name = apName;
183 for( unsigned i = 0; get_subsystem( apName.c_str() ) != NULL; i++ ) {
185 buf << apName << "_" << i;
189 SG_LOG( SG_ALL, SG_ALERT, "Duplicate autopilot component " << name << ", renamed to " << apName );
192 if( get_subsystem( apName.c_str() ) != NULL ) {
193 SG_LOG( SG_ALL, SG_ALERT, "Duplicate autopilot configuration name " << apName << " ignored" );
197 SGPath config = globals->resolve_aircraft_path(pathNode->getStringValue());
199 SG_LOG( SG_ALL, SG_INFO, "Reading autopilot configuration from " << config.str() );
202 SGPropertyNode_ptr root = new SGPropertyNode();
203 readProperties( config.str(), root );
205 SG_LOG( SG_AUTOPILOT, SG_INFO, "adding autopilot subsystem " << apName );
206 FGXMLAutopilot::Autopilot * ap = new FGXMLAutopilot::Autopilot( autopilotNodes[i], root );
207 ap->set_name( apName );
208 set_subsystem( apName, ap );
209 _autopilotNames.push_back( apName );
211 } catch (const sg_exception& e) {
212 SG_LOG( SG_AUTOPILOT, SG_ALERT, "Failed to load autopilot configuration: "
213 << config.str() << ":" << e.getMessage() );
218 SGSubsystemGroup::bind();
219 SGSubsystemGroup::init();