]> git.mxchange.org Git - flightgear.git/blob - src/Autopilot/xmlauto.cxx
Merge branch 'jmt/acinclude'
[flightgear.git] / src / Autopilot / xmlauto.cxx
1 // xmlauto.cxx - a more flexible, generic way to build autopilots
2 //
3 // Written by Curtis Olson, started January 2004.
4 //
5 // Copyright (C) 2004  Curtis L. Olson  - http://www.flightgear.org/~curt
6 //
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.
11 //
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.
16 //
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.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include <config.h>
25 #endif
26
27 #include <iostream>
28
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>
33
34 #include <Main/fg_props.hxx>
35 #include <Main/globals.hxx>
36 #include <Main/util.hxx>
37
38 #include "xmlauto.hxx"
39
40 using std::cout;
41 using std::endl;
42
43 FGPeriodicalValue::FGPeriodicalValue( SGPropertyNode_ptr root )
44 {
45   SGPropertyNode_ptr minNode = root->getChild( "min" );
46   SGPropertyNode_ptr maxNode = root->getChild( "max" );
47   if( minNode == NULL || maxNode == NULL ) {
48     SG_LOG(SG_AUTOPILOT, SG_ALERT, "periodical defined, but no <min> and/or <max> tag. Period ignored." );
49   } else {
50     minPeriod = new FGXMLAutoInput( minNode );
51     maxPeriod = new FGXMLAutoInput( maxNode );
52   }
53 }
54
55 double FGPeriodicalValue::normalize( double value )
56 {
57   if( !(minPeriod && maxPeriod )) return value;
58
59   double p1 = minPeriod->get_value();
60   double p2 = maxPeriod->get_value();
61
62   double min = std::min<double>(p1,p2);
63   double max = std::max<double>(p1,p2);
64   double phase = fabs(max - min);
65
66   if( phase > SGLimitsd::min() ) {
67     while( value < min )  value += phase;
68     while( value >= max ) value -= phase;
69   } else {
70     value = min; // phase is zero
71   }
72
73   return value;
74 }
75
76 FGXMLAutoInput::FGXMLAutoInput( SGPropertyNode_ptr node, double value, double offset, double scale) :
77   value(0.0),
78   abs(false),
79   _condition(NULL) 
80 {
81   parse( node, value, offset, scale );
82 }
83
84
85 void FGXMLAutoInput::parse( SGPropertyNode_ptr node, double aValue, double aOffset, double aScale )
86 {
87     value = aValue;
88     property = NULL; 
89     offset = NULL;
90     scale = NULL;
91     min = NULL;
92     max = NULL;
93     periodical = NULL;
94
95     if( node == NULL )
96         return;
97
98     SGPropertyNode * n;
99
100     if( (n = node->getChild("condition")) != NULL ) {
101         _condition = sgReadCondition(fgGetNode("/"), n);
102     }
103
104     if( (n = node->getChild( "scale" )) != NULL ) {
105         scale = new FGXMLAutoInput( n, aScale );
106     }
107
108     if( (n = node->getChild( "offset" )) != NULL ) {
109         offset = new FGXMLAutoInput( n, aOffset );
110     }
111
112     if( (n = node->getChild( "max" )) != NULL ) {
113         max = new FGXMLAutoInput( n );
114     }
115
116     if( (n = node->getChild( "min" )) != NULL ) {
117         min = new FGXMLAutoInput( n );
118     }
119
120     if( (n = node->getChild( "abs" )) != NULL ) {
121       abs = n->getBoolValue();
122     }
123
124     if( (n = node->getChild( "period" )) != NULL ) {
125       periodical = new FGPeriodicalValue( n );
126     }
127
128     SGPropertyNode *valueNode = node->getChild( "value" );
129     if ( valueNode != NULL ) {
130         value = valueNode->getDoubleValue();
131     }
132
133     n = node->getChild( "property" );
134     // if no <property> element, check for <prop> element for backwards
135     // compatibility
136     if(  n == NULL )
137         n = node->getChild( "prop" );
138
139     if (  n != NULL ) {
140         property = fgGetNode(  n->getStringValue(), true );
141         if ( valueNode != NULL ) {
142             // initialize property with given value 
143             // if both <prop> and <value> exist
144             double s = get_scale();
145             if( s != 0 )
146               property->setDoubleValue( (value - get_offset())/s );
147             else
148               property->setDoubleValue( 0 ); // if scale is zero, value*scale is zero
149         }
150     }
151
152     if ( n == NULL && valueNode == NULL ) {
153         // no <value> element and no <prop> element, use text node 
154         const char * textnode = node->getStringValue();
155         char * endp = NULL;
156         // try to convert to a double value. If the textnode does not start with a number
157         // endp will point to the beginning of the string. We assume this should be
158         // a property name
159         value = strtod( textnode, &endp );
160         if( endp == textnode ) {
161           property = fgGetNode( textnode, true );
162         }
163     }
164 }
165
166 void FGXMLAutoInput::set_value( double aValue ) 
167 {
168     double s = get_scale();
169     if( s != 0 )
170         property->setDoubleValue( (aValue - get_offset())/s );
171     else
172         property->setDoubleValue( 0 ); // if scale is zero, value*scale is zero
173 }
174
175 double FGXMLAutoInput::get_value() 
176 {
177     if( property != NULL ) 
178         value = property->getDoubleValue();
179
180     if( scale ) 
181         value *= scale->get_value();
182
183     if( offset ) 
184         value += offset->get_value();
185
186     if( min ) {
187         double m = min->get_value();
188         if( value < m )
189             value = m;
190     }
191
192     if( max ) {
193         double m = max->get_value();
194         if( value > m )
195             value = m;
196     }
197
198     if( periodical ) {
199       value = periodical->normalize( value );
200     }
201     
202     return abs ? fabs(value) : value;
203 }
204
205 FGXMLAutoComponent::FGXMLAutoComponent() :
206       _condition( NULL ),
207       enable_prop( NULL ),
208       enable_value( NULL ),
209       passive_mode( fgGetNode("/autopilot/locks/passive-mode", true) ),
210       honor_passive( false ),
211       name(""),
212       feedback_if_disabled( false ),
213       debug(false),
214       enabled( false )
215 {
216 }
217
218 FGXMLAutoComponent::~FGXMLAutoComponent() 
219 {
220     delete enable_value;
221 }
222
223 void FGXMLAutoComponent::parseNode(SGPropertyNode* aNode)
224 {
225   SGPropertyNode *prop; 
226   for (int i = 0; i < aNode->nChildren(); ++i ) {
227     SGPropertyNode *child = aNode->getChild(i);
228     string cname(child->getName());
229     
230     if (parseNodeHook(cname, child)) {
231       // derived class handled it, fine
232     } else if ( cname == "name" ) {
233       name = child->getStringValue();
234     } else if ( cname == "feedback-if-disabled" ) {
235       feedback_if_disabled = child->getBoolValue();
236     } else if ( cname == "debug" ) {
237       debug = child->getBoolValue();
238     } else if ( cname == "enable" ) {
239       if( (prop = child->getChild("condition")) != NULL ) {
240         _condition = sgReadCondition(fgGetNode("/"), prop);
241       } else {
242          if ( (prop = child->getChild( "prop" )) != NULL ) {
243              enable_prop = fgGetNode( prop->getStringValue(), true );
244          }
245
246          if ( (prop = child->getChild( "value" )) != NULL ) {
247              delete enable_value;
248              enable_value = new string(prop->getStringValue());
249          }
250       }
251       if ( (prop = child->getChild( "honor-passive" )) != NULL ) {
252           honor_passive = prop->getBoolValue();
253       }
254     } else if ( cname == "input" ) {
255       valueInput.push_back( new FGXMLAutoInput( child ) );
256     } else if ( cname == "reference" ) {
257       referenceInput.push_back( new FGXMLAutoInput( child ) );
258     } else if ( cname == "output" ) {
259       // grab all <prop> and <property> childs
260       int found = 0;
261       // backwards compatibility: allow <prop> elements
262       for( int i = 0; (prop = child->getChild("prop", i)) != NULL; i++ ) { 
263           SGPropertyNode *tmp = fgGetNode( prop->getStringValue(), true );
264           output_list.push_back( tmp );
265           found++;
266       }
267       for( int i = 0; (prop = child->getChild("property", i)) != NULL; i++ ) { 
268           SGPropertyNode *tmp = fgGetNode( prop->getStringValue(), true );
269           output_list.push_back( tmp );
270           found++;
271       }
272
273       // no <prop> elements, text node of <output> is property name
274       if( found == 0 )
275           output_list.push_back( fgGetNode(child->getStringValue(), true ) );
276     } else if ( cname == "config" ) {
277       parseConfig(child);
278     } else if ( cname == "min" ) {
279         uminInput.push_back( new FGXMLAutoInput( child ) );
280     } else if ( cname == "u_min" ) {
281         uminInput.push_back( new FGXMLAutoInput( child ) );
282     } else if ( cname == "max" ) {
283         umaxInput.push_back( new FGXMLAutoInput( child ) );
284     } else if ( cname == "u_max" ) {
285         umaxInput.push_back( new FGXMLAutoInput( child ) );
286     } else if ( cname == "period" ) {
287       periodical = new FGPeriodicalValue( child );
288     } else {
289       SG_LOG(SG_AUTOPILOT, SG_ALERT, "malformed autopilot definition - unrecognized node:" 
290         << cname << " in section " << name);
291       throw sg_io_exception("XMLAuto: unrecognized component node:" + cname, "Section=" + name);
292     }
293   } // of top-level iteration
294 }
295
296 void FGXMLAutoComponent::parseConfig(SGPropertyNode* aConfig)
297 {
298   for (int i = 0; i < aConfig->nChildren(); ++i ) {
299     SGPropertyNode *child = aConfig->getChild(i);
300     string cname(child->getName());
301     
302     if (parseConfigHook(cname, child)) {
303       // derived class handled it, fine
304     } else if ( cname == "min" ) {
305         uminInput.push_back( new FGXMLAutoInput( child ) );
306     } else if ( cname == "u_min" ) {
307         uminInput.push_back( new FGXMLAutoInput( child ) );
308     } else if ( cname == "max" ) {
309         umaxInput.push_back( new FGXMLAutoInput( child ) );
310     } else if ( cname == "u_max" ) {
311         umaxInput.push_back( new FGXMLAutoInput( child ) );
312     } else {
313       SG_LOG(SG_AUTOPILOT, SG_ALERT, "malformed autopilot definition - unrecognized config node:" 
314         << cname << " in section " << name);
315       throw sg_io_exception("XMLAuto: unrecognized config node:" + cname, "Section=" + name);
316     }
317   } // of config iteration
318 }
319
320 bool FGXMLAutoComponent::parseNodeHook(const string& aName, SGPropertyNode* aNode)
321 {
322   return false;
323 }
324
325 bool FGXMLAutoComponent::parseConfigHook(const string& aName, SGPropertyNode* aNode)
326 {
327   return false;
328 }
329
330 bool FGXMLAutoComponent::isPropertyEnabled()
331 {
332     if( _condition )
333         return _condition->test();
334
335     if( enable_prop ) {
336         if( enable_value ) {
337             return *enable_value == enable_prop->getStringValue();
338         } else {
339             return enable_prop->getBoolValue();
340         }
341     }
342     return true;
343 }
344
345 void FGXMLAutoComponent::do_feedback_if_disabled()
346 {
347     if( output_list.size() > 0 ) {    
348         FGXMLAutoInput * input = valueInput.get_active();
349         if( input != NULL )
350             input->set_value( output_list[0]->getDoubleValue() );
351     }
352 }
353
354 double FGXMLAutoComponent::clamp( double value )
355 {
356     //If this is a periodical value, normalize it into our domain 
357     // before clamping
358     if( periodical )
359       value = periodical->normalize( value );
360
361     // clamp, if either min or max is defined
362     if( uminInput.size() + umaxInput.size() > 0 ) {
363         double d = umaxInput.get_value( 0.0 );
364         if( value > d ) value = d;
365         d = uminInput.get_value( 0.0 );
366         if( value < d ) value = d;
367     }
368     return value;
369 }
370
371 FGPIDController::FGPIDController( SGPropertyNode *node ):
372     FGXMLAutoComponent(),
373     alpha( 0.1 ),
374     beta( 1.0 ),
375     gamma( 0.0 ),
376     ep_n_1( 0.0 ),
377     edf_n_1( 0.0 ),
378     edf_n_2( 0.0 ),
379     u_n_1( 0.0 ),
380     desiredTs( 0.0 ),
381     elapsedTime( 0.0 )
382 {
383   parseNode(node);
384 }
385
386 bool FGPIDController::parseConfigHook(const string& aName, SGPropertyNode* aNode)
387 {
388   if (aName == "Ts") {
389     desiredTs = aNode->getDoubleValue();
390   } else if (aName == "Kp") {
391     Kp.push_back( new FGXMLAutoInput(aNode) );
392   } else if (aName == "Ti") {
393     Ti.push_back( new FGXMLAutoInput(aNode) );
394   } else if (aName == "Td") {
395     Td.push_back( new FGXMLAutoInput(aNode) );
396   } else if (aName == "beta") {
397     beta = aNode->getDoubleValue();
398   } else if (aName == "alpha") {
399     alpha = aNode->getDoubleValue();
400   } else if (aName == "gamma") {
401     gamma = aNode->getDoubleValue();
402   } else {
403     // unhandled by us, let the base class try it
404     return false;
405   }
406
407   return true;
408 }
409
410 /*
411  * Roy Vegard Ovesen:
412  *
413  * Ok! Here is the PID controller algorithm that I would like to see
414  * implemented:
415  *
416  *   delta_u_n = Kp * [ (ep_n - ep_n-1) + ((Ts/Ti)*e_n)
417  *               + (Td/Ts)*(edf_n - 2*edf_n-1 + edf_n-2) ]
418  *
419  *   u_n = u_n-1 + delta_u_n
420  *
421  * where:
422  *
423  * delta_u : The incremental output
424  * Kp      : Proportional gain
425  * ep      : Proportional error with reference weighing
426  *           ep = beta * r - y
427  *           where:
428  *           beta : Weighing factor
429  *           r    : Reference (setpoint)
430  *           y    : Process value, measured
431  * e       : Error
432  *           e = r - y
433  * Ts      : Sampling interval
434  * Ti      : Integrator time
435  * Td      : Derivator time
436  * edf     : Derivate error with reference weighing and filtering
437  *           edf_n = edf_n-1 / ((Ts/Tf) + 1) + ed_n * (Ts/Tf) / ((Ts/Tf) + 1)
438  *           where:
439  *           Tf : Filter time
440  *           Tf = alpha * Td , where alpha usually is set to 0.1
441  *           ed : Unfiltered derivate error with reference weighing
442  *             ed = gamma * r - y
443  *             where:
444  *             gamma : Weighing factor
445  * 
446  * u       : absolute output
447  * 
448  * Index n means the n'th value.
449  * 
450  * 
451  * Inputs:
452  * enabled ,
453  * y_n , r_n , beta=1 , gamma=0 , alpha=0.1 ,
454  * Kp , Ti , Td , Ts (is the sampling time available?)
455  * u_min , u_max
456  * 
457  * Output:
458  * u_n
459  */
460
461 void FGPIDController::update( double dt ) {
462     double e_n;             // error
463     double edf_n;
464     double delta_u_n = 0.0; // incremental output
465     double u_n = 0.0;       // absolute output
466     double Ts;              // sampling interval (sec)
467
468     double u_min = uminInput.get_value();
469     double u_max = umaxInput.get_value();
470
471     elapsedTime += dt;
472     if ( elapsedTime <= desiredTs ) {
473         // do nothing if time step is not positive (i.e. no time has
474         // elapsed)
475         return;
476     }
477     Ts = elapsedTime;
478     elapsedTime = 0.0;
479
480     if ( isPropertyEnabled() ) {
481         if ( !enabled ) {
482             // first time being enabled, seed u_n with current
483             // property tree value
484             u_n = get_output_value();
485             u_n_1 = u_n;
486         }
487         enabled = true;
488     } else {
489         enabled = false;
490         do_feedback();
491     }
492
493     if ( enabled && Ts > 0.0) {
494         if ( debug ) cout << "Updating " << get_name()
495                           << " Ts " << Ts << endl;
496
497         double y_n = valueInput.get_value();
498         double r_n = referenceInput.get_value();
499                       
500         if ( debug ) cout << "  input = " << y_n << " ref = " << r_n << endl;
501
502         // Calculates proportional error:
503         double ep_n = beta * r_n - y_n;
504         if ( debug ) cout << "  ep_n = " << ep_n;
505         if ( debug ) cout << "  ep_n_1 = " << ep_n_1;
506
507         // Calculates error:
508         e_n = r_n - y_n;
509         if ( debug ) cout << " e_n = " << e_n;
510
511         double td = Td.get_value();
512         if ( td > 0.0 ) { // do we need to calcluate derivative error?
513
514           // Calculates derivate error:
515             double ed_n = gamma * r_n - y_n;
516             if ( debug ) cout << " ed_n = " << ed_n;
517
518             // Calculates filter time:
519             double Tf = alpha * td;
520             if ( debug ) cout << " Tf = " << Tf;
521
522             // Filters the derivate error:
523             edf_n = edf_n_1 / (Ts/Tf + 1)
524                 + ed_n * (Ts/Tf) / (Ts/Tf + 1);
525             if ( debug ) cout << " edf_n = " << edf_n;
526         } else {
527             edf_n_2 = edf_n_1 = edf_n = 0.0;
528         }
529
530         // Calculates the incremental output:
531         double ti = Ti.get_value();
532         if ( ti > 0.0 ) {
533             delta_u_n = Kp.get_value() * ( (ep_n - ep_n_1)
534                                + ((Ts/ti) * e_n)
535                                + ((td/Ts) * (edf_n - 2*edf_n_1 + edf_n_2)) );
536
537           if ( debug ) {
538               cout << " delta_u_n = " << delta_u_n << endl;
539               cout << "P:" << Kp.get_value() * (ep_n - ep_n_1)
540                    << " I:" << Kp.get_value() * ((Ts/ti) * e_n)
541                    << " D:" << Kp.get_value() * ((td/Ts) * (edf_n - 2*edf_n_1 + edf_n_2))
542                    << endl;
543         }
544         }
545
546         // Integrator anti-windup logic:
547         if ( delta_u_n > (u_max - u_n_1) ) {
548             delta_u_n = u_max - u_n_1;
549             if ( debug ) cout << " max saturation " << endl;
550         } else if ( delta_u_n < (u_min - u_n_1) ) {
551             delta_u_n = u_min - u_n_1;
552             if ( debug ) cout << " min saturation " << endl;
553         }
554
555         // Calculates absolute output:
556         u_n = u_n_1 + delta_u_n;
557         if ( debug ) cout << "  output = " << u_n << endl;
558
559         // Updates indexed values;
560         u_n_1   = u_n;
561         ep_n_1  = ep_n;
562         edf_n_2 = edf_n_1;
563         edf_n_1 = edf_n;
564
565         set_output_value( u_n );
566     } else if ( !enabled ) {
567         ep_n_1 = 0.0;
568         edf_n_2 = edf_n_1 = edf_n = 0.0;
569     }
570 }
571
572
573 FGPISimpleController::FGPISimpleController( SGPropertyNode *node ):
574     FGXMLAutoComponent(),
575     int_sum( 0.0 )
576 {
577   parseNode(node);
578 }
579
580 bool FGPISimpleController::parseConfigHook(const string& aName, SGPropertyNode* aNode)
581 {
582   if (aName == "Kp") {
583     Kp.push_back( new FGXMLAutoInput(aNode) );
584   } else if (aName == "Ki") {
585     Ki.push_back( new FGXMLAutoInput(aNode) );
586   } else {
587     // unhandled by us, let the base class try it
588     return false;
589   }
590
591   return true;
592 }
593
594 void FGPISimpleController::update( double dt ) {
595
596     if ( isPropertyEnabled() ) {
597         if ( !enabled ) {
598             // we have just been enabled, zero out int_sum
599             int_sum = 0.0;
600         }
601         enabled = true;
602     } else {
603         enabled = false;
604         do_feedback();
605     }
606
607     if ( enabled ) {
608         if ( debug ) cout << "Updating " << get_name() << endl;
609         double y_n = valueInput.get_value();
610         double r_n = referenceInput.get_value();
611                       
612         double error = r_n - y_n;
613         if ( debug ) cout << "input = " << y_n
614                           << " reference = " << r_n
615                           << " error = " << error
616                           << endl;
617
618         double prop_comp = clamp(error * Kp.get_value());
619         int_sum += error * Ki.get_value() * dt;
620
621
622         double output = prop_comp + int_sum;
623         double clamped_output = clamp( output );
624         if( output != clamped_output ) // anti-windup
625           int_sum = clamped_output - prop_comp;
626
627         if ( debug ) cout << "prop_comp = " << prop_comp
628                           << " int_sum = " << int_sum << endl;
629
630         set_output_value( clamped_output );
631         if ( debug ) cout << "output = " << clamped_output << endl;
632     }
633 }
634
635
636 FGPredictor::FGPredictor ( SGPropertyNode *node ):
637     FGXMLAutoComponent(),
638     average(0.0)
639 {
640   parseNode(node);
641 }
642
643 bool FGPredictor::parseNodeHook(const string& aName, SGPropertyNode* aNode)
644 {
645   if (aName == "seconds") {
646     seconds.push_back( new FGXMLAutoInput( aNode, 0 ) );
647   } else if (aName == "filter-gain") {
648     filter_gain.push_back( new FGXMLAutoInput( aNode, 0 ) );
649   } else {
650     return false;
651   }
652   
653   return true;
654 }
655
656 void FGPredictor::update( double dt ) {
657     /*
658        Simple moving average filter converts input value to predicted value "seconds".
659
660        Smoothing as described by Curt Olson:
661          gain would be valid in the range of 0 - 1.0
662          1.0 would mean no filtering.
663          0.0 would mean no input.
664          0.5 would mean (1 part past value + 1 part current value) / 2
665          0.1 would mean (9 parts past value + 1 part current value) / 10
666          0.25 would mean (3 parts past value + 1 part current value) / 4
667
668     */
669
670     double ivalue = valueInput.get_value();
671
672     if ( isPropertyEnabled() ) {
673         if ( !enabled ) {
674             // first time being enabled
675             last_value = ivalue;
676         }
677         enabled = true;
678     } else {
679         enabled = false;
680         do_feedback();
681     }
682
683     if ( enabled ) {
684
685         if ( dt > 0.0 ) {
686             double current = (ivalue - last_value)/dt; // calculate current error change (per second)
687             average = dt < 1.0 ? ((1.0 - dt) * average + current * dt) : current;
688
689             // calculate output with filter gain adjustment
690             double output = ivalue + 
691                (1.0 - filter_gain.get_value()) * (average * seconds.get_value()) + 
692                        filter_gain.get_value() * (current * seconds.get_value());
693             output = clamp( output );
694             set_output_value( output );
695         }
696         last_value = ivalue;
697     }
698 }
699
700
701 FGDigitalFilter::FGDigitalFilter(SGPropertyNode *node):
702     FGXMLAutoComponent(),
703     filterType(none)
704 {
705     parseNode(node);
706
707     output.resize(2, 0.0);
708     input.resize(samplesInput.get_value() + 1, 0.0);
709 }
710
711
712 bool FGDigitalFilter::parseNodeHook(const string& aName, SGPropertyNode* aNode)
713 {
714   if (aName == "type" ) {
715     string val(aNode->getStringValue());
716     if ( val == "exponential" ) {
717       filterType = exponential;
718     } else if (val == "double-exponential") {
719       filterType = doubleExponential;
720     } else if (val == "moving-average") {
721       filterType = movingAverage;
722     } else if (val == "noise-spike") {
723       filterType = noiseSpike;
724     } else if (val == "gain") {
725       filterType = gain;
726     } else if (val == "reciprocal") {
727       filterType = reciprocal;
728     } else if (val == "differential") {
729       filterType = differential;
730       // use a constant of two samples for current and previous input value
731       samplesInput.push_back( new FGXMLAutoInput(NULL, 2.0 ) ); 
732     }
733   } else if (aName == "filter-time" ) {
734     TfInput.push_back( new FGXMLAutoInput( aNode, 1.0 ) );
735     if( filterType == none ) filterType = exponential;
736   } else if (aName == "samples" ) {
737     samplesInput.push_back( new FGXMLAutoInput( aNode, 1 ) );
738     if( filterType == none ) filterType = movingAverage;
739   } else if (aName == "max-rate-of-change" ) {
740     rateOfChangeInput.push_back( new FGXMLAutoInput( aNode, 1 ) );
741     if( filterType == none ) filterType = noiseSpike;
742   } else if (aName == "gain" ) {
743     gainInput.push_back( new FGXMLAutoInput( aNode, 1 ) );
744     if( filterType == none ) filterType = gain;
745   } else {
746     return false; // not handled by us, let the base class try
747   }
748   
749   return true;
750 }
751
752 void FGDigitalFilter::update(double dt)
753 {
754     if ( isPropertyEnabled() ) {
755
756         input.push_front(valueInput.get_value()-referenceInput.get_value());
757         input.resize(samplesInput.get_value() + 1, 0.0);
758
759         if ( !enabled ) {
760             // first time being enabled, initialize output to the
761             // value of the output property to avoid bumping.
762             output.push_front(get_output_value());
763         }
764
765         enabled = true;
766     } else {
767         enabled = false;
768         do_feedback();
769     }
770
771     if ( !enabled || dt < SGLimitsd::min() ) 
772         return;
773
774     /*
775      * Exponential filter
776      *
777      * Output[n] = alpha*Input[n] + (1-alpha)*Output[n-1]
778      *
779      */
780      if( debug ) cout << "Updating " << get_name()
781                       << " dt " << dt << endl;
782
783     if (filterType == exponential)
784     {
785         double alpha = 1 / ((TfInput.get_value()/dt) + 1);
786         output.push_front(alpha * input[0] + 
787                           (1 - alpha) * output[0]);
788     } 
789     else if (filterType == doubleExponential)
790     {
791         double alpha = 1 / ((TfInput.get_value()/dt) + 1);
792         output.push_front(alpha * alpha * input[0] + 
793                           2 * (1 - alpha) * output[0] -
794                           (1 - alpha) * (1 - alpha) * output[1]);
795     }
796     else if (filterType == movingAverage)
797     {
798         output.push_front(output[0] + 
799                           (input[0] - input.back()) / samplesInput.get_value());
800     }
801     else if (filterType == noiseSpike)
802     {
803         double maxChange = rateOfChangeInput.get_value() * dt;
804
805         if ((output[0] - input[0]) > maxChange)
806         {
807             output.push_front(output[0] - maxChange);
808         }
809         else if ((output[0] - input[0]) < -maxChange)
810         {
811             output.push_front(output[0] + maxChange);
812         }
813         else if (fabs(input[0] - output[0]) <= maxChange)
814         {
815             output.push_front(input[0]);
816         }
817     }
818     else if (filterType == gain)
819     {
820         output[0] = gainInput.get_value() * input[0];
821     }
822     else if (filterType == reciprocal)
823     {
824         if (input[0] != 0.0) {
825             output[0] = gainInput.get_value() / input[0];
826         }
827     }
828     else if (filterType == differential)
829     {
830         if( dt > SGLimitsd::min() ) {
831             output[0] = (input[0]-input[1]) * TfInput.get_value() / dt;
832         }
833     }
834
835     output[0] = clamp(output[0]) ;
836     set_output_value( output[0] );
837
838     output.resize(2);
839
840     if (debug)
841     {
842         cout << "input:" << input[0] 
843              << "\toutput:" << output[0] << endl;
844     }
845 }
846
847 FGXMLAutoLogic::FGXMLAutoLogic(SGPropertyNode * node ) :
848     FGXMLAutoComponent(),
849     inverted(false)
850 {
851     parseNode(node);
852 }
853
854 bool FGXMLAutoLogic::parseNodeHook(const std::string& aName, SGPropertyNode* aNode)
855 {
856     if (aName == "input") {
857         input = sgReadCondition( fgGetNode("/"), aNode );
858     } else if (aName == "inverted") {
859         inverted = aNode->getBoolValue();
860     } else {
861         return false;
862     }
863   
864     return true;
865 }
866
867 void FGXMLAutoLogic::update(double dt)
868 {
869     if ( isPropertyEnabled() ) {
870         if ( !enabled ) {
871             // we have just been enabled
872         }
873         enabled = true;
874     } else {
875         enabled = false;
876         do_feedback();
877     }
878
879     if ( !enabled || dt < SGLimitsd::min() ) 
880         return;
881
882     if( input == NULL ) {
883         if ( debug ) cout << "No input for " << get_name() << endl;
884         return;
885     }
886
887     bool i = input->test();
888
889     if ( debug ) cout << "Updating " << get_name() << ": " << (inverted ? !i : i) << endl;
890
891     set_output_value( i );
892 }
893
894
895 FGXMLAutopilotGroup::FGXMLAutopilotGroup() :
896   SGSubsystemGroup()
897 #ifdef XMLAUTO_USEHELPER
898   ,average(0.0), // average/filtered prediction
899   v_last(0.0),  // last velocity
900   last_static_pressure(0.0),
901   vel(fgGetNode( "/velocities/airspeed-kt", true )),
902   // Estimate speed in 5,10 seconds
903   lookahead5(fgGetNode( "/autopilot/internal/lookahead-5-sec-airspeed-kt", true )),
904   lookahead10(fgGetNode( "/autopilot/internal/lookahead-10-sec-airspeed-kt", true )),
905   bug(fgGetNode( "/autopilot/settings/heading-bug-deg", true )),
906   mag_hdg(fgGetNode( "/orientation/heading-magnetic-deg", true )),
907   bug_error(fgGetNode( "/autopilot/internal/heading-bug-error-deg", true )),
908   fdm_bug_error(fgGetNode( "/autopilot/internal/fdm-heading-bug-error-deg", true )),
909   target_true(fgGetNode( "/autopilot/settings/true-heading-deg", true )),
910   true_hdg(fgGetNode( "/orientation/heading-deg", true )),
911   true_error(fgGetNode( "/autopilot/internal/true-heading-error-deg", true )),
912   target_nav1(fgGetNode( "/instrumentation/nav[0]/radials/target-auto-hdg-deg", true )),
913   true_nav1(fgGetNode( "/autopilot/internal/nav1-heading-error-deg", true )),
914   true_track_nav1(fgGetNode( "/autopilot/internal/nav1-track-error-deg", true )),
915   nav1_course_error(fgGetNode( "/autopilot/internal/nav1-course-error", true )),
916   nav1_selected_course(fgGetNode( "/instrumentation/nav[0]/radials/selected-deg", true )),
917   vs_fps(fgGetNode( "/velocities/vertical-speed-fps", true )),
918   vs_fpm(fgGetNode( "/autopilot/internal/vert-speed-fpm", true )),
919   static_pressure(fgGetNode( "/systems/static[0]/pressure-inhg", true )),
920   pressure_rate(fgGetNode( "/autopilot/internal/pressure-rate", true )),
921   track(fgGetNode( "/orientation/track-deg", true ))
922 #endif
923 {
924 }
925
926 void FGXMLAutopilotGroup::update( double dt )
927 {
928     // update all configured autopilots
929     SGSubsystemGroup::update( dt );
930 #ifdef XMLAUTO_USEHELPER
931     // update helper values
932     double v = vel->getDoubleValue();
933     double a = 0.0;
934     if ( dt > 0.0 ) {
935         a = (v - v_last) / dt;
936
937         if ( dt < 1.0 ) {
938             average = (1.0 - dt) * average + dt * a;
939         } else {
940             average = a;
941         }
942
943         lookahead5->setDoubleValue( v + average * 5.0 );
944         lookahead10->setDoubleValue( v + average * 10.0 );
945         v_last = v;
946     }
947
948     // Calculate heading bug error normalized to +/- 180.0
949     double diff = bug->getDoubleValue() - mag_hdg->getDoubleValue();
950     SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
951     bug_error->setDoubleValue( diff );
952
953     fdm_bug_error->setDoubleValue( diff );
954
955     // Calculate true heading error normalized to +/- 180.0
956     diff = target_true->getDoubleValue() - true_hdg->getDoubleValue();
957     SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
958     true_error->setDoubleValue( diff );
959
960     // Calculate nav1 target heading error normalized to +/- 180.0
961     diff = target_nav1->getDoubleValue() - true_hdg->getDoubleValue();
962     SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
963     true_nav1->setDoubleValue( diff );
964
965     // Calculate true groundtrack
966     diff = target_nav1->getDoubleValue() - track->getDoubleValue();
967     SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
968     true_track_nav1->setDoubleValue( diff );
969
970     // Calculate nav1 selected course error normalized to +/- 180.0
971     diff = nav1_selected_course->getDoubleValue() - mag_hdg->getDoubleValue();
972     SG_NORMALIZE_RANGE( diff, -180.0, 180.0 );
973     nav1_course_error->setDoubleValue( diff );
974
975     // Calculate vertical speed in fpm
976     vs_fpm->setDoubleValue( vs_fps->getDoubleValue() * 60.0 );
977
978
979     // Calculate static port pressure rate in [inhg/s].
980     // Used to determine vertical speed.
981     if ( dt > 0.0 ) {
982         double current_static_pressure = static_pressure->getDoubleValue();
983         double current_pressure_rate = 
984             ( current_static_pressure - last_static_pressure ) / dt;
985
986         pressure_rate->setDoubleValue(current_pressure_rate);
987         last_static_pressure = current_static_pressure;
988     }
989 #endif
990 }
991
992 void FGXMLAutopilotGroup::reinit()
993 {
994     for( vector<string>::size_type i = 0; i < _autopilotNames.size(); i++ ) {
995       FGXMLAutopilot * ap = (FGXMLAutopilot*)get_subsystem( _autopilotNames[i] );
996       if( ap == NULL ) continue; // ?
997       remove_subsystem( _autopilotNames[i] );
998       delete ap;
999     }
1000     _autopilotNames.clear();
1001     init();
1002 }
1003
1004 void FGXMLAutopilotGroup::init()
1005 {
1006     vector<SGPropertyNode_ptr> autopilotNodes = fgGetNode( "/sim/systems", true )->getChildren("autopilot");
1007     if( autopilotNodes.size() == 0 ) {
1008         SG_LOG( SG_ALL, SG_WARN, "No autopilot configuration specified for this model!");
1009         return;
1010     }
1011
1012     for( vector<SGPropertyNode_ptr>::size_type i = 0; i < autopilotNodes.size(); i++ ) {
1013         SGPropertyNode_ptr pathNode = autopilotNodes[i]->getNode( "path" );
1014         if( pathNode == NULL ) {
1015             SG_LOG( SG_ALL, SG_WARN, "No autopilot configuration file specified for this autopilot!");
1016             continue;
1017         }
1018
1019         string apName;
1020         SGPropertyNode_ptr nameNode = autopilotNodes[i]->getNode( "name" );
1021         if( nameNode != NULL ) {
1022             apName = nameNode->getStringValue();
1023         } else {
1024           std::ostringstream buf;
1025           buf <<  "unnamed_autopilot_" << i;
1026           apName = buf.str();
1027         }
1028
1029         if( get_subsystem( apName.c_str() ) != NULL ) {
1030             SG_LOG( SG_ALL, SG_ALERT, "Duplicate autopilot configuration name " << apName << " ignored" );
1031             continue;
1032         }
1033
1034         SGPath config( globals->get_fg_root() );
1035         config.append( pathNode->getStringValue() );
1036
1037         SG_LOG( SG_ALL, SG_INFO, "Reading autopilot configuration from " << config.str() );
1038         // FGXMLAutopilot
1039         FGXMLAutopilot * ap = new FGXMLAutopilot;
1040         try {
1041             SGPropertyNode_ptr root = new SGPropertyNode();
1042             readProperties( config.str(), root );
1043
1044
1045             if ( ! ap->build( root ) ) {
1046                 SG_LOG( SG_ALL, SG_ALERT,
1047                   "Detected an internal inconsistency in the autopilot configuration." << endl << " See earlier errors for details." );
1048                 delete ap;
1049                 continue;
1050             }        
1051         } catch (const sg_exception& e) {
1052             SG_LOG( SG_AUTOPILOT, SG_ALERT, "Failed to load autopilot configuration: "
1053                     << config.str() << ":" << e.getMessage() );
1054             delete ap;
1055             continue;
1056         }
1057
1058         SG_LOG( SG_AUTOPILOT, SG_INFO, "adding  autopilot subsystem " << apName );
1059         set_subsystem( apName, ap );
1060         _autopilotNames.push_back( apName );
1061     }
1062
1063     SGSubsystemGroup::init();
1064 }
1065
1066 FGXMLAutopilot::FGXMLAutopilot() {
1067 }
1068
1069
1070 FGXMLAutopilot::~FGXMLAutopilot() {
1071 }
1072
1073  
1074 /* read all /sim/systems/autopilot[n]/path properties, try to read the file specified therein
1075  * and configure/add the digital filters specified in that file
1076  */
1077 void FGXMLAutopilot::init() 
1078 {
1079 }
1080
1081
1082 void FGXMLAutopilot::reinit() {
1083     components.clear();
1084     init();
1085 }
1086
1087
1088 void FGXMLAutopilot::bind() {
1089 }
1090
1091 void FGXMLAutopilot::unbind() {
1092 }
1093
1094 bool FGXMLAutopilot::build( SGPropertyNode_ptr config_props ) {
1095     SGPropertyNode *node;
1096     int i;
1097
1098     int count = config_props->nChildren();
1099     for ( i = 0; i < count; ++i ) {
1100         node = config_props->getChild(i);
1101         string name = node->getName();
1102         // cout << name << endl;
1103         SG_LOG( SG_AUTOPILOT, SG_BULK, "adding  autopilot component " << name );
1104         if ( name == "pid-controller" ) {
1105             components.push_back( new FGPIDController( node ) );
1106         } else if ( name == "pi-simple-controller" ) {
1107             components.push_back( new FGPISimpleController( node ) );
1108         } else if ( name == "predict-simple" ) {
1109             components.push_back( new FGPredictor( node ) );
1110         } else if ( name == "filter" ) {
1111             components.push_back( new FGDigitalFilter( node ) );
1112         } else if ( name == "logic" ) {
1113             components.push_back( new FGXMLAutoLogic( node ) );
1114         } else {
1115             SG_LOG( SG_AUTOPILOT, SG_WARN, "Unknown top level autopilot section: " << name );
1116 //            return false;
1117         }
1118     }
1119
1120     return true;
1121 }
1122
1123 /*
1124  * Update the list of autopilot components
1125  */
1126
1127 void FGXMLAutopilot::update( double dt ) 
1128 {
1129     unsigned int i;
1130     for ( i = 0; i < components.size(); ++i ) {
1131         components[i]->update( dt );
1132     }
1133 }
1134