From 9e35d18f2e6dcee58dbc9bbac6615e19eae33eea Mon Sep 17 00:00:00 2001 From: Torsten Dreyer Date: Mon, 24 May 2010 11:57:55 +0200 Subject: [PATCH] Add RS, JK, D and T flip flops as components Flip flops are useful items for data latches and can be used to implement pushbuttons, glideslope locks etc. Check http://en.wikipedia.org/wiki/Flip-flop_(electronics) and http://wiki.flightgear.org/index.php/Autopilot_Configuration_Reference for details --- src/Autopilot/xmlauto.cxx | 247 ++++++++++++++++++++++++++++++++++++++ src/Autopilot/xmlauto.hxx | 30 ++++- 2 files changed, 275 insertions(+), 2 deletions(-) diff --git a/src/Autopilot/xmlauto.cxx b/src/Autopilot/xmlauto.cxx index fe51f37b1..56df5cf86 100644 --- a/src/Autopilot/xmlauto.cxx +++ b/src/Autopilot/xmlauto.cxx @@ -26,6 +26,7 @@ #include +#include #include #include #include @@ -893,6 +894,237 @@ void FGXMLAutoLogic::update(double dt) set_output_value( i ); } +class FGXMLAutoRSFlipFlop : public FGXMLAutoFlipFlop { +public: + FGXMLAutoRSFlipFlop( SGPropertyNode * node ) : + FGXMLAutoFlipFlop( node ) {} + + void updateState( double dt ) { + + if( sInput == NULL ) { + if ( debug ) cout << "No set (S) input for " << get_name() << endl; + return; + } + + if( rInput == NULL ) { + if ( debug ) cout << "No reset (R) input for " << get_name() << endl; + return; + } + + bool s = sInput->test(); + bool r = rInput->test(); + + // s == false && q == false: no change, keep state + if( s || r ) { + bool q = false; + if( s ) q = true; // set + if( r ) q = false; // reset + // s && q: race condition. we let r win + if( inverted ) q = !q; + + if ( debug ) cout << "Updating " << get_name() << ":" + << " s=" << s + << ",r=" << r + << ",q=" << q << endl; + set_output_value( q ); + } else { + if ( debug ) cout << "Updating " << get_name() << ":" + << " s=" << s + << ",r=" << r + << ",q=unchanged" << endl; + } + } +}; + +class FGXMLAutoJKFlipFlop : public FGXMLAutoFlipFlop { +private: + bool clock; +public: + FGXMLAutoJKFlipFlop( SGPropertyNode * node ) : + FGXMLAutoFlipFlop( node ), + clock(false) {} + + void updateState( double dt ) { + + if( jInput == NULL ) { + if ( debug ) cout << "No set (j) input for " << get_name() << endl; + return; + } + + if( kInput == NULL ) { + if ( debug ) cout << "No reset (k) input for " << get_name() << endl; + return; + } + + bool j = jInput->test(); + bool k = kInput->test(); + /* + if the user provided a clock input, use it. + Otherwise use framerate as clock + This JK operates on the raising edge. + */ + bool c = clockInput ? clockInput->test() : false; + bool raisingEdge = clockInput ? (c && !clock) : true; + clock = c; + + if( !raisingEdge ) return; + + bool q = get_bool_output_value(); + // j == false && k == false: no change, keep state + if( (j || k) ) { + if( j && k ) { + q = !q; // toggle + } else { + if( j ) q = true; // set + if( k ) q = false; // reset + } + if( inverted ) q = !q; + + if ( debug ) cout << "Updating " << get_name() << ":" + << " j=" << j + << ",k=" << k + << ",q=" << q << endl; + set_output_value( q ); + } else { + if ( debug ) cout << "Updating " << get_name() << ":" + << " j=" << j + << ",k=" << k + << ",q=unchanged" << endl; + } + } +}; + +class FGXMLAutoDFlipFlop : public FGXMLAutoFlipFlop { +private: + bool clock; +public: + FGXMLAutoDFlipFlop( SGPropertyNode * node ) : + FGXMLAutoFlipFlop( node ), + clock(false) {} + + void updateState( double dt ) { + + if( clockInput == NULL ) { + if ( debug ) cout << "No (clock) input for " << get_name() << endl; + return; + } + + if( dInput == NULL ) { + if ( debug ) cout << "No (D) input for " << get_name() << endl; + return; + } + + bool d = dInput->test(); + + // check the clock - raising edge + bool c = clockInput->test(); + bool raisingEdge = c && !clock; + clock = c; + + if( raisingEdge ) { + bool q = d; + if( inverted ) q = !q; + + if ( debug ) cout << "Updating " << get_name() << ":" + << " d=" << d + << ",q=" << q << endl; + set_output_value( q ); + } else { + if ( debug ) cout << "Updating " << get_name() << ":" + << " d=" << d + << ",q=unchanged" << endl; + } + } +}; + +class FGXMLAutoTFlipFlop : public FGXMLAutoFlipFlop { +private: + bool clock; +public: + FGXMLAutoTFlipFlop( SGPropertyNode * node ) : + FGXMLAutoFlipFlop( node ), + clock(false) {} + + void updateState( double dt ) { + + if( clockInput == NULL ) { + if ( debug ) cout << "No (clock) input for " << get_name() << endl; + return; + } + + // check the clock - raising edge + bool c = clockInput->test(); + bool raisingEdge = c && !clock; + clock = c; + + if( raisingEdge ) { + bool q = !get_bool_output_value(); // toggle + if( inverted ) q = !q; // doesnt really make sense for a T-FF + + if ( debug ) cout << "Updating " << get_name() << ":" + << ",q=" << q << endl; + set_output_value( q ); + } else { + if ( debug ) cout << "Updating " << get_name() << ":" + << ",q=unchanged" << endl; + } + } +}; + +FGXMLAutoFlipFlop::FGXMLAutoFlipFlop(SGPropertyNode * node ) : + FGXMLAutoComponent(), + inverted(false) +{ + parseNode(node); +} + +bool FGXMLAutoFlipFlop::parseNodeHook(const std::string& aName, SGPropertyNode* aNode) +{ + if (aName == "set"||aName == "S") { + sInput = sgReadCondition( fgGetNode("/"), aNode ); + } else if (aName == "reset" || aName == "R" ) { + rInput = sgReadCondition( fgGetNode("/"), aNode ); + } else if (aName == "J") { + jInput = sgReadCondition( fgGetNode("/"), aNode ); + } else if (aName == "K") { + kInput = sgReadCondition( fgGetNode("/"), aNode ); + } else if (aName == "T") { + tInput = sgReadCondition( fgGetNode("/"), aNode ); + } else if (aName == "D") { + dInput = sgReadCondition( fgGetNode("/"), aNode ); + } else if (aName == "clock") { + clockInput = sgReadCondition( fgGetNode("/"), aNode ); + } else if (aName == "inverted") { + inverted = aNode->getBoolValue(); + } else if (aName == "type") { + // ignore element type, evaluated by loader + } else { + return false; + } + + return true; +} + +void FGXMLAutoFlipFlop::update(double dt) +{ + if ( isPropertyEnabled() ) { + if ( !enabled ) { + // we have just been enabled + // initialize to a bool property + set_output_value( get_bool_output_value() ); + } + enabled = true; + } else { + enabled = false; + do_feedback(); + } + + if ( !enabled || dt < SGLimitsd::min() ) + return; + + updateState( dt ); +} + FGXMLAutopilotGroup::FGXMLAutopilotGroup() : SGSubsystemGroup() @@ -1113,6 +1345,21 @@ bool FGXMLAutopilot::build( SGPropertyNode_ptr config_props ) { components.push_back( new FGDigitalFilter( node ) ); } else if ( name == "logic" ) { components.push_back( new FGXMLAutoLogic( node ) ); + } else if ( name == "flipflop" ) { + FGXMLAutoFlipFlop * flipFlop = NULL; + SGPropertyNode_ptr typeNode = node->getNode( "type" ); + string val; + if( typeNode != NULL ) val = typeNode->getStringValue(); + val = simgear::strutils::strip(val); + if( val == "RS" || val =="SR" ) flipFlop = new FGXMLAutoRSFlipFlop( node ); + else if( val == "JK" ) flipFlop = new FGXMLAutoJKFlipFlop( node ); + else if( val == "T" ) flipFlop = new FGXMLAutoTFlipFlop( node ); + else if( val == "D" ) flipFlop = new FGXMLAutoDFlipFlop( node ); + if( flipFlop == NULL ) { + SG_LOG(SG_AUTOPILOT, SG_ALERT, "can't create flipflop of type: " << val); + return false; + } + components.push_back( flipFlop ); } else { SG_LOG( SG_AUTOPILOT, SG_WARN, "Unknown top level autopilot section: " << name ); // return false; diff --git a/src/Autopilot/xmlauto.hxx b/src/Autopilot/xmlauto.hxx index a29d49e23..ccc17ee56 100644 --- a/src/Autopilot/xmlauto.hxx +++ b/src/Autopilot/xmlauto.hxx @@ -222,6 +222,10 @@ public: return output_list.size() == 0 ? 0.0 : clamp(output_list[0]->getDoubleValue()); } + inline bool get_bool_output_value() { + return output_list.size() == 0 ? false : output_list[0]->getBoolValue(); + } + /* Returns true if the enable-condition is true. @@ -381,9 +385,9 @@ private: std::deque output; std::deque input; - enum filterTypes { exponential, doubleExponential, movingAverage, + enum FilterTypes { exponential, doubleExponential, movingAverage, noiseSpike, gain, reciprocal, differential, none }; - filterTypes filterType; + FilterTypes filterType; protected: bool parseNodeHook(const std::string& aName, SGPropertyNode* aNode); @@ -411,6 +415,28 @@ public: void update(double dt); }; +class FGXMLAutoFlipFlop : public FGXMLAutoComponent +{ +private: +protected: + SGSharedPtr sInput; + SGSharedPtr rInput; + SGSharedPtr clockInput; + SGSharedPtr jInput; + SGSharedPtr kInput; + SGSharedPtr tInput; + SGSharedPtr dInput; + bool inverted; + FGXMLAutoFlipFlop( SGPropertyNode * node ); + bool parseNodeHook(const std::string& aName, SGPropertyNode* aNode); + + void update( double dt ); + virtual void updateState( double dt ) = 0; + +public: + ~FGXMLAutoFlipFlop() {}; +}; + /** * Model an autopilot system. * -- 2.39.5