]> git.mxchange.org Git - flightgear.git/blob - src/Autopilot/digitalfilter.cxx
Fix #1186 moving average filter
[flightgear.git] / src / Autopilot / digitalfilter.cxx
1 // digitalfilter.cxx - a selection of digital filters
2 //
3 // Written by Torsten Dreyer
4 // Based heavily on work created by Curtis Olson, started January 2004.
5 //
6 // Copyright (C) 2004  Curtis L. Olson  - http://www.flightgear.org/~curt
7 // Copyright (C) 2010  Torsten Dreyer - Torsten (at) t3r (dot) de
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License as
11 // published by the Free Software Foundation; either version 2 of the
12 // License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful, but
15 // WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 // General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
22 //
23
24 #include "digitalfilter.hxx"
25 #include "functor.hxx"
26 #include <deque>
27
28 using std::map;
29 using std::string;
30 using std::endl;
31 using std::cout;
32
33 namespace FGXMLAutopilot {
34
35 /**
36  *
37  *
38  */
39 class DigitalFilterImplementation : public SGReferenced {
40 protected:
41   virtual bool configure( const std::string & nodeName, SGPropertyNode_ptr configNode) = 0;
42 public:
43   virtual ~DigitalFilterImplementation() {}
44   DigitalFilterImplementation();
45   virtual void   initialize( double initvalue ) {}
46   virtual double compute( double dt, double input ) = 0;
47   bool configure( SGPropertyNode_ptr configNode );
48
49   void setDigitalFilter( DigitalFilter * digitalFilter ) { _digitalFilter = digitalFilter; }
50
51 protected:
52   DigitalFilter * _digitalFilter;
53 };
54
55 /* --------------------------------------------------------------------------------- */
56 /* --------------------------------------------------------------------------------- */
57 class GainFilterImplementation : public DigitalFilterImplementation {
58 protected:
59   InputValueList _gainInput;
60   bool configure( const std::string & nodeName, SGPropertyNode_ptr configNode );
61 public:
62   GainFilterImplementation() : _gainInput(1.0) {}
63   double compute(  double dt, double input );
64 };
65
66 class ReciprocalFilterImplementation : public GainFilterImplementation {
67 public:
68   double compute(  double dt, double input );
69 };
70
71 class DerivativeFilterImplementation : public GainFilterImplementation {
72   InputValueList _TfInput;
73   double _input_1;
74   bool configure( const std::string & nodeName, SGPropertyNode_ptr configNode );
75 public:
76   DerivativeFilterImplementation();
77   double compute(  double dt, double input );
78   virtual void initialize( double initvalue );
79 };
80
81 class ExponentialFilterImplementation : public GainFilterImplementation {
82 protected:
83   InputValueList _TfInput;
84   bool configure( const std::string & nodeName, SGPropertyNode_ptr configNode );
85   bool _isSecondOrder;
86   double output_1, output_2;
87 public:
88   ExponentialFilterImplementation();
89   double compute(  double dt, double input );
90   virtual void initialize( double initvalue );
91 };
92
93 class MovingAverageFilterImplementation : public DigitalFilterImplementation {
94 protected:
95   InputValueList _samplesInput;
96   double _output_1;
97   std::deque <double> _inputQueue;
98   bool configure( const std::string & nodeName, SGPropertyNode_ptr configNode );
99 public:
100   MovingAverageFilterImplementation();
101   double compute(  double dt, double input );
102   virtual void initialize( double initvalue );
103 };
104
105 class NoiseSpikeFilterImplementation : public DigitalFilterImplementation {
106 protected:
107   double _output_1;
108   InputValueList _rateOfChangeInput;
109   bool configure( const std::string & nodeName, SGPropertyNode_ptr configNode );
110 public:
111   NoiseSpikeFilterImplementation();
112   double compute(  double dt, double input );
113   virtual void initialize( double initvalue );
114 };
115
116 /* --------------------------------------------------------------------------------- */
117 /* --------------------------------------------------------------------------------- */
118
119 } // namespace FGXMLAutopilot
120
121 using namespace FGXMLAutopilot;
122
123 /* --------------------------------------------------------------------------------- */
124 /* --------------------------------------------------------------------------------- */
125 DigitalFilterImplementation::DigitalFilterImplementation() :
126   _digitalFilter(NULL)
127 {
128 }
129
130 bool DigitalFilterImplementation::configure( SGPropertyNode_ptr configNode )
131 {
132   for (int i = 0; i < configNode->nChildren(); ++i ) {
133     SGPropertyNode_ptr prop;
134
135     SGPropertyNode_ptr child = configNode->getChild(i);
136     string cname(child->getName());
137
138     if( configure( cname, child ) )
139       continue;
140
141   } // for configNode->nChildren()
142
143   return true;
144 }
145
146 /* --------------------------------------------------------------------------------- */
147 /* --------------------------------------------------------------------------------- */
148
149 double GainFilterImplementation::compute(  double dt, double input )
150 {
151   return _gainInput.get_value() * input;
152 }
153
154 bool GainFilterImplementation::configure( const std::string & nodeName, SGPropertyNode_ptr configNode )
155 {
156   if (nodeName == "gain" ) {
157     _gainInput.push_back( new InputValue( configNode, 1 ) );
158     return true;
159   }
160
161   return false;
162 }
163
164 /* --------------------------------------------------------------------------------- */
165 /* --------------------------------------------------------------------------------- */
166
167 double ReciprocalFilterImplementation::compute(  double dt, double input )
168 {
169   if( input >= -SGLimitsd::min() && input <= SGLimitsd::min() )
170     return SGLimitsd::max();
171
172   return _gainInput.get_value() / input;
173
174 }
175
176 /* --------------------------------------------------------------------------------- */
177 /* --------------------------------------------------------------------------------- */
178
179 DerivativeFilterImplementation::DerivativeFilterImplementation() :
180   _input_1(0.0)
181 {
182 }
183
184 void DerivativeFilterImplementation::initialize( double initvalue )
185 {
186   _input_1 = initvalue;
187 }
188
189
190 bool DerivativeFilterImplementation::configure( const std::string & nodeName, SGPropertyNode_ptr configNode )
191 {
192   if( GainFilterImplementation::configure( nodeName, configNode ) )
193     return true;
194
195   if (nodeName == "filter-time" ) {
196     _TfInput.push_back( new InputValue( configNode, 1 ) );
197     return true;
198   }
199
200   return false;
201 }
202
203 double DerivativeFilterImplementation::compute(  double dt, double input )
204 {
205   double output = (input - _input_1) * _TfInput.get_value() * _gainInput.get_value() / dt;
206   _input_1 = input;
207   return output;
208
209 }
210
211 /* --------------------------------------------------------------------------------- */
212 /* --------------------------------------------------------------------------------- */
213
214 MovingAverageFilterImplementation::MovingAverageFilterImplementation() :
215   _output_1(0.0)
216 {
217 }
218
219 void MovingAverageFilterImplementation::initialize( double initvalue )
220 {
221   _output_1 = initvalue;
222 }
223
224 double MovingAverageFilterImplementation::compute(  double dt, double input )
225 {
226   std::deque<double>::size_type samples = _samplesInput.get_value();
227
228   if (_inputQueue.size() != samples) {
229     // For constant size filters, this code executed once.
230     bool shrunk = _inputQueue.size() > samples;
231     _inputQueue.resize(samples, _output_1);
232     if (shrunk) {
233       _output_1 = 0.0;
234       for (int ii = 0; ii < samples; ii++)
235       _output_1 += _inputQueue[ii];
236       _output_1 /= samples;
237     }
238   }
239
240   double output_0 = _output_1 + (input - _inputQueue.back()) / samples;
241
242   _output_1 = output_0;
243   _inputQueue.pop_back();
244   _inputQueue.push_front(input);
245   return output_0;
246 }
247
248 bool MovingAverageFilterImplementation::configure( const std::string & nodeName, SGPropertyNode_ptr configNode )
249 {
250   if (nodeName == "samples" ) {
251     _samplesInput.push_back( new InputValue( configNode, 1 ) );
252     return true;
253   }
254
255   return false;
256 }
257
258 /* --------------------------------------------------------------------------------- */
259 /* --------------------------------------------------------------------------------- */
260
261 NoiseSpikeFilterImplementation::NoiseSpikeFilterImplementation() :
262   _output_1(0.0)
263 {
264 }
265
266 void NoiseSpikeFilterImplementation::initialize( double initvalue )
267 {
268   _output_1 = initvalue;
269 }
270
271 double NoiseSpikeFilterImplementation::compute(  double dt, double input )
272 {
273   double delta = input - _output_1;
274   if( fabs(delta) <= SGLimitsd::min() ) return input; // trivial
275
276   double maxChange = _rateOfChangeInput.get_value() * dt;
277   const PeriodicalValue * periodical = _digitalFilter->getPeriodicalValue();
278   if( periodical ) delta = periodical->normalizeSymmetric( delta );
279
280   if( fabs(delta) <= maxChange )
281     return (_output_1 = input);
282   else
283     return (_output_1 = _output_1 + copysign( maxChange, delta ));
284 }
285
286 bool NoiseSpikeFilterImplementation::configure( const std::string & nodeName, SGPropertyNode_ptr configNode )
287 {
288   if (nodeName == "max-rate-of-change" ) {
289     _rateOfChangeInput.push_back( new InputValue( configNode, 1 ) );
290     return true;
291   }
292
293   return false;
294 }
295
296 /* --------------------------------------------------------------------------------- */
297 /* --------------------------------------------------------------------------------- */
298
299 ExponentialFilterImplementation::ExponentialFilterImplementation()
300   : _isSecondOrder(false),
301     output_1(0.0),
302     output_2(0.0)
303 {
304 }
305
306 void ExponentialFilterImplementation::initialize( double initvalue )
307 {
308   output_1 = output_2 = initvalue;
309 }
310
311 double ExponentialFilterImplementation::compute(  double dt, double input )
312 {
313   input = GainFilterImplementation::compute( dt, input );
314   double tf = _TfInput.get_value();
315
316   double output_0;
317
318   // avoid negative filter times 
319   // and div by zero if -tf == dt
320
321   double alpha = tf > 0.0 ? 1 / ((tf/dt) + 1) : 1.0;
322  
323   if(_isSecondOrder) {
324     output_0 = alpha * alpha * input + 
325                2 * (1 - alpha) * output_1 -
326               (1 - alpha) * (1 - alpha) * output_2;
327   } else {
328     output_0 = alpha * input + (1 - alpha) * output_1;
329   }
330   output_2 = output_1;
331   return (output_1 = output_0);
332 }
333
334 bool ExponentialFilterImplementation::configure( const std::string & nodeName, SGPropertyNode_ptr configNode )
335 {
336   if( GainFilterImplementation::configure( nodeName, configNode ) )
337     return true;
338
339   if (nodeName == "filter-time" ) {
340     _TfInput.push_back( new InputValue( configNode, 1 ) );
341     return true;
342   }
343
344   if (nodeName == "type" ) {
345     string type(configNode->getStringValue());
346     _isSecondOrder = type == "double-exponential";
347   }
348
349   return false;
350 }
351
352 /* --------------------------------------------------------------------------------- */
353 /* Digital Filter Component Implementation                                           */
354 /* --------------------------------------------------------------------------------- */
355
356 DigitalFilter::DigitalFilter() :
357     AnalogComponent(),
358     _initializeTo(INITIALIZE_INPUT)
359 {
360 }
361
362 DigitalFilter::~DigitalFilter()
363 {
364 }
365
366
367 static map<string,FunctorBase<DigitalFilterImplementation> *> componentForge;
368
369 bool DigitalFilter::configure(const string& nodeName, SGPropertyNode_ptr configNode)
370 {
371   if( componentForge.empty() ) {
372     componentForge["gain"] = new CreateAndConfigureFunctor<GainFilterImplementation,DigitalFilterImplementation>();
373     componentForge["exponential"] = new CreateAndConfigureFunctor<ExponentialFilterImplementation,DigitalFilterImplementation>();
374     componentForge["double-exponential"] = new CreateAndConfigureFunctor<ExponentialFilterImplementation,DigitalFilterImplementation>();
375     componentForge["moving-average"] = new CreateAndConfigureFunctor<MovingAverageFilterImplementation,DigitalFilterImplementation>();
376     componentForge["noise-spike"] = new CreateAndConfigureFunctor<NoiseSpikeFilterImplementation,DigitalFilterImplementation>();
377     componentForge["reciprocal"] = new CreateAndConfigureFunctor<ReciprocalFilterImplementation,DigitalFilterImplementation>();
378     componentForge["derivative"] = new CreateAndConfigureFunctor<DerivativeFilterImplementation,DigitalFilterImplementation>();
379   }
380
381   SG_LOG( SG_AUTOPILOT, SG_BULK, "DigitalFilter::configure(" << nodeName << ")" << endl );
382   if( AnalogComponent::configure( nodeName, configNode ) )
383     return true;
384
385   if (nodeName == "type" ) {
386     string type( configNode->getStringValue() );
387     if( componentForge.count(type) == 0 ) {
388       SG_LOG( SG_AUTOPILOT, SG_BULK, "unhandled filter type <" << type << ">" << endl );
389       return true;
390     }
391     _implementation = (*componentForge[type])( configNode->getParent() );
392     _implementation->setDigitalFilter( this );
393     return true;
394   }
395
396   if( nodeName == "initialize-to" ) {
397     string s( configNode->getStringValue() );
398     if( s == "input" ) {
399       _initializeTo = INITIALIZE_INPUT;
400     } else if( s == "output" ) {
401       _initializeTo = INITIALIZE_OUTPUT;
402     } else if( s == "none" ) {
403       _initializeTo = INITIALIZE_NONE;
404     } else {
405       SG_LOG( SG_AUTOPILOT, SG_WARN, "unhandled initialize-to value '" << s << "' ignored" );
406     }
407     return true;
408   }
409
410   SG_LOG( SG_AUTOPILOT, SG_BULK, "DigitalFilter::configure(" << nodeName << ") [unhandled]" << endl );
411   return false; // not handled by us, let the base class try
412 }
413
414 void DigitalFilter::update( bool firstTime, double dt)
415 {
416   if( _implementation == NULL ) return;
417
418   if( firstTime ) {
419     switch( _initializeTo ) {
420
421       case INITIALIZE_INPUT:
422         SG_LOG(SG_AUTOPILOT,SG_DEBUG, "First time initialization of " << get_name() << " to " << _valueInput.get_value() );
423         _implementation->initialize( _valueInput.get_value() );
424         break;
425
426       case INITIALIZE_OUTPUT:
427         SG_LOG(SG_AUTOPILOT,SG_DEBUG, "First time initialization of " << get_name() << " to " << get_output_value() );
428         _implementation->initialize( get_output_value() );
429         break;
430
431       default:
432         SG_LOG(SG_AUTOPILOT,SG_DEBUG, "First time initialization of " << get_name() << " to (uninitialized)" );
433         break;
434     }
435   }
436
437   double input = _valueInput.get_value() - _referenceInput.get_value();
438   double output = _implementation->compute( dt, input );
439
440   set_output_value( output );
441
442   if(_debug) {
443     cout << "input:" << input
444          << "\toutput:" << output << endl;
445   }
446 }