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