1 // digitalfilter.cxx - a selection of digital filters
3 // Written by Torsten Dreyer
4 // Based heavily on work created by Curtis Olson, started January 2004.
6 // Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
7 // Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
9 // Washout/high-pass filter, lead-lag filter and integrator added.
10 // low-pass and lag aliases added to Exponential filter,
11 // rate-limit added. A J Teeder 2013
13 // This program is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU General Public License as
15 // published by the Free Software Foundation; either version 2 of the
16 // License, or (at your option) any later version.
18 // This program is distributed in the hope that it will be useful, but
19 // WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // General Public License for more details.
23 // You should have received a copy of the GNU General Public License
24 // along with this program; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 #include "digitalfilter.hxx"
29 #include "functor.hxx"
37 namespace FGXMLAutopilot {
43 class DigitalFilterImplementation : public SGReferenced {
45 virtual bool configure( SGPropertyNode& cfg_node,
46 const std::string& cfg_name,
47 SGPropertyNode& prop_root ) = 0;
49 virtual ~DigitalFilterImplementation() {}
50 DigitalFilterImplementation();
51 virtual void initialize( double initvalue ) {}
52 virtual double compute( double dt, double input ) = 0;
53 bool configure( SGPropertyNode& cfg,
54 SGPropertyNode& prop_root );
56 void setDigitalFilter( DigitalFilter * digitalFilter ) { _digitalFilter = digitalFilter; }
59 DigitalFilter * _digitalFilter;
62 /* --------------------------------------------------------------------------------- */
63 /* --------------------------------------------------------------------------------- */
64 class GainFilterImplementation : public DigitalFilterImplementation {
66 InputValueList _gainInput;
67 bool configure( SGPropertyNode& cfg_node,
68 const std::string& cfg_name,
69 SGPropertyNode& prop_root );
71 GainFilterImplementation() : _gainInput(1.0) {}
72 double compute( double dt, double input );
75 class ReciprocalFilterImplementation : public GainFilterImplementation {
77 double compute( double dt, double input );
80 class DerivativeFilterImplementation : public GainFilterImplementation {
81 InputValueList _TfInput;
83 bool configure( SGPropertyNode& cfg_node,
84 const std::string& cfg_name,
85 SGPropertyNode& prop_root );
87 DerivativeFilterImplementation();
88 double compute( double dt, double input );
89 virtual void initialize( double initvalue );
92 class ExponentialFilterImplementation : public GainFilterImplementation {
94 InputValueList _TfInput;
95 bool configure( SGPropertyNode& cfg_node,
96 const std::string& cfg_name,
97 SGPropertyNode& prop_root );
99 double _output_1, _output_2;
101 ExponentialFilterImplementation();
102 double compute( double dt, double input );
103 virtual void initialize( double initvalue );
106 class MovingAverageFilterImplementation : public DigitalFilterImplementation {
108 InputValueList _samplesInput;
110 std::deque <double> _inputQueue;
111 bool configure( SGPropertyNode& cfg_node,
112 const std::string& cfg_name,
113 SGPropertyNode& prop_root );
115 MovingAverageFilterImplementation();
116 double compute( double dt, double input );
117 virtual void initialize( double initvalue );
120 class NoiseSpikeFilterImplementation : public DigitalFilterImplementation {
123 InputValueList _rateOfChangeInput;
124 bool configure( SGPropertyNode& cfg_node,
125 const std::string& cfg_name,
126 SGPropertyNode& prop_root );
128 NoiseSpikeFilterImplementation();
129 double compute( double dt, double input );
130 virtual void initialize( double initvalue );
133 class RateLimitFilterImplementation : public DigitalFilterImplementation {
136 InputValueList _rateOfChangeMax;
137 InputValueList _rateOfChangeMin ;
138 bool configure( SGPropertyNode& cfg_node,
139 const std::string& cfg_name,
140 SGPropertyNode& prop_root );
142 RateLimitFilterImplementation();
143 double compute( double dt, double input );
144 virtual void initialize( double initvalue );
147 class IntegratorFilterImplementation : public GainFilterImplementation {
149 InputValueList _TfInput;
150 InputValueList _minInput;
151 InputValueList _maxInput;
154 bool configure( SGPropertyNode& cfg_node,
155 const std::string& cfg_name,
156 SGPropertyNode& prop_root );
158 IntegratorFilterImplementation();
159 double compute( double dt, double input );
160 virtual void initialize( double initvalue );
163 class HighPassFilterImplementation : public GainFilterImplementation {
165 InputValueList _TfInput;
168 bool configure( SGPropertyNode& cfg_node,
169 const std::string& cfg_name,
170 SGPropertyNode& prop_root );
172 HighPassFilterImplementation();
173 double compute( double dt, double input );
174 virtual void initialize( double initvalue );
176 class LeadLagFilterImplementation : public GainFilterImplementation {
178 InputValueList _TfaInput;
179 InputValueList _TfbInput;
182 bool configure( SGPropertyNode& cfg_node,
183 const std::string& cfg_name,
184 SGPropertyNode& prop_root );
186 LeadLagFilterImplementation();
187 double compute( double dt, double input );
188 virtual void initialize( double initvalue );
190 /* --------------------------------------------------------------------------------- */
191 /* --------------------------------------------------------------------------------- */
193 } // namespace FGXMLAutopilot
195 using namespace FGXMLAutopilot;
197 /* --------------------------------------------------------------------------------- */
198 /* --------------------------------------------------------------------------------- */
199 DigitalFilterImplementation::DigitalFilterImplementation() :
204 //------------------------------------------------------------------------------
205 bool DigitalFilterImplementation::configure( SGPropertyNode& cfg,
206 SGPropertyNode& prop_root )
208 for (int i = 0; i < cfg.nChildren(); ++i )
210 SGPropertyNode_ptr child = cfg.getChild(i);
212 if( configure(*child, child->getNameString(), prop_root) )
219 /* --------------------------------------------------------------------------------- */
220 /* --------------------------------------------------------------------------------- */
222 double GainFilterImplementation::compute( double dt, double input )
224 return _gainInput.get_value() * input;
227 bool GainFilterImplementation::configure( SGPropertyNode& cfg_node,
228 const std::string& cfg_name,
229 SGPropertyNode& prop_root )
231 if (cfg_name == "gain" ) {
232 _gainInput.push_back( new InputValue(prop_root, cfg_node, 1) );
239 /* --------------------------------------------------------------------------------- */
240 /* --------------------------------------------------------------------------------- */
242 double ReciprocalFilterImplementation::compute( double dt, double input )
244 if( input >= -SGLimitsd::min() && input <= SGLimitsd::min() )
245 return SGLimitsd::max();
247 return _gainInput.get_value() / input;
251 /* --------------------------------------------------------------------------------- */
252 /* --------------------------------------------------------------------------------- */
254 DerivativeFilterImplementation::DerivativeFilterImplementation() :
259 void DerivativeFilterImplementation::initialize( double initvalue )
261 _input_1 = initvalue;
264 //------------------------------------------------------------------------------
265 bool DerivativeFilterImplementation::configure( SGPropertyNode& cfg_node,
266 const std::string& cfg_name,
267 SGPropertyNode& prop_root )
269 if( GainFilterImplementation::configure(cfg_node, cfg_name, prop_root) )
272 if (cfg_name == "filter-time" ) {
273 _TfInput.push_back( new InputValue(prop_root, cfg_node, 1) );
280 double DerivativeFilterImplementation::compute( double dt, double input )
282 double output = (input - _input_1) * _TfInput.get_value() * _gainInput.get_value() / dt;
288 /* --------------------------------------------------------------------------------- */
289 /* --------------------------------------------------------------------------------- */
291 MovingAverageFilterImplementation::MovingAverageFilterImplementation() :
296 void MovingAverageFilterImplementation::initialize( double initvalue )
298 _output_1 = initvalue;
301 double MovingAverageFilterImplementation::compute( double dt, double input )
303 typedef std::deque<double>::size_type size_type;
304 size_type samples = _samplesInput.get_value();
306 if (_inputQueue.size() != samples) {
307 // For constant size filters, this code executed once.
308 bool shrunk = _inputQueue.size() > samples;
309 _inputQueue.resize(samples, _output_1);
312 for (size_type ii = 0; ii < samples; ii++)
313 _output_1 += _inputQueue[ii];
314 _output_1 /= samples;
318 double output_0 = _output_1 + (input - _inputQueue.back()) / samples;
320 _output_1 = output_0;
321 _inputQueue.pop_back();
322 _inputQueue.push_front(input);
326 bool MovingAverageFilterImplementation::configure( SGPropertyNode& cfg_node,
327 const std::string& cfg_name,
328 SGPropertyNode& prop_root )
330 if (cfg_name == "samples" ) {
331 _samplesInput.push_back( new InputValue(prop_root, cfg_node, 1) );
338 /* --------------------------------------------------------------------------------- */
339 /* --------------------------------------------------------------------------------- */
341 NoiseSpikeFilterImplementation::NoiseSpikeFilterImplementation() :
346 void NoiseSpikeFilterImplementation::initialize( double initvalue )
348 _output_1 = initvalue;
351 double NoiseSpikeFilterImplementation::compute( double dt, double input )
353 double delta = input - _output_1;
354 if( fabs(delta) <= SGLimitsd::min() ) return input; // trivial
356 double maxChange = _rateOfChangeInput.get_value() * dt;
357 const PeriodicalValue * periodical = _digitalFilter->getPeriodicalValue();
358 if( periodical ) delta = periodical->normalizeSymmetric( delta );
360 if( fabs(delta) <= maxChange )
361 return (_output_1 = input);
363 return (_output_1 = _output_1 + copysign( maxChange, delta ));
366 //------------------------------------------------------------------------------
367 bool NoiseSpikeFilterImplementation::configure( SGPropertyNode& cfg_node,
368 const std::string& cfg_name,
369 SGPropertyNode& prop_root )
371 if (cfg_name == "max-rate-of-change" ) {
372 _rateOfChangeInput.push_back( new InputValue(prop_root, cfg_node, 1) );
379 /* --------------------------------------------------------------------------------- */
381 RateLimitFilterImplementation::RateLimitFilterImplementation() :
386 void RateLimitFilterImplementation::initialize( double initvalue )
388 _output_1 = initvalue;
391 double RateLimitFilterImplementation::compute( double dt, double input )
393 double delta = input - _output_1;
396 if( fabs(delta) <= SGLimitsd::min() ) return input; // trivial
398 double maxChange = _rateOfChangeMax.get_value() * dt;
399 double minChange = _rateOfChangeMin.get_value() * dt;
400 // const PeriodicalValue * periodical = _digitalFilter->getPeriodicalValue();
401 // if( periodical ) delta = periodical->normalizeSymmetric( delta );
404 if(delta >= maxChange ) output = _output_1 + maxChange;
405 if(delta <= minChange ) output = _output_1 + minChange;
411 bool RateLimitFilterImplementation::configure( SGPropertyNode& cfg_node,
412 const std::string& cfg_name,
413 SGPropertyNode& prop_root )
415 if (cfg_name == "max-rate-of-change" ) {
416 _rateOfChangeMax.push_back( new InputValue(prop_root, cfg_node, 1) );
419 if (cfg_name == "min-rate-of-change" ) {
420 _rateOfChangeMin.push_back( new InputValue(prop_root, cfg_node, 1) );
427 /* --------------------------------------------------------------------------------- */
428 /* --------------------------------------------------------------------------------- */
430 ExponentialFilterImplementation::ExponentialFilterImplementation()
431 : _isSecondOrder(false),
437 void ExponentialFilterImplementation::initialize( double initvalue )
439 _output_1 = _output_2 = initvalue;
442 double ExponentialFilterImplementation::compute( double dt, double input )
444 input = GainFilterImplementation::compute( dt, input );
445 double tf = _TfInput.get_value();
449 // avoid negative filter times
450 // and div by zero if -tf == dt
452 double alpha = tf > 0.0 ? 1 / ((tf/dt) + 1) : 1.0;
455 output_0 = alpha * alpha * input +
456 2 * (1 - alpha) * _output_1 -
457 (1 - alpha) * (1 - alpha) * _output_2;
459 output_0 = alpha * input + (1 - alpha) * _output_1;
461 _output_2 = _output_1;
462 return (_output_1 = output_0);
465 //------------------------------------------------------------------------------
466 bool ExponentialFilterImplementation::configure( SGPropertyNode& cfg_node,
467 const std::string& cfg_name,
468 SGPropertyNode& prop_root )
470 if( GainFilterImplementation::configure(cfg_node, cfg_name, prop_root) )
473 if (cfg_name == "filter-time" ) {
474 _TfInput.push_back( new InputValue(prop_root, cfg_node, 1) );
478 if (cfg_name == "type" ) {
479 string type(cfg_node.getStringValue());
480 _isSecondOrder = type == "double-exponential";
486 /* --------------------------------------------------------------------------------- */
488 IntegratorFilterImplementation::IntegratorFilterImplementation() :
494 void IntegratorFilterImplementation::initialize( double initvalue )
496 _input_1 = _output_1 = initvalue;
499 //------------------------------------------------------------------------------
500 bool IntegratorFilterImplementation::configure( SGPropertyNode& cfg_node,
501 const std::string& cfg_name,
502 SGPropertyNode& prop_root )
504 if( GainFilterImplementation::configure(cfg_node, cfg_name, prop_root) )
507 if (cfg_name == "u_min" ) {
508 _minInput.push_back( new InputValue(prop_root, cfg_node, 1) );
511 if (cfg_name == "u_max" ) {
512 _maxInput.push_back( new InputValue(prop_root, cfg_node, 1) );
518 double IntegratorFilterImplementation::compute( double dt, double input )
520 double output = _output_1 + input * _gainInput.get_value() * dt;
521 double u_min = _minInput.get_value();
522 double u_max = _maxInput.get_value();
523 if (output >= u_max) output = u_max; // clamping inside "::compute" prevents integrator wind-up
524 if (output <= u_min) output = u_min;
531 /* --------------------------------------------------------------------------------- */
533 HighPassFilterImplementation::HighPassFilterImplementation() :
540 void HighPassFilterImplementation::initialize( double initvalue )
542 _input_1 = initvalue;
543 _output_1 = initvalue;
546 double HighPassFilterImplementation::compute( double dt, double input )
548 input = GainFilterImplementation::compute( dt, input );
549 double tf = _TfInput.get_value();
553 // avoid negative filter times
554 // and div by zero if -tf == dt
556 double alpha = tf > 0.0 ? 1 / ((tf/dt) + 1) : 1.0;
557 output = (1 - alpha) * (input - _input_1 + _output_1);
563 //------------------------------------------------------------------------------
564 bool HighPassFilterImplementation::configure( SGPropertyNode& cfg_node,
565 const std::string& cfg_name,
566 SGPropertyNode& prop_root )
568 if( GainFilterImplementation::configure(cfg_node, cfg_name, prop_root) )
571 if (cfg_name == "filter-time" ) {
572 _TfInput.push_back( new InputValue(prop_root, cfg_node, 1) );
579 /* --------------------------------------------------------------------------------- */
581 LeadLagFilterImplementation::LeadLagFilterImplementation() :
588 void LeadLagFilterImplementation::initialize( double initvalue )
590 _input_1 = initvalue;
591 _output_1 = initvalue;
594 double LeadLagFilterImplementation::compute( double dt, double input )
596 input = GainFilterImplementation::compute( dt, input );
597 double tfa = _TfaInput.get_value();
598 double tfb = _TfbInput.get_value();
602 // avoid negative filter times
603 // and div by zero if -tf == dt
605 double alpha = tfa > 0.0 ? 1 / ((tfa/dt) + 1) : 1.0;
606 double beta = tfb > 0.0 ? 1 / ((tfb/dt) + 1) : 1.0;
607 output = (1 - beta) * (input / (1 - alpha) - _input_1 + _output_1);
613 //------------------------------------------------------------------------------
614 bool LeadLagFilterImplementation::configure( SGPropertyNode& cfg_node,
615 const std::string& cfg_name,
616 SGPropertyNode& prop_root )
618 if( GainFilterImplementation::configure(cfg_node, cfg_name, prop_root) )
621 if (cfg_name == "filter-time-a" ) {
622 _TfaInput.push_back( new InputValue(prop_root, cfg_node, 1) );
625 if (cfg_name == "filter-time-b" ) {
626 _TfbInput.push_back( new InputValue(prop_root, cfg_node, 1) );
631 /* -------------------------------------------------------------------------- */
632 /* Digital Filter Component Implementation */
633 /* -------------------------------------------------------------------------- */
635 DigitalFilter::DigitalFilter() :
637 _initializeTo(INITIALIZE_INPUT)
641 DigitalFilter::~DigitalFilter()
646 static map<string,FunctorBase<DigitalFilterImplementation> *> componentForge;
648 //------------------------------------------------------------------------------
649 bool DigitalFilter::configure( SGPropertyNode& cfg_node,
650 const std::string& cfg_name,
651 SGPropertyNode& prop_root )
653 if( componentForge.empty() ) {
654 componentForge["gain"] = new CreateAndConfigureFunctor<GainFilterImplementation,DigitalFilterImplementation>();
655 componentForge["exponential"] = new CreateAndConfigureFunctor<ExponentialFilterImplementation,DigitalFilterImplementation>();
656 componentForge["double-exponential"] = new CreateAndConfigureFunctor<ExponentialFilterImplementation,DigitalFilterImplementation>();
657 componentForge["moving-average"] = new CreateAndConfigureFunctor<MovingAverageFilterImplementation,DigitalFilterImplementation>();
658 componentForge["noise-spike"] = new CreateAndConfigureFunctor<NoiseSpikeFilterImplementation,DigitalFilterImplementation>();
659 componentForge["rate-limit"] = new CreateAndConfigureFunctor<RateLimitFilterImplementation,DigitalFilterImplementation>();
660 componentForge["reciprocal"] = new CreateAndConfigureFunctor<ReciprocalFilterImplementation,DigitalFilterImplementation>();
661 componentForge["derivative"] = new CreateAndConfigureFunctor<DerivativeFilterImplementation,DigitalFilterImplementation>();
662 componentForge["high-pass"] = new CreateAndConfigureFunctor<HighPassFilterImplementation,DigitalFilterImplementation>();
663 componentForge["lead-lag"] = new CreateAndConfigureFunctor<LeadLagFilterImplementation,DigitalFilterImplementation>();
664 componentForge["integrator"] = new CreateAndConfigureFunctor<IntegratorFilterImplementation,DigitalFilterImplementation>();
667 SG_LOG(SG_AUTOPILOT, SG_BULK, "DigitalFilter::configure(" << cfg_name << ")");
669 if( AnalogComponent::configure(cfg_node, cfg_name, prop_root) )
672 if (cfg_name == "type" ) {
673 string type( cfg_node.getStringValue() );
674 if( componentForge.count(type) == 0 ) {
675 SG_LOG( SG_AUTOPILOT, SG_BULK, "unhandled filter type <" << type << ">" << endl );
678 _implementation = (*componentForge[type])(*cfg_node.getParent(), prop_root);
679 _implementation->setDigitalFilter( this );
683 if( cfg_name == "initialize-to" ) {
684 string s( cfg_node.getStringValue() );
686 _initializeTo = INITIALIZE_INPUT;
687 } else if( s == "output" ) {
688 _initializeTo = INITIALIZE_OUTPUT;
689 } else if( s == "none" ) {
690 _initializeTo = INITIALIZE_NONE;
692 SG_LOG( SG_AUTOPILOT, SG_WARN, "unhandled initialize-to value '" << s << "' ignored" );
701 "DigitalFilter::configure(" << cfg_name << ") [unhandled]"
703 return false; // not handled by us, let the base class try
706 void DigitalFilter::update( bool firstTime, double dt)
708 if( _implementation == NULL ) return;
711 switch( _initializeTo ) {
713 case INITIALIZE_INPUT:
714 SG_LOG(SG_AUTOPILOT,SG_DEBUG, "First time initialization of " << get_name() << " to " << _valueInput.get_value() );
715 _implementation->initialize( _valueInput.get_value() );
718 case INITIALIZE_OUTPUT:
719 SG_LOG(SG_AUTOPILOT,SG_DEBUG, "First time initialization of " << get_name() << " to " << get_output_value() );
720 _implementation->initialize( get_output_value() );
724 SG_LOG(SG_AUTOPILOT,SG_DEBUG, "First time initialization of " << get_name() << " to (uninitialized)" );
729 double input = _valueInput.get_value() - _referenceInput.get_value();
730 double output = _implementation->compute( dt, input );
732 set_output_value( output );
735 cout << "input:" << input
736 << "\toutput:" << output << endl;