1 // xmlauto.cxx - a more flexible, generic way to build autopilots
3 // Written by Curtis Olson, started January 2004.
5 // Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include <simgear/structure/exception.hxx>
28 #include <simgear/misc/sg_path.hxx>
29 #include <simgear/sg_inlines.h>
31 #include <Main/fg_props.hxx>
32 #include <Main/globals.hxx>
33 #include <Main/util.hxx>
35 #include "xmlauto.hxx"
38 FGPIDController::FGPIDController( SGPropertyNode *node ):
62 for ( i = 0; i < node->nChildren(); ++i ) {
63 SGPropertyNode *child = node->getChild(i);
64 string cname = child->getName();
65 string cval = child->getStringValue();
66 if ( cname == "name" ) {
68 } else if ( cname == "debug" ) {
69 debug = child->getBoolValue();
70 } else if ( cname == "enable" ) {
71 // cout << "parsing enable" << endl;
72 SGPropertyNode *prop = child->getChild( "prop" );
74 // cout << "prop = " << prop->getStringValue() << endl;
75 enable_prop = fgGetNode( prop->getStringValue(), true );
77 // cout << "no prop child" << endl;
79 SGPropertyNode *val = child->getChild( "value" );
81 enable_value = val->getStringValue();
83 SGPropertyNode *pass = child->getChild( "honor-passive" );
85 honor_passive = pass->getBoolValue();
87 } else if ( cname == "input" ) {
88 SGPropertyNode *prop = child->getChild( "prop" );
90 input_prop = fgGetNode( prop->getStringValue(), true );
92 prop = child->getChild( "scale" );
94 y_scale = prop->getDoubleValue();
96 prop = child->getChild( "offset" );
98 y_offset = prop->getDoubleValue();
100 } else if ( cname == "reference" ) {
101 SGPropertyNode *prop = child->getChild( "prop" );
102 if ( prop != NULL ) {
103 r_n_prop = fgGetNode( prop->getStringValue(), true );
105 prop = child->getChild( "value" );
106 if ( prop != NULL ) {
107 r_n_value = prop->getDoubleValue();
110 prop = child->getChild( "scale" );
111 if ( prop != NULL ) {
112 r_scale = prop->getDoubleValue();
114 prop = child->getChild( "offset" );
115 if ( prop != NULL ) {
116 r_offset = prop->getDoubleValue();
118 } else if ( cname == "output" ) {
120 SGPropertyNode *prop;
121 while ( (prop = child->getChild("prop", i)) != NULL ) {
122 SGPropertyNode *tmp = fgGetNode( prop->getStringValue(), true );
123 output_list.push_back( tmp );
126 } else if ( cname == "config" ) {
127 SGPropertyNode *config;
129 config = child->getChild( "Ts" );
130 if ( config != NULL ) {
131 desiredTs = config->getDoubleValue();
134 config = child->getChild( "Kp" );
135 if ( config != NULL ) {
136 SGPropertyNode *val = config->getChild( "value" );
138 Kp = val->getDoubleValue();
141 SGPropertyNode *prop = config->getChild( "prop" );
142 if ( prop != NULL ) {
143 Kp_prop = fgGetNode( prop->getStringValue(), true );
145 Kp_prop->setDoubleValue(Kp);
149 // output deprecated usage warning
150 if (val == NULL && prop == NULL) {
151 Kp = config->getDoubleValue();
152 SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated Kp config. Please use <prop> and/or <value> tags." );
153 if ( name.length() ) {
154 SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
159 config = child->getChild( "beta" );
160 if ( config != NULL ) {
161 beta = config->getDoubleValue();
164 config = child->getChild( "alpha" );
165 if ( config != NULL ) {
166 alpha = config->getDoubleValue();
169 config = child->getChild( "gamma" );
170 if ( config != NULL ) {
171 gamma = config->getDoubleValue();
174 config = child->getChild( "Ti" );
175 if ( config != NULL ) {
176 SGPropertyNode *val = config->getChild( "value" );
178 Ti = val->getDoubleValue();
181 SGPropertyNode *prop = config->getChild( "prop" );
182 if ( prop != NULL ) {
183 Ti_prop = fgGetNode( prop->getStringValue(), true );
185 Ti_prop->setDoubleValue(Kp);
189 // output deprecated usage warning
190 if (val == NULL && prop == NULL) {
191 Ti = config->getDoubleValue();
192 SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated Ti config. Please use <prop> and/or <value> tags." );
193 if ( name.length() ) {
194 SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
199 config = child->getChild( "Td" );
200 if ( config != NULL ) {
201 SGPropertyNode *val = config->getChild( "value" );
203 Td = val->getDoubleValue();
206 SGPropertyNode *prop = config->getChild( "prop" );
207 if ( prop != NULL ) {
208 Td_prop = fgGetNode( prop->getStringValue(), true );
210 Td_prop->setDoubleValue(Kp);
214 // output deprecated usage warning
215 if (val == NULL && prop == NULL) {
216 Td = config->getDoubleValue();
217 SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated Td config. Please use <prop> and/or <value> tags." );
218 if ( name.length() ) {
219 SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
224 config = child->getChild( "u_min" );
225 if ( config != NULL ) {
226 SGPropertyNode *val = config->getChild( "value" );
228 u_min = val->getDoubleValue();
231 SGPropertyNode *prop = config->getChild( "prop" );
232 if ( prop != NULL ) {
233 umin_prop = fgGetNode( prop->getStringValue(), true );
235 umin_prop->setDoubleValue(u_min);
239 // output deprecated usage warning
240 if (val == NULL && prop == NULL) {
241 u_min = config->getDoubleValue();
242 SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated u_min config. Please use <prop> and/or <value> tags." );
243 if ( name.length() ) {
244 SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
249 config = child->getChild( "u_max" );
250 if ( config != NULL ) {
251 SGPropertyNode *val = config->getChild( "value" );
253 u_max = val->getDoubleValue();
256 SGPropertyNode *prop = config->getChild( "prop" );
257 if ( prop != NULL ) {
258 umax_prop = fgGetNode( prop->getStringValue(), true );
260 umax_prop->setDoubleValue(u_max);
264 // output deprecated usage warning
265 if (val == NULL && prop == NULL) {
266 u_max = config->getDoubleValue();
267 SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated u_max config. Please use <prop> and/or <value> tags." );
268 if ( name.length() ) {
269 SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
274 SG_LOG( SG_AUTOPILOT, SG_WARN, "Error in autopilot config logic" );
275 if ( name.length() ) {
276 SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
286 * Ok! Here is the PID controller algorithm that I would like to see
289 * delta_u_n = Kp * [ (ep_n - ep_n-1) + ((Ts/Ti)*e_n)
290 * + (Td/Ts)*(edf_n - 2*edf_n-1 + edf_n-2) ]
292 * u_n = u_n-1 + delta_u_n
296 * delta_u : The incremental output
297 * Kp : Proportional gain
298 * ep : Proportional error with reference weighing
301 * beta : Weighing factor
302 * r : Reference (setpoint)
303 * y : Process value, measured
306 * Ts : Sampling interval
307 * Ti : Integrator time
308 * Td : Derivator time
309 * edf : Derivate error with reference weighing and filtering
310 * edf_n = edf_n-1 / ((Ts/Tf) + 1) + ed_n * (Ts/Tf) / ((Ts/Tf) + 1)
313 * Tf = alpha * Td , where alpha usually is set to 0.1
314 * ed : Unfiltered derivate error with reference weighing
317 * gamma : Weighing factor
319 * u : absolute output
321 * Index n means the n'th value.
326 * y_n , r_n , beta=1 , gamma=0 , alpha=0.1 ,
327 * Kp , Ti , Td , Ts (is the sampling time available?)
334 void FGPIDController::update( double dt ) {
335 double ep_n; // proportional error with reference weighing
337 double ed_n; // derivative error
338 double edf_n; // derivative error filter
339 double Tf; // filter time
340 double delta_u_n = 0.0; // incremental output
341 double u_n = 0.0; // absolute output
342 double Ts; // sampling interval (sec)
343 if (umin_prop != NULL)u_min = umin_prop->getDoubleValue();
344 if (umax_prop != NULL)u_max = umax_prop->getDoubleValue();
345 if (Ti_prop != NULL)Ti = Ti_prop->getDoubleValue();
346 if (Td_prop != NULL)Td = Td_prop->getDoubleValue();
349 if ( elapsedTime <= desiredTs ) {
350 // do nothing if time step is not positive (i.e. no time has
357 if (enable_prop != NULL && enable_prop->getStringValue() == enable_value) {
359 // first time being enabled, seed u_n with current
360 // property tree value
361 u_n = output_list[0]->getDoubleValue();
363 if ( u_n < u_min ) { u_n = u_min; }
364 if ( u_n > u_max ) { u_n = u_max; }
372 if ( enabled && Ts > 0.0) {
373 if ( debug ) cout << "Updating " << name
374 << " Ts " << Ts << endl;
377 if ( input_prop != NULL ) {
378 y_n = input_prop->getDoubleValue() * y_scale + y_offset;
382 if ( r_n_prop != NULL ) {
383 r_n = r_n_prop->getDoubleValue() * r_scale + r_offset;
388 if ( debug ) cout << " input = " << y_n << " ref = " << r_n << endl;
390 // Calculates proportional error:
391 ep_n = beta * r_n - y_n;
392 if ( debug ) cout << " ep_n = " << ep_n;
393 if ( debug ) cout << " ep_n_1 = " << ep_n_1;
397 if ( debug ) cout << " e_n = " << e_n;
399 // Calculates derivate error:
400 ed_n = gamma * r_n - y_n;
401 if ( debug ) cout << " ed_n = " << ed_n;
404 // Calculates filter time:
406 if ( debug ) cout << " Tf = " << Tf;
408 // Filters the derivate error:
409 edf_n = edf_n_1 / (Ts/Tf + 1)
410 + ed_n * (Ts/Tf) / (Ts/Tf + 1);
411 if ( debug ) cout << " edf_n = " << edf_n;
416 // Calculates the incremental output:
418 if (Kp_prop != NULL) {
419 Kp = Kp_prop->getDoubleValue();
421 delta_u_n = Kp * ( (ep_n - ep_n_1)
423 + ((Td/Ts) * (edf_n - 2*edf_n_1 + edf_n_2)) );
427 cout << " delta_u_n = " << delta_u_n << endl;
428 cout << "P:" << Kp * (ep_n - ep_n_1)
429 << " I:" << Kp * ((Ts/Ti) * e_n)
430 << " D:" << Kp * ((Td/Ts) * (edf_n - 2*edf_n_1 + edf_n_2))
434 // Integrator anti-windup logic:
435 if ( delta_u_n > (u_max - u_n_1) ) {
436 delta_u_n = u_max - u_n_1;
437 if ( debug ) cout << " max saturation " << endl;
438 } else if ( delta_u_n < (u_min - u_n_1) ) {
439 delta_u_n = u_min - u_n_1;
440 if ( debug ) cout << " min saturation " << endl;
443 // Calculates absolute output:
444 u_n = u_n_1 + delta_u_n;
445 if ( debug ) cout << " output = " << u_n << endl;
447 // Updates indexed values;
453 // passive_ignore == true means that we go through all the
454 // motions, but drive the outputs. This is analogous to
455 // running the autopilot with the "servos" off. This is
456 // helpful for things like flight directors which position
457 // their vbars from the autopilot computations.
458 if ( passive_mode->getBoolValue() && honor_passive ) {
462 for ( i = 0; i < output_list.size(); ++i ) {
463 output_list[i]->setDoubleValue( u_n );
466 } else if ( !enabled ) {
469 // Updates indexed values;
478 FGPISimpleController::FGPISimpleController( SGPropertyNode *node ):
479 proportional( false ),
496 for ( i = 0; i < node->nChildren(); ++i ) {
497 SGPropertyNode *child = node->getChild(i);
498 string cname = child->getName();
499 string cval = child->getStringValue();
500 if ( cname == "name" ) {
502 } else if ( cname == "debug" ) {
503 debug = child->getBoolValue();
504 } else if ( cname == "enable" ) {
505 // cout << "parsing enable" << endl;
506 SGPropertyNode *prop = child->getChild( "prop" );
507 if ( prop != NULL ) {
508 // cout << "prop = " << prop->getStringValue() << endl;
509 enable_prop = fgGetNode( prop->getStringValue(), true );
511 // cout << "no prop child" << endl;
513 SGPropertyNode *val = child->getChild( "value" );
515 enable_value = val->getStringValue();
517 } else if ( cname == "input" ) {
518 SGPropertyNode *prop = child->getChild( "prop" );
519 if ( prop != NULL ) {
520 input_prop = fgGetNode( prop->getStringValue(), true );
522 prop = child->getChild( "scale" );
523 if ( prop != NULL ) {
524 y_scale = prop->getDoubleValue();
526 } else if ( cname == "reference" ) {
527 SGPropertyNode *prop = child->getChild( "prop" );
528 if ( prop != NULL ) {
529 r_n_prop = fgGetNode( prop->getStringValue(), true );
531 prop = child->getChild( "value" );
532 if ( prop != NULL ) {
533 r_n_value = prop->getDoubleValue();
536 prop = child->getChild( "scale" );
537 if ( prop != NULL ) {
538 r_scale = prop->getDoubleValue();
540 } else if ( cname == "output" ) {
542 SGPropertyNode *prop;
543 while ( (prop = child->getChild("prop", i)) != NULL ) {
544 SGPropertyNode *tmp = fgGetNode( prop->getStringValue(), true );
545 output_list.push_back( tmp );
548 } else if ( cname == "config" ) {
549 SGPropertyNode *prop;
551 prop = child->getChild( "Kp" );
552 if ( prop != NULL ) {
553 SGPropertyNode *val = prop->getChild( "value" );
555 Kp = val->getDoubleValue();
558 SGPropertyNode *prop1 = prop->getChild( "prop" );
559 if ( prop1 != NULL ) {
560 Kp_prop = fgGetNode( prop1->getStringValue(), true );
562 Kp_prop->setDoubleValue(Kp);
566 // output deprecated usage warning
567 if (val == NULL && prop1 == NULL) {
568 Kp = prop->getDoubleValue();
569 SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated Kp config. Please use <prop> and/or <value> tags." );
574 prop = child->getChild( "Ki" );
575 if ( prop != NULL ) {
576 Ki = prop->getDoubleValue();
580 prop = child->getChild( "u_min" );
581 if ( prop != NULL ) {
582 SGPropertyNode *val = prop->getChild( "value" );
584 u_min = val->getDoubleValue();
587 SGPropertyNode *prop1 = prop->getChild( "prop" );
588 if ( prop1 != NULL ) {
589 umin_prop = fgGetNode( prop1->getStringValue(), true );
591 umin_prop->setDoubleValue(u_min);
595 // output deprecated usage warning
596 if (val == NULL && prop1 == NULL) {
597 u_min = prop->getDoubleValue();
598 SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated u_min config. Please use <prop> and/or <value> tags." );
603 prop = child->getChild( "u_max" );
604 if ( prop != NULL ) {
605 SGPropertyNode *val = prop->getChild( "value" );
607 u_max = val->getDoubleValue();
610 SGPropertyNode *prop1 = prop->getChild( "prop" );
611 if ( prop1 != NULL ) {
612 umax_prop = fgGetNode( prop1->getStringValue(), true );
614 umax_prop->setDoubleValue(u_max);
618 // output deprecated usage warning
619 if (val == NULL && prop1 == NULL) {
620 u_max = prop->getDoubleValue();
621 SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated u_max config. Please use <prop> and/or <value> tags." );
626 SG_LOG( SG_AUTOPILOT, SG_WARN, "Error in autopilot config logic" );
627 if ( name.length() ) {
628 SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
635 void FGPISimpleController::update( double dt ) {
636 if (umin_prop != NULL)u_min = umin_prop->getDoubleValue();
637 if (umax_prop != NULL)u_max = umax_prop->getDoubleValue();
638 if (Kp_prop != NULL)Kp = Kp_prop->getDoubleValue();
640 if (enable_prop != NULL && enable_prop->getStringValue() == enable_value) {
642 // we have just been enabled, zero out int_sum
651 if ( debug ) cout << "Updating " << name << endl;
653 if ( input_prop != NULL ) {
654 input = input_prop->getDoubleValue() * y_scale;
658 if ( r_n_prop != NULL ) {
659 r_n = r_n_prop->getDoubleValue() * r_scale;
664 double error = r_n - input;
665 if ( debug ) cout << "input = " << input
666 << " reference = " << r_n
667 << " error = " << error
670 double prop_comp = 0.0;
672 if ( offset_prop != NULL ) {
673 offset = offset_prop->getDoubleValue();
674 if ( debug ) cout << "offset = " << offset << endl;
676 offset = offset_value;
679 if ( proportional ) {
680 prop_comp = error * Kp + offset;
684 int_sum += error * Ki * dt;
689 if ( debug ) cout << "prop_comp = " << prop_comp
690 << " int_sum = " << int_sum << endl;
692 double output = prop_comp + int_sum;
695 if ( output < u_min ) {
698 if ( output > u_max ) {
702 if ( debug ) cout << "output = " << output << endl;
705 for ( i = 0; i < output_list.size(); ++i ) {
706 output_list[i]->setDoubleValue( output );
712 FGPredictor::FGPredictor ( SGPropertyNode *node ):
713 last_value ( 999999999.9 ),
721 for ( i = 0; i < node->nChildren(); ++i ) {
722 SGPropertyNode *child = node->getChild(i);
723 string cname = child->getName();
724 string cval = child->getStringValue();
725 if ( cname == "name" ) {
727 } else if ( cname == "debug" ) {
728 debug = child->getBoolValue();
729 } else if ( cname == "input" ) {
730 input_prop = fgGetNode( child->getStringValue(), true );
731 } else if ( cname == "seconds" ) {
732 seconds = child->getDoubleValue();
733 } else if ( cname == "filter-gain" ) {
734 filter_gain = child->getDoubleValue();
735 } else if ( cname == "output" ) {
736 SGPropertyNode *tmp = fgGetNode( child->getStringValue(), true );
737 output_list.push_back( tmp );
742 void FGPredictor::update( double dt ) {
744 Simple moving average filter converts input value to predicted value "seconds".
746 Smoothing as described by Curt Olson:
747 gain would be valid in the range of 0 - 1.0
748 1.0 would mean no filtering.
749 0.0 would mean no input.
750 0.5 would mean (1 part past value + 1 part current value) / 2
751 0.1 would mean (9 parts past value + 1 part current value) / 10
752 0.25 would mean (3 parts past value + 1 part current value) / 4
756 if ( input_prop != NULL ) {
757 ivalue = input_prop->getDoubleValue();
758 // no sense if there isn't an input :-)
766 // first time initialize average
767 if (last_value >= 999999999.0) {
772 double current = (ivalue - last_value)/dt; // calculate current error change (per second)
774 average = (1.0 - dt) * average + current * dt;
779 // calculate output with filter gain adjustment
780 double output = ivalue + (1.0 - filter_gain) * (average * seconds) + filter_gain * (current * seconds);
783 for ( i = 0; i < output_list.size(); ++i ) {
784 output_list[i]->setDoubleValue( output );
792 FGDigitalFilter::FGDigitalFilter(SGPropertyNode *node):
798 output_min_clamp( -std::numeric_limits<double>::max() ),
799 output_max_clamp( std::numeric_limits<double>::max() )
802 for ( i = 0; i < node->nChildren(); ++i ) {
803 SGPropertyNode *child = node->getChild(i);
804 string cname = child->getName();
805 string cval = child->getStringValue();
806 if ( cname == "name" ) {
808 } else if ( cname == "debug" ) {
809 debug = child->getBoolValue();
810 } else if ( cname == "enable" ) {
811 SGPropertyNode *prop = child->getChild( "prop" );
812 if ( prop != NULL ) {
813 enable_prop = fgGetNode( prop->getStringValue(), true );
815 SGPropertyNode *val = child->getChild( "value" );
817 enable_value = val->getStringValue();
819 SGPropertyNode *pass = child->getChild( "honor-passive" );
820 if ( pass != NULL ) {
821 honor_passive = pass->getBoolValue();
823 } else if ( cname == "type" ) {
824 if ( cval == "exponential" ) {
825 filterType = exponential;
826 } else if (cval == "double-exponential") {
827 filterType = doubleExponential;
828 } else if (cval == "moving-average") {
829 filterType = movingAverage;
830 } else if (cval == "noise-spike") {
831 filterType = noiseSpike;
832 } else if (cval == "gain") {
834 } else if (cval == "reciprocal") {
835 filterType = reciprocal;
837 } else if ( cname == "input" ) {
838 input_prop = fgGetNode( child->getStringValue(), true );
839 } else if ( cname == "filter-time" ) {
840 Tf = child->getDoubleValue();
841 } else if ( cname == "samples" ) {
842 samples = child->getIntValue();
843 } else if ( cname == "max-rate-of-change" ) {
844 rateOfChange = child->getDoubleValue();
845 } else if ( cname == "gain" ) {
846 SGPropertyNode *val = child->getChild( "value" );
848 gainFactor = val->getDoubleValue();
850 SGPropertyNode *prop = child->getChild( "prop" );
851 if ( prop != NULL ) {
852 gain_prop = fgGetNode( prop->getStringValue(), true );
853 gain_prop->setDoubleValue(gainFactor);
855 } else if ( cname == "u_min" ) {
856 output_min_clamp = child->getDoubleValue();
857 } else if ( cname == "u_max" ) {
858 output_max_clamp = child->getDoubleValue();
859 } else if ( cname == "output" ) {
860 SGPropertyNode *tmp = fgGetNode( child->getStringValue(), true );
861 output_list.push_back( tmp );
865 output.resize(2, 0.0);
866 input.resize(samples + 1, 0.0);
869 void FGDigitalFilter::update(double dt)
871 if ( (input_prop != NULL &&
872 enable_prop != NULL &&
873 enable_prop->getStringValue() == enable_value) ||
874 (enable_prop == NULL &&
875 input_prop != NULL) ) {
877 input.push_front(input_prop->getDoubleValue());
878 input.resize(samples + 1, 0.0);
881 // first time being enabled, initialize output to the
882 // value of the output property to avoid bumping.
883 output.push_front(output_list[0]->getDoubleValue());
891 if ( enabled && dt > 0.0 ) {
895 * Output[n] = alpha*Input[n] + (1-alpha)*Output[n-1]
899 if (filterType == exponential)
901 double alpha = 1 / ((Tf/dt) + 1);
902 output.push_front(alpha * input[0] +
903 (1 - alpha) * output[0]);
905 else if (filterType == doubleExponential)
907 double alpha = 1 / ((Tf/dt) + 1);
908 output.push_front(alpha * alpha * input[0] +
909 2 * (1 - alpha) * output[0] -
910 (1 - alpha) * (1 - alpha) * output[1]);
912 else if (filterType == movingAverage)
914 output.push_front(output[0] +
915 (input[0] - input.back()) / samples);
917 else if (filterType == noiseSpike)
919 double maxChange = rateOfChange * dt;
921 if ((output[0] - input[0]) > maxChange)
923 output.push_front(output[0] - maxChange);
925 else if ((output[0] - input[0]) < -maxChange)
927 output.push_front(output[0] + maxChange);
929 else if (fabs(input[0] - output[0]) <= maxChange)
931 output.push_front(input[0]);
934 else if (filterType == gain)
936 if (gain_prop != NULL) {
937 gainFactor = gain_prop->getDoubleValue();
939 output[0] = gainFactor * input[0];
941 else if (filterType == reciprocal)
943 if (gain_prop != NULL) {
944 gainFactor = gain_prop->getDoubleValue();
946 if (input[0] != 0.0) {
947 output[0] = gainFactor / input[0];
951 if (output[0] < output_min_clamp) {
952 output[0] = output_min_clamp;
954 else if (output[0] > output_max_clamp) {
955 output[0] = output_max_clamp;
959 for ( i = 0; i < output_list.size(); ++i ) {
960 output_list[i]->setDoubleValue( output[0] );
966 cout << "input:" << input[0]
967 << "\toutput:" << output[0] << endl;
973 FGXMLAutopilot::FGXMLAutopilot() {
977 FGXMLAutopilot::~FGXMLAutopilot() {
981 void FGXMLAutopilot::init() {
982 config_props = fgGetNode( "/autopilot/new-config", true );
984 SGPropertyNode *path_n = fgGetNode("/sim/systems/autopilot/path");
987 SGPath config( globals->get_fg_root() );
988 config.append( path_n->getStringValue() );
990 SG_LOG( SG_ALL, SG_INFO, "Reading autopilot configuration from "
993 readProperties( config.str(), config_props );
996 SG_LOG( SG_ALL, SG_ALERT,
997 "Detected an internal inconsistency in the autopilot");
998 SG_LOG( SG_ALL, SG_ALERT,
999 " configuration. See earlier errors for" );
1000 SG_LOG( SG_ALL, SG_ALERT,
1004 } catch (const sg_exception& exc) {
1005 SG_LOG( SG_ALL, SG_ALERT, "Failed to load autopilot configuration: "
1010 SG_LOG( SG_ALL, SG_WARN,
1011 "No autopilot configuration specified for this model!");
1016 void FGXMLAutopilot::reinit() {
1022 void FGXMLAutopilot::bind() {
1025 void FGXMLAutopilot::unbind() {
1028 bool FGXMLAutopilot::build() {
1029 SGPropertyNode *node;
1032 int count = config_props->nChildren();
1033 for ( i = 0; i < count; ++i ) {
1034 node = config_props->getChild(i);
1035 string name = node->getName();
1036 // cout << name << endl;
1037 if ( name == "pid-controller" ) {
1038 components.push_back( new FGPIDController( node ) );
1039 } else if ( name == "pi-simple-controller" ) {
1040 components.push_back( new FGPISimpleController( node ) );
1041 } else if ( name == "predict-simple" ) {
1042 components.push_back( new FGPredictor( node ) );
1043 } else if ( name == "filter" ) {
1044 components.push_back( new FGDigitalFilter( node ) );
1046 SG_LOG( SG_ALL, SG_ALERT, "Unknown top level section: "
1057 * Update helper values
1059 static void update_helper( double dt ) {
1060 // Estimate speed in 5,10 seconds
1061 static SGPropertyNode *vel = fgGetNode( "/velocities/airspeed-kt", true );
1062 static SGPropertyNode *lookahead5
1063 = fgGetNode( "/autopilot/internal/lookahead-5-sec-airspeed-kt", true );
1064 static SGPropertyNode *lookahead10
1065 = fgGetNode( "/autopilot/internal/lookahead-10-sec-airspeed-kt", true );
1067 static double average = 0.0; // average/filtered prediction
1068 static double v_last = 0.0; // last velocity
1070 double v = vel->getDoubleValue();
1073 a = (v - v_last) / dt;
1076 average = (1.0 - dt) * average + dt * a;
1081 lookahead5->setDoubleValue( v + average * 5.0 );
1082 lookahead10->setDoubleValue( v + average * 10.0 );
1086 // Calculate heading bug error normalized to +/- 180.0 (based on
1087 // DG indicated heading)
1088 static SGPropertyNode *bug
1089 = fgGetNode( "/autopilot/settings/heading-bug-deg", true );
1090 static SGPropertyNode *ind_hdg
1091 = fgGetNode( "/instrumentation/heading-indicator/indicated-heading-deg",
1093 static SGPropertyNode *ind_bug_error
1094 = fgGetNode( "/autopilot/internal/heading-bug-error-deg", true );
1096 double diff = bug->getDoubleValue() - ind_hdg->getDoubleValue();
1097 if ( diff < -180.0 ) { diff += 360.0; }
1098 if ( diff > 180.0 ) { diff -= 360.0; }
1099 ind_bug_error->setDoubleValue( diff );
1101 // Calculate heading bug error normalized to +/- 180.0 (based on
1102 // actual/nodrift magnetic-heading, i.e. a DG slaved to magnetic
1104 static SGPropertyNode *mag_hdg
1105 = fgGetNode( "/orientation/heading-magnetic-deg", true );
1106 static SGPropertyNode *fdm_bug_error
1107 = fgGetNode( "/autopilot/internal/fdm-heading-bug-error-deg", true );
1109 diff = bug->getDoubleValue() - mag_hdg->getDoubleValue();
1110 if ( diff < -180.0 ) { diff += 360.0; }
1111 if ( diff > 180.0 ) { diff -= 360.0; }
1112 fdm_bug_error->setDoubleValue( diff );
1114 // Calculate true heading error normalized to +/- 180.0
1115 static SGPropertyNode *target_true
1116 = fgGetNode( "/autopilot/settings/true-heading-deg", true );
1117 static SGPropertyNode *true_hdg
1118 = fgGetNode( "/orientation/heading-deg", true );
1119 static SGPropertyNode *true_track
1120 = fgGetNode( "/instrumentation/gps/indicated-track-true-deg", true );
1121 static SGPropertyNode *true_error
1122 = fgGetNode( "/autopilot/internal/true-heading-error-deg", true );
1124 diff = target_true->getDoubleValue() - true_hdg->getDoubleValue();
1125 if ( diff < -180.0 ) { diff += 360.0; }
1126 if ( diff > 180.0 ) { diff -= 360.0; }
1127 true_error->setDoubleValue( diff );
1129 // Calculate nav1 target heading error normalized to +/- 180.0
1130 static SGPropertyNode *target_nav1
1131 = fgGetNode( "/instrumentation/nav[0]/radials/target-auto-hdg-deg", true );
1132 static SGPropertyNode *true_nav1
1133 = fgGetNode( "/autopilot/internal/nav1-heading-error-deg", true );
1134 static SGPropertyNode *true_track_nav1
1135 = fgGetNode( "/autopilot/internal/nav1-track-error-deg", true );
1137 diff = target_nav1->getDoubleValue() - true_hdg->getDoubleValue();
1138 if ( diff < -180.0 ) { diff += 360.0; }
1139 if ( diff > 180.0 ) { diff -= 360.0; }
1140 true_nav1->setDoubleValue( diff );
1142 diff = target_nav1->getDoubleValue() - true_track->getDoubleValue();
1143 if ( diff < -180.0 ) { diff += 360.0; }
1144 if ( diff > 180.0 ) { diff -= 360.0; }
1145 true_track_nav1->setDoubleValue( diff );
1147 // Calculate nav1 selected course error normalized to +/- 180.0
1148 // (based on DG indicated heading)
1149 static SGPropertyNode *nav1_course_error
1150 = fgGetNode( "/autopilot/internal/nav1-course-error", true );
1151 static SGPropertyNode *nav1_selected_course
1152 = fgGetNode( "/instrumentation/nav[0]/radials/selected-deg", true );
1154 diff = nav1_selected_course->getDoubleValue() - ind_hdg->getDoubleValue();
1155 // if ( diff < -180.0 ) { diff += 360.0; }
1156 // if ( diff > 180.0 ) { diff -= 360.0; }
1157 SG_NORMALIZE_RANGE( diff, -180.0, 180.0 );
1158 nav1_course_error->setDoubleValue( diff );
1160 // Calculate vertical speed in fpm
1161 static SGPropertyNode *vs_fps
1162 = fgGetNode( "/velocities/vertical-speed-fps", true );
1163 static SGPropertyNode *vs_fpm
1164 = fgGetNode( "/autopilot/internal/vert-speed-fpm", true );
1166 vs_fpm->setDoubleValue( vs_fps->getDoubleValue() * 60.0 );
1169 // Calculate static port pressure rate in [inhg/s].
1170 // Used to determine vertical speed.
1171 static SGPropertyNode *static_pressure
1172 = fgGetNode( "/systems/static[0]/pressure-inhg", true );
1173 static SGPropertyNode *pressure_rate
1174 = fgGetNode( "/autopilot/internal/pressure-rate", true );
1176 static double last_static_pressure = 0.0;
1179 double current_static_pressure = static_pressure->getDoubleValue();
1181 double current_pressure_rate =
1182 ( current_static_pressure - last_static_pressure ) / dt;
1184 pressure_rate->setDoubleValue(current_pressure_rate);
1186 last_static_pressure = current_static_pressure;
1193 * Update the list of autopilot components
1196 void FGXMLAutopilot::update( double dt ) {
1197 update_helper( dt );
1200 for ( i = 0; i < components.size(); ++i ) {
1201 components[i]->update( dt );