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.
29 #include <simgear/structure/exception.hxx>
30 #include <simgear/misc/sg_path.hxx>
31 #include <simgear/sg_inlines.h>
32 #include <simgear/props/props_io.hxx>
34 #include <Main/fg_props.hxx>
35 #include <Main/globals.hxx>
36 #include <Main/util.hxx>
38 #include "xmlauto.hxx"
43 static SGCondition * getCondition( SGPropertyNode * node )
45 const SGPropertyNode* conditionNode = node->getChild("condition");
46 return conditionNode ? sgReadCondition(node, conditionNode) : NULL;
49 FGPIDController::FGPIDController( SGPropertyNode *node ):
73 for ( i = 0; i < node->nChildren(); ++i ) {
74 SGPropertyNode *child = node->getChild(i);
75 string cname = child->getName();
76 string cval = child->getStringValue();
77 if ( cname == "name" ) {
79 } else if ( cname == "debug" ) {
80 debug = child->getBoolValue();
81 } else if ( cname == "enable" ) {
82 _condition = getCondition( child );
83 if( _condition == NULL ) {
84 // cout << "parsing enable" << endl;
85 SGPropertyNode *prop = child->getChild( "prop" );
87 // cout << "prop = " << prop->getStringValue() << endl;
88 enable_prop = fgGetNode( prop->getStringValue(), true );
90 // cout << "no prop child" << endl;
92 SGPropertyNode *val = child->getChild( "value" );
94 enable_value = val->getStringValue();
97 SGPropertyNode *pass = child->getChild( "honor-passive" );
99 honor_passive = pass->getBoolValue();
101 } else if ( cname == "input" ) {
102 SGPropertyNode *prop = child->getChild( "prop" );
103 if ( prop != NULL ) {
104 input_prop = fgGetNode( prop->getStringValue(), true );
106 prop = child->getChild( "scale" );
107 if ( prop != NULL ) {
108 y_scale = prop->getDoubleValue();
110 prop = child->getChild( "offset" );
111 if ( prop != NULL ) {
112 y_offset = prop->getDoubleValue();
114 } else if ( cname == "reference" ) {
115 SGPropertyNode *prop = child->getChild( "prop" );
116 if ( prop != NULL ) {
117 r_n_prop = fgGetNode( prop->getStringValue(), true );
119 prop = child->getChild( "value" );
120 if ( prop != NULL ) {
121 r_n_value = prop->getDoubleValue();
124 prop = child->getChild( "scale" );
125 if ( prop != NULL ) {
126 r_scale = prop->getDoubleValue();
128 prop = child->getChild( "offset" );
129 if ( prop != NULL ) {
130 r_offset = prop->getDoubleValue();
132 } else if ( cname == "output" ) {
134 SGPropertyNode *prop;
135 while ( (prop = child->getChild("prop", i)) != NULL ) {
136 SGPropertyNode *tmp = fgGetNode( prop->getStringValue(), true );
137 output_list.push_back( tmp );
140 } else if ( cname == "config" ) {
141 SGPropertyNode *config;
143 config = child->getChild( "Ts" );
144 if ( config != NULL ) {
145 desiredTs = config->getDoubleValue();
148 config = child->getChild( "Kp" );
149 if ( config != NULL ) {
150 SGPropertyNode *val = config->getChild( "value" );
152 Kp = val->getDoubleValue();
155 SGPropertyNode *prop = config->getChild( "prop" );
156 if ( prop != NULL ) {
157 Kp_prop = fgGetNode( prop->getStringValue(), true );
159 Kp_prop->setDoubleValue(Kp);
163 // output deprecated usage warning
164 if (val == NULL && prop == NULL) {
165 Kp = config->getDoubleValue();
166 SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated Kp config. Please use <prop> and/or <value> tags." );
167 if ( name.length() ) {
168 SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
173 config = child->getChild( "beta" );
174 if ( config != NULL ) {
175 beta = config->getDoubleValue();
178 config = child->getChild( "alpha" );
179 if ( config != NULL ) {
180 alpha = config->getDoubleValue();
183 config = child->getChild( "gamma" );
184 if ( config != NULL ) {
185 gamma = config->getDoubleValue();
188 config = child->getChild( "Ti" );
189 if ( config != NULL ) {
190 SGPropertyNode *val = config->getChild( "value" );
192 Ti = val->getDoubleValue();
195 SGPropertyNode *prop = config->getChild( "prop" );
196 if ( prop != NULL ) {
197 Ti_prop = fgGetNode( prop->getStringValue(), true );
199 Ti_prop->setDoubleValue(Kp);
203 // output deprecated usage warning
204 if (val == NULL && prop == NULL) {
205 Ti = config->getDoubleValue();
206 SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated Ti config. Please use <prop> and/or <value> tags." );
207 if ( name.length() ) {
208 SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
213 config = child->getChild( "Td" );
214 if ( config != NULL ) {
215 SGPropertyNode *val = config->getChild( "value" );
217 Td = val->getDoubleValue();
220 SGPropertyNode *prop = config->getChild( "prop" );
221 if ( prop != NULL ) {
222 Td_prop = fgGetNode( prop->getStringValue(), true );
224 Td_prop->setDoubleValue(Kp);
228 // output deprecated usage warning
229 if (val == NULL && prop == NULL) {
230 Td = config->getDoubleValue();
231 SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated Td config. Please use <prop> and/or <value> tags." );
232 if ( name.length() ) {
233 SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
238 config = child->getChild( "u_min" );
239 if ( config != NULL ) {
240 SGPropertyNode *val = config->getChild( "value" );
242 u_min = val->getDoubleValue();
245 SGPropertyNode *prop = config->getChild( "prop" );
246 if ( prop != NULL ) {
247 umin_prop = fgGetNode( prop->getStringValue(), true );
249 umin_prop->setDoubleValue(u_min);
253 // output deprecated usage warning
254 if (val == NULL && prop == NULL) {
255 u_min = config->getDoubleValue();
256 SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated u_min config. Please use <prop> and/or <value> tags." );
257 if ( name.length() ) {
258 SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
263 config = child->getChild( "u_max" );
264 if ( config != NULL ) {
265 SGPropertyNode *val = config->getChild( "value" );
267 u_max = val->getDoubleValue();
270 SGPropertyNode *prop = config->getChild( "prop" );
271 if ( prop != NULL ) {
272 umax_prop = fgGetNode( prop->getStringValue(), true );
274 umax_prop->setDoubleValue(u_max);
278 // output deprecated usage warning
279 if (val == NULL && prop == NULL) {
280 u_max = config->getDoubleValue();
281 SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated u_max config. Please use <prop> and/or <value> tags." );
282 if ( name.length() ) {
283 SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
288 SG_LOG( SG_AUTOPILOT, SG_WARN, "Error in autopilot config logic" );
289 if ( name.length() ) {
290 SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
300 * Ok! Here is the PID controller algorithm that I would like to see
303 * delta_u_n = Kp * [ (ep_n - ep_n-1) + ((Ts/Ti)*e_n)
304 * + (Td/Ts)*(edf_n - 2*edf_n-1 + edf_n-2) ]
306 * u_n = u_n-1 + delta_u_n
310 * delta_u : The incremental output
311 * Kp : Proportional gain
312 * ep : Proportional error with reference weighing
315 * beta : Weighing factor
316 * r : Reference (setpoint)
317 * y : Process value, measured
320 * Ts : Sampling interval
321 * Ti : Integrator time
322 * Td : Derivator time
323 * edf : Derivate error with reference weighing and filtering
324 * edf_n = edf_n-1 / ((Ts/Tf) + 1) + ed_n * (Ts/Tf) / ((Ts/Tf) + 1)
327 * Tf = alpha * Td , where alpha usually is set to 0.1
328 * ed : Unfiltered derivate error with reference weighing
331 * gamma : Weighing factor
333 * u : absolute output
335 * Index n means the n'th value.
340 * y_n , r_n , beta=1 , gamma=0 , alpha=0.1 ,
341 * Kp , Ti , Td , Ts (is the sampling time available?)
348 void FGPIDController::update( double dt ) {
349 double ep_n; // proportional error with reference weighing
351 double ed_n; // derivative error
352 double edf_n; // derivative error filter
353 double Tf; // filter time
354 double delta_u_n = 0.0; // incremental output
355 double u_n = 0.0; // absolute output
356 double Ts; // sampling interval (sec)
357 if (umin_prop != NULL)u_min = umin_prop->getDoubleValue();
358 if (umax_prop != NULL)u_max = umax_prop->getDoubleValue();
359 if (Ti_prop != NULL)Ti = Ti_prop->getDoubleValue();
360 if (Td_prop != NULL)Td = Td_prop->getDoubleValue();
363 if ( elapsedTime <= desiredTs ) {
364 // do nothing if time step is not positive (i.e. no time has
371 if (( _condition && _condition->test() ) ||
372 (enable_prop != NULL && enable_prop->getStringValue() == enable_value)) {
374 // first time being enabled, seed u_n with current
375 // property tree value
376 u_n = output_list[0]->getDoubleValue();
378 if ( u_n < u_min ) { u_n = u_min; }
379 if ( u_n > u_max ) { u_n = u_max; }
387 if ( enabled && Ts > 0.0) {
388 if ( debug ) cout << "Updating " << name
389 << " Ts " << Ts << endl;
392 if ( input_prop != NULL ) {
393 y_n = input_prop->getDoubleValue() * y_scale + y_offset;
397 if ( r_n_prop != NULL ) {
398 r_n = r_n_prop->getDoubleValue() * r_scale + r_offset;
403 if ( debug ) cout << " input = " << y_n << " ref = " << r_n << endl;
405 // Calculates proportional error:
406 ep_n = beta * r_n - y_n;
407 if ( debug ) cout << " ep_n = " << ep_n;
408 if ( debug ) cout << " ep_n_1 = " << ep_n_1;
412 if ( debug ) cout << " e_n = " << e_n;
414 // Calculates derivate error:
415 ed_n = gamma * r_n - y_n;
416 if ( debug ) cout << " ed_n = " << ed_n;
419 // Calculates filter time:
421 if ( debug ) cout << " Tf = " << Tf;
423 // Filters the derivate error:
424 edf_n = edf_n_1 / (Ts/Tf + 1)
425 + ed_n * (Ts/Tf) / (Ts/Tf + 1);
426 if ( debug ) cout << " edf_n = " << edf_n;
431 // Calculates the incremental output:
433 if (Kp_prop != NULL) {
434 Kp = Kp_prop->getDoubleValue();
436 delta_u_n = Kp * ( (ep_n - ep_n_1)
438 + ((Td/Ts) * (edf_n - 2*edf_n_1 + edf_n_2)) );
442 cout << " delta_u_n = " << delta_u_n << endl;
443 cout << "P:" << Kp * (ep_n - ep_n_1)
444 << " I:" << Kp * ((Ts/Ti) * e_n)
445 << " D:" << Kp * ((Td/Ts) * (edf_n - 2*edf_n_1 + edf_n_2))
449 // Integrator anti-windup logic:
450 if ( delta_u_n > (u_max - u_n_1) ) {
451 delta_u_n = u_max - u_n_1;
452 if ( debug ) cout << " max saturation " << endl;
453 } else if ( delta_u_n < (u_min - u_n_1) ) {
454 delta_u_n = u_min - u_n_1;
455 if ( debug ) cout << " min saturation " << endl;
458 // Calculates absolute output:
459 u_n = u_n_1 + delta_u_n;
460 if ( debug ) cout << " output = " << u_n << endl;
462 // Updates indexed values;
468 // passive_ignore == true means that we go through all the
469 // motions, but drive the outputs. This is analogous to
470 // running the autopilot with the "servos" off. This is
471 // helpful for things like flight directors which position
472 // their vbars from the autopilot computations.
473 if ( passive_mode->getBoolValue() && honor_passive ) {
477 for ( i = 0; i < output_list.size(); ++i ) {
478 output_list[i]->setDoubleValue( u_n );
481 } else if ( !enabled ) {
484 // Updates indexed values;
493 FGPISimpleController::FGPISimpleController( SGPropertyNode *node ):
494 proportional( false ),
511 for ( i = 0; i < node->nChildren(); ++i ) {
512 SGPropertyNode *child = node->getChild(i);
513 string cname = child->getName();
514 string cval = child->getStringValue();
515 if ( cname == "name" ) {
517 } else if ( cname == "debug" ) {
518 debug = child->getBoolValue();
519 } else if ( cname == "enable" ) {
520 _condition = getCondition( child );
521 if( _condition == NULL ) {
522 // cout << "parsing enable" << endl;
523 SGPropertyNode *prop = child->getChild( "prop" );
524 if ( prop != NULL ) {
525 // cout << "prop = " << prop->getStringValue() << endl;
526 enable_prop = fgGetNode( prop->getStringValue(), true );
528 // cout << "no prop child" << endl;
530 SGPropertyNode *val = child->getChild( "value" );
532 enable_value = val->getStringValue();
535 } else if ( cname == "input" ) {
536 SGPropertyNode *prop = child->getChild( "prop" );
537 if ( prop != NULL ) {
538 input_prop = fgGetNode( prop->getStringValue(), true );
540 prop = child->getChild( "scale" );
541 if ( prop != NULL ) {
542 y_scale = prop->getDoubleValue();
544 } else if ( cname == "reference" ) {
545 SGPropertyNode *prop = child->getChild( "prop" );
546 if ( prop != NULL ) {
547 r_n_prop = fgGetNode( prop->getStringValue(), true );
549 prop = child->getChild( "value" );
550 if ( prop != NULL ) {
551 r_n_value = prop->getDoubleValue();
554 prop = child->getChild( "scale" );
555 if ( prop != NULL ) {
556 r_scale = prop->getDoubleValue();
558 } else if ( cname == "output" ) {
560 SGPropertyNode *prop;
561 while ( (prop = child->getChild("prop", i)) != NULL ) {
562 SGPropertyNode *tmp = fgGetNode( prop->getStringValue(), true );
563 output_list.push_back( tmp );
566 } else if ( cname == "config" ) {
567 SGPropertyNode *prop;
569 prop = child->getChild( "Kp" );
570 if ( prop != NULL ) {
571 SGPropertyNode *val = prop->getChild( "value" );
573 Kp = val->getDoubleValue();
576 SGPropertyNode *prop1 = prop->getChild( "prop" );
577 if ( prop1 != NULL ) {
578 Kp_prop = fgGetNode( prop1->getStringValue(), true );
580 Kp_prop->setDoubleValue(Kp);
584 // output deprecated usage warning
585 if (val == NULL && prop1 == NULL) {
586 Kp = prop->getDoubleValue();
587 SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated Kp config. Please use <prop> and/or <value> tags." );
592 prop = child->getChild( "Ki" );
593 if ( prop != NULL ) {
594 Ki = prop->getDoubleValue();
598 prop = child->getChild( "u_min" );
599 if ( prop != NULL ) {
600 SGPropertyNode *val = prop->getChild( "value" );
602 u_min = val->getDoubleValue();
605 SGPropertyNode *prop1 = prop->getChild( "prop" );
606 if ( prop1 != NULL ) {
607 umin_prop = fgGetNode( prop1->getStringValue(), true );
609 umin_prop->setDoubleValue(u_min);
613 // output deprecated usage warning
614 if (val == NULL && prop1 == NULL) {
615 u_min = prop->getDoubleValue();
616 SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated u_min config. Please use <prop> and/or <value> tags." );
621 prop = child->getChild( "u_max" );
622 if ( prop != NULL ) {
623 SGPropertyNode *val = prop->getChild( "value" );
625 u_max = val->getDoubleValue();
628 SGPropertyNode *prop1 = prop->getChild( "prop" );
629 if ( prop1 != NULL ) {
630 umax_prop = fgGetNode( prop1->getStringValue(), true );
632 umax_prop->setDoubleValue(u_max);
636 // output deprecated usage warning
637 if (val == NULL && prop1 == NULL) {
638 u_max = prop->getDoubleValue();
639 SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated u_max config. Please use <prop> and/or <value> tags." );
644 SG_LOG( SG_AUTOPILOT, SG_WARN, "Error in autopilot config logic" );
645 if ( name.length() ) {
646 SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
653 void FGPISimpleController::update( double dt ) {
654 if (umin_prop != NULL)u_min = umin_prop->getDoubleValue();
655 if (umax_prop != NULL)u_max = umax_prop->getDoubleValue();
656 if (Kp_prop != NULL)Kp = Kp_prop->getDoubleValue();
658 if (( _condition && _condition->test() ) ||
659 (enable_prop != NULL && enable_prop->getStringValue() == enable_value)) {
661 // we have just been enabled, zero out int_sum
670 if ( debug ) cout << "Updating " << name << endl;
672 if ( input_prop != NULL ) {
673 input = input_prop->getDoubleValue() * y_scale;
677 if ( r_n_prop != NULL ) {
678 r_n = r_n_prop->getDoubleValue() * r_scale;
683 double error = r_n - input;
684 if ( debug ) cout << "input = " << input
685 << " reference = " << r_n
686 << " error = " << error
689 double prop_comp = 0.0;
691 if ( offset_prop != NULL ) {
692 offset = offset_prop->getDoubleValue();
693 if ( debug ) cout << "offset = " << offset << endl;
695 offset = offset_value;
698 if ( proportional ) {
699 prop_comp = error * Kp + offset;
703 int_sum += error * Ki * dt;
708 if ( debug ) cout << "prop_comp = " << prop_comp
709 << " int_sum = " << int_sum << endl;
711 double output = prop_comp + int_sum;
714 if ( output < u_min ) {
717 if ( output > u_max ) {
721 if ( debug ) cout << "output = " << output << endl;
724 for ( i = 0; i < output_list.size(); ++i ) {
725 output_list[i]->setDoubleValue( output );
731 FGPredictor::FGPredictor ( SGPropertyNode *node ):
732 last_value ( 999999999.9 ),
740 for ( i = 0; i < node->nChildren(); ++i ) {
741 SGPropertyNode *child = node->getChild(i);
742 string cname = child->getName();
743 string cval = child->getStringValue();
744 if ( cname == "name" ) {
746 } else if ( cname == "debug" ) {
747 debug = child->getBoolValue();
748 } else if ( cname == "input" ) {
749 input_prop = fgGetNode( child->getStringValue(), true );
750 } else if ( cname == "seconds" ) {
751 seconds = child->getDoubleValue();
752 } else if ( cname == "filter-gain" ) {
753 filter_gain = child->getDoubleValue();
754 } else if ( cname == "output" ) {
755 SGPropertyNode *tmp = fgGetNode( child->getStringValue(), true );
756 output_list.push_back( tmp );
761 void FGPredictor::update( double dt ) {
763 Simple moving average filter converts input value to predicted value "seconds".
765 Smoothing as described by Curt Olson:
766 gain would be valid in the range of 0 - 1.0
767 1.0 would mean no filtering.
768 0.0 would mean no input.
769 0.5 would mean (1 part past value + 1 part current value) / 2
770 0.1 would mean (9 parts past value + 1 part current value) / 10
771 0.25 would mean (3 parts past value + 1 part current value) / 4
775 if ( input_prop != NULL ) {
776 ivalue = input_prop->getDoubleValue();
777 // no sense if there isn't an input :-)
785 // first time initialize average
786 if (last_value >= 999999999.0) {
791 double current = (ivalue - last_value)/dt; // calculate current error change (per second)
793 average = (1.0 - dt) * average + current * dt;
798 // calculate output with filter gain adjustment
799 double output = ivalue + (1.0 - filter_gain) * (average * seconds) + filter_gain * (current * seconds);
802 for ( i = 0; i < output_list.size(); ++i ) {
803 output_list[i]->setDoubleValue( output );
811 FGDigitalFilter::FGDigitalFilter(SGPropertyNode *node):
817 output_min_clamp( -std::numeric_limits<double>::max() ),
818 output_max_clamp( std::numeric_limits<double>::max() )
821 for ( i = 0; i < node->nChildren(); ++i ) {
822 SGPropertyNode *child = node->getChild(i);
823 string cname = child->getName();
824 string cval = child->getStringValue();
825 if ( cname == "name" ) {
827 } else if ( cname == "debug" ) {
828 debug = child->getBoolValue();
829 } else if ( cname == "enable" ) {
830 _condition = getCondition( child );
831 if( _condition == NULL ) {
832 SGPropertyNode *prop = child->getChild( "prop" );
833 if ( prop != NULL ) {
834 enable_prop = fgGetNode( prop->getStringValue(), true );
836 SGPropertyNode *val = child->getChild( "value" );
838 enable_value = val->getStringValue();
841 SGPropertyNode *pass = child->getChild( "honor-passive" );
842 if ( pass != NULL ) {
843 honor_passive = pass->getBoolValue();
845 } else if ( cname == "type" ) {
846 if ( cval == "exponential" ) {
847 filterType = exponential;
848 } else if (cval == "double-exponential") {
849 filterType = doubleExponential;
850 } else if (cval == "moving-average") {
851 filterType = movingAverage;
852 } else if (cval == "noise-spike") {
853 filterType = noiseSpike;
854 } else if (cval == "gain") {
856 } else if (cval == "reciprocal") {
857 filterType = reciprocal;
859 } else if ( cname == "input" ) {
860 input_prop = fgGetNode( child->getStringValue(), true );
861 } else if ( cname == "filter-time" ) {
862 Tf = child->getDoubleValue();
863 } else if ( cname == "samples" ) {
864 samples = child->getIntValue();
865 } else if ( cname == "max-rate-of-change" ) {
866 rateOfChange = child->getDoubleValue();
867 } else if ( cname == "gain" ) {
868 SGPropertyNode *val = child->getChild( "value" );
870 gainFactor = val->getDoubleValue();
872 SGPropertyNode *prop = child->getChild( "prop" );
873 if ( prop != NULL ) {
874 gain_prop = fgGetNode( prop->getStringValue(), true );
875 gain_prop->setDoubleValue(gainFactor);
877 } else if ( cname == "u_min" ) {
878 output_min_clamp = child->getDoubleValue();
879 } else if ( cname == "u_max" ) {
880 output_max_clamp = child->getDoubleValue();
881 } else if ( cname == "output" ) {
882 SGPropertyNode *tmp = fgGetNode( child->getStringValue(), true );
883 output_list.push_back( tmp );
887 output.resize(2, 0.0);
888 input.resize(samples + 1, 0.0);
891 void FGDigitalFilter::update(double dt)
893 if ( input_prop != NULL && (
894 ( _condition != NULL && _condition->test() ) ||
895 ( enable_prop != NULL &&
896 enable_prop->getStringValue() == enable_value) ||
897 (enable_prop == NULL && _condition == NULL ) ) ) {
899 input.push_front(input_prop->getDoubleValue());
900 input.resize(samples + 1, 0.0);
903 // first time being enabled, initialize output to the
904 // value of the output property to avoid bumping.
905 output.push_front(output_list[0]->getDoubleValue());
913 if ( enabled && dt > 0.0 ) {
917 * Output[n] = alpha*Input[n] + (1-alpha)*Output[n-1]
921 if (filterType == exponential)
923 double alpha = 1 / ((Tf/dt) + 1);
924 output.push_front(alpha * input[0] +
925 (1 - alpha) * output[0]);
927 else if (filterType == doubleExponential)
929 double alpha = 1 / ((Tf/dt) + 1);
930 output.push_front(alpha * alpha * input[0] +
931 2 * (1 - alpha) * output[0] -
932 (1 - alpha) * (1 - alpha) * output[1]);
934 else if (filterType == movingAverage)
936 output.push_front(output[0] +
937 (input[0] - input.back()) / samples);
939 else if (filterType == noiseSpike)
941 double maxChange = rateOfChange * dt;
943 if ((output[0] - input[0]) > maxChange)
945 output.push_front(output[0] - maxChange);
947 else if ((output[0] - input[0]) < -maxChange)
949 output.push_front(output[0] + maxChange);
951 else if (fabs(input[0] - output[0]) <= maxChange)
953 output.push_front(input[0]);
956 else if (filterType == gain)
958 if (gain_prop != NULL) {
959 gainFactor = gain_prop->getDoubleValue();
961 output[0] = gainFactor * input[0];
963 else if (filterType == reciprocal)
965 if (gain_prop != NULL) {
966 gainFactor = gain_prop->getDoubleValue();
968 if (input[0] != 0.0) {
969 output[0] = gainFactor / input[0];
973 if (output[0] < output_min_clamp) {
974 output[0] = output_min_clamp;
976 else if (output[0] > output_max_clamp) {
977 output[0] = output_max_clamp;
981 for ( i = 0; i < output_list.size(); ++i ) {
982 output_list[i]->setDoubleValue( output[0] );
988 cout << "input:" << input[0]
989 << "\toutput:" << output[0] << endl;
995 FGXMLAutopilot::FGXMLAutopilot() {
999 FGXMLAutopilot::~FGXMLAutopilot() {
1003 void FGXMLAutopilot::init() {
1004 config_props = fgGetNode( "/autopilot/new-config", true );
1006 SGPropertyNode *path_n = fgGetNode("/sim/systems/autopilot/path");
1009 SGPath config( globals->get_fg_root() );
1010 config.append( path_n->getStringValue() );
1012 SG_LOG( SG_ALL, SG_INFO, "Reading autopilot configuration from "
1015 readProperties( config.str(), config_props );
1018 SG_LOG( SG_ALL, SG_ALERT,
1019 "Detected an internal inconsistency in the autopilot");
1020 SG_LOG( SG_ALL, SG_ALERT,
1021 " configuration. See earlier errors for" );
1022 SG_LOG( SG_ALL, SG_ALERT,
1026 } catch (const sg_exception& exc) {
1027 SG_LOG( SG_ALL, SG_ALERT, "Failed to load autopilot configuration: "
1032 SG_LOG( SG_ALL, SG_WARN,
1033 "No autopilot configuration specified for this model!");
1038 void FGXMLAutopilot::reinit() {
1044 void FGXMLAutopilot::bind() {
1047 void FGXMLAutopilot::unbind() {
1050 bool FGXMLAutopilot::build() {
1051 SGPropertyNode *node;
1054 int count = config_props->nChildren();
1055 for ( i = 0; i < count; ++i ) {
1056 node = config_props->getChild(i);
1057 string name = node->getName();
1058 // cout << name << endl;
1059 if ( name == "pid-controller" ) {
1060 components.push_back( new FGPIDController( node ) );
1061 } else if ( name == "pi-simple-controller" ) {
1062 components.push_back( new FGPISimpleController( node ) );
1063 } else if ( name == "predict-simple" ) {
1064 components.push_back( new FGPredictor( node ) );
1065 } else if ( name == "filter" ) {
1066 components.push_back( new FGDigitalFilter( node ) );
1068 SG_LOG( SG_ALL, SG_ALERT, "Unknown top level section: "
1079 * Update helper values
1081 static void update_helper( double dt ) {
1082 // Estimate speed in 5,10 seconds
1083 static SGPropertyNode_ptr vel = fgGetNode( "/velocities/airspeed-kt", true );
1084 static SGPropertyNode_ptr lookahead5
1085 = fgGetNode( "/autopilot/internal/lookahead-5-sec-airspeed-kt", true );
1086 static SGPropertyNode_ptr lookahead10
1087 = fgGetNode( "/autopilot/internal/lookahead-10-sec-airspeed-kt", true );
1089 static double average = 0.0; // average/filtered prediction
1090 static double v_last = 0.0; // last velocity
1092 double v = vel->getDoubleValue();
1095 a = (v - v_last) / dt;
1098 average = (1.0 - dt) * average + dt * a;
1103 lookahead5->setDoubleValue( v + average * 5.0 );
1104 lookahead10->setDoubleValue( v + average * 10.0 );
1108 // Calculate heading bug error normalized to +/- 180.0 (based on
1109 // DG indicated heading)
1110 static SGPropertyNode_ptr bug
1111 = fgGetNode( "/autopilot/settings/heading-bug-deg", true );
1112 static SGPropertyNode_ptr ind_hdg
1113 = fgGetNode( "/instrumentation/heading-indicator/indicated-heading-deg",
1115 static SGPropertyNode_ptr ind_bug_error
1116 = fgGetNode( "/autopilot/internal/heading-bug-error-deg", true );
1118 double diff = bug->getDoubleValue() - ind_hdg->getDoubleValue();
1119 if ( diff < -180.0 ) { diff += 360.0; }
1120 if ( diff > 180.0 ) { diff -= 360.0; }
1121 ind_bug_error->setDoubleValue( diff );
1123 // Calculate heading bug error normalized to +/- 180.0 (based on
1124 // actual/nodrift magnetic-heading, i.e. a DG slaved to magnetic
1126 static SGPropertyNode_ptr mag_hdg
1127 = fgGetNode( "/orientation/heading-magnetic-deg", true );
1128 static SGPropertyNode_ptr fdm_bug_error
1129 = fgGetNode( "/autopilot/internal/fdm-heading-bug-error-deg", true );
1131 diff = bug->getDoubleValue() - mag_hdg->getDoubleValue();
1132 if ( diff < -180.0 ) { diff += 360.0; }
1133 if ( diff > 180.0 ) { diff -= 360.0; }
1134 fdm_bug_error->setDoubleValue( diff );
1136 // Calculate true heading error normalized to +/- 180.0
1137 static SGPropertyNode_ptr target_true
1138 = fgGetNode( "/autopilot/settings/true-heading-deg", true );
1139 static SGPropertyNode_ptr true_hdg
1140 = fgGetNode( "/orientation/heading-deg", true );
1141 static SGPropertyNode_ptr true_track
1142 = fgGetNode( "/instrumentation/gps/indicated-track-true-deg", true );
1143 static SGPropertyNode_ptr true_error
1144 = fgGetNode( "/autopilot/internal/true-heading-error-deg", true );
1146 diff = target_true->getDoubleValue() - true_hdg->getDoubleValue();
1147 if ( diff < -180.0 ) { diff += 360.0; }
1148 if ( diff > 180.0 ) { diff -= 360.0; }
1149 true_error->setDoubleValue( diff );
1151 // Calculate nav1 target heading error normalized to +/- 180.0
1152 static SGPropertyNode_ptr target_nav1
1153 = fgGetNode( "/instrumentation/nav[0]/radials/target-auto-hdg-deg", true );
1154 static SGPropertyNode_ptr true_nav1
1155 = fgGetNode( "/autopilot/internal/nav1-heading-error-deg", true );
1156 static SGPropertyNode_ptr true_track_nav1
1157 = fgGetNode( "/autopilot/internal/nav1-track-error-deg", true );
1159 diff = target_nav1->getDoubleValue() - true_hdg->getDoubleValue();
1160 if ( diff < -180.0 ) { diff += 360.0; }
1161 if ( diff > 180.0 ) { diff -= 360.0; }
1162 true_nav1->setDoubleValue( diff );
1164 diff = target_nav1->getDoubleValue() - true_track->getDoubleValue();
1165 if ( diff < -180.0 ) { diff += 360.0; }
1166 if ( diff > 180.0 ) { diff -= 360.0; }
1167 true_track_nav1->setDoubleValue( diff );
1169 // Calculate nav1 selected course error normalized to +/- 180.0
1170 // (based on DG indicated heading)
1171 static SGPropertyNode_ptr nav1_course_error
1172 = fgGetNode( "/autopilot/internal/nav1-course-error", true );
1173 static SGPropertyNode_ptr nav1_selected_course
1174 = fgGetNode( "/instrumentation/nav[0]/radials/selected-deg", true );
1176 diff = nav1_selected_course->getDoubleValue() - ind_hdg->getDoubleValue();
1177 // if ( diff < -180.0 ) { diff += 360.0; }
1178 // if ( diff > 180.0 ) { diff -= 360.0; }
1179 SG_NORMALIZE_RANGE( diff, -180.0, 180.0 );
1180 nav1_course_error->setDoubleValue( diff );
1182 // Calculate vertical speed in fpm
1183 static SGPropertyNode_ptr vs_fps
1184 = fgGetNode( "/velocities/vertical-speed-fps", true );
1185 static SGPropertyNode_ptr vs_fpm
1186 = fgGetNode( "/autopilot/internal/vert-speed-fpm", true );
1188 vs_fpm->setDoubleValue( vs_fps->getDoubleValue() * 60.0 );
1191 // Calculate static port pressure rate in [inhg/s].
1192 // Used to determine vertical speed.
1193 static SGPropertyNode_ptr static_pressure
1194 = fgGetNode( "/systems/static[0]/pressure-inhg", true );
1195 static SGPropertyNode_ptr pressure_rate
1196 = fgGetNode( "/autopilot/internal/pressure-rate", true );
1198 static double last_static_pressure = 0.0;
1201 double current_static_pressure = static_pressure->getDoubleValue();
1203 double current_pressure_rate =
1204 ( current_static_pressure - last_static_pressure ) / dt;
1206 pressure_rate->setDoubleValue(current_pressure_rate);
1208 last_static_pressure = current_static_pressure;
1215 * Update the list of autopilot components
1218 void FGXMLAutopilot::update( double dt ) {
1219 update_helper( dt );
1222 for ( i = 0; i < components.size(); ++i ) {
1223 components[i]->update( dt );