]> git.mxchange.org Git - flightgear.git/blobdiff - src/Autopilot/flipflop.cxx
Merge commit 'refs/merge-requests/1579' of git://gitorious.org/fg/flightgear into...
[flightgear.git] / src / Autopilot / flipflop.cxx
index bfd9a3af7dcb34b196d7127999557018876b7de8..484f89d0588b67dbfdede9af479bf8bde0db003b 100644 (file)
 #include "inputvalue.hxx"
 #include <Main/fg_props.hxx>
 
-using namespace FGXMLAutopilot;
-
+using std::map;
+using std::string;
+using std::endl;
+using std::cout;
+
+namespace FGXMLAutopilot {
+
+/**
+ * @brief Flip flop implementation for a RS flip flop with dominant RESET
+ *
+ * RS (reset-set) flip flops act as a fundamental latch. It has two input lines, 
+ * S (set) and R (reset). Activating the set input sets the output while activating
+ * the reset input resets the output. If both inputs are activated, the output
+ * is deactivated, too. This is why the RESET line is called dominant. Use a
+ * SRFlipFlopImplementation for a dominant SET line.
+ *
+ * <table>
+ * <tr>
+ * <td colspan="3">Logictable</td>
+ * </tr>
+ * <tr>
+ *   <td>S</td><td>R</td><td>Q</td>
+ * </tr>
+ * <tr>
+ *   <td>false</td><td>false</td><td>unchanged</td>
+ * </tr>
+ * <tr>
+ *   <td>false</td><td>true</td><td>false</td>
+ * </tr>
+ * <tr>
+ *   <td>true</td><td>false</td><td>true</td>
+ * </tr>
+ * <tr>
+ *   <td>true</td><td>true</td><td>false</td>
+ * </tr>
+ * </table>
+ */
 class RSFlipFlopImplementation : public FlipFlopImplementation {
 protected:
   bool _rIsDominant;
@@ -34,61 +69,237 @@ public:
   virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
 };
 
+/**
+ * @brief Flip flop implementation for a RS flip flop with dominant SET
+ *
+ * SR (set-reset) flip flops act as a fundamental latch. It has two input lines, 
+ * S (set) and R (reset). Activating the set input sets the output while activating
+ * the reset input resets the output. If both inputs are activated, the output
+ * is activated, too. This is why the SET line is called dominant. Use a
+ * RSFlipFlopImplementation for a dominant RESET line.
+ * 
+ * <table>
+ * <tr>
+ * <td colspan="3">Logictable</td>
+ * </tr>
+ * <tr>
+ *   <td>S</td><td>R</td><td>Q</td>
+ * </tr>
+ * <tr>
+ *   <td>false</td><td>false</td><td>unchanged</td>
+ * </tr>
+ * <tr>
+ *   <td>false</td><td>true</td><td>false</td>
+ * </tr>
+ * <tr>
+ *   <td>true</td><td>false</td><td>true</td>
+ * </tr>
+ * <tr>
+ *   <td>true</td><td>true</td><td>true</td>
+ * </tr>
+ * </table>
+ */
 class SRFlipFlopImplementation : public RSFlipFlopImplementation {
 public:
   SRFlipFlopImplementation() : RSFlipFlopImplementation( false ) {}
 };
 
+/**
+ * @brief Base class for clocked flip flop implementation
+ *
+ * A clocked flip flop computes it's output on the raising edge (false/true transition)
+ * of the clock input. If such a transition is detected, the onRaisingEdge method is called
+ * by this implementation. All clocked flip flops inherit from the RS flip flop and may
+ * be set or reset by the respective set/reset lines. Note that the RS implementation 
+ * ignores the clock, The output is set immediately, regardless of the state of the clock
+ * input. The "clock" input is mandatory for clocked flip flops.
+ * 
+ */
 class ClockedFlipFlopImplementation : public RSFlipFlopImplementation {
-protected:
+private:
+  /** 
+   * @brief the previous state of the clock input 
+   */
   bool _clock;
+protected:
+
+  /**
+   * @brief pure virtual function to be implemented from the implementing class, gets called
+   * from the update method if the raising edge of the clock input was detected. 
+   * @param input a map of named input lines
+   * @param q a reference to a boolean variable to receive the output state
+   * @return true if the state has changed, false otherwise
+   */
   virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) = 0;
 public:
+
+  /**
+   * @brief constructor for a ClockedFlipFlopImplementation
+   * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
+   */
   ClockedFlipFlopImplementation( bool rIsDominant = true ) : RSFlipFlopImplementation( rIsDominant ), _clock(false) {}
+
+  /**
+   * @brief evaluates the output state from the input lines. 
+   * This method basically waits for a raising edge and calls onRaisingEdge
+   * @param dt the elapsed time in seconds from since the last call
+   * @param input a map of named input lines
+   * @param q a reference to a boolean variable to receive the output state
+   * @return true if the state has changed, false otherwise
+   */
   virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
 };
 
+/**
+ * @brief Implements a JK flip flop as a clocked flip flop
+ *
+ * The JK flip flop has five input lines: R, S, clock, J and K. The R and S lines work as described
+ * in the RS flip flop. Setting the J line to true sets the output to true on the next raising
+ * edge of the clock line. Setting the K line to true sets the output to false on the next raising
+ * edge of the clock line. If both, J and K are true, the output is toggled at with every raising
+ * edge of the clock line. 
+ *
+ * Undefined inputs default to false.
+ *
+ * <table>
+ * <tr>
+ * <td colspan="7">Logictable</td>
+ * </tr>
+ * <tr>
+ *   <td>S</td><td>R</td><td>J</td><td>K</td><td>clock</td><td>Q (previous)</td><td>Q</td>
+ * </tr>
+ * <tr>
+ *   <td>false</td><td>false</td><td>false</td><td>false</td><td>any</td><td>any</td><td>unchanged</td>
+ * </tr>
+ * <tr>
+ *   <td>true</td><td>false</td><td>any</td><td>any</td><td>any</td><td>any</td><td>true</td>
+ * </tr>
+ * <tr>
+ *   <td>any</td><td>true</td><td>any</td><td>any</td><td>any</td><td>any</td><td>false</td>
+ * </tr>
+ * <tr>
+ *   <td>false</td><td>false</td><td>true</td><td>false</td><td>^</td><td>any</td><td>true</td>
+ * </tr>
+ * <tr>
+ *   <td>false</td><td>false</td><td>false</td><td>true</td><td>^</td><td>any</td><td>false</td>
+ * </tr>
+ * <tr>
+ *   <td>false</td><td>false</td><td>true</td><td>true</td><td>^</td><td>false</td><td>true</td>
+ * </tr>
+ * <tr>
+ *   <td>false</td><td>false</td><td>true</td><td>true</td><td>^</td><td>true</td><td>false</td>
+ * </tr>
+ * </table>
+ */
 class JKFlipFlopImplementation : public ClockedFlipFlopImplementation {
 public:
+  /**
+   * @brief constructor for a JKFlipFlopImplementation
+   * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
+   */
   JKFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
+
+  /**
+   * @brief compute the output state according to the logic table on the raising edge of the clock
+   * @param input a map of named input lines
+   * @param q a reference to a boolean variable to receive the output state
+   * @return true if the state has changed, false otherwise
+   */
   virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q );
 };
 
+/** 
+ * @brief Implements a D (delay) flip flop.
+ *
+ */
 class DFlipFlopImplementation : public ClockedFlipFlopImplementation {
 public:
+  /**
+   * @brief constructor for a DFlipFlopImplementation
+   * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
+   */
   DFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
+
+  /**
+   * @brief compute the output state according to the logic table on the raising edge of the clock
+   * @param input a map of named input lines
+   * @param q a reference to a boolean variable to receive the output state
+   * @return true if the state has changed, false otherwise
+   */
   virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) {
     q = input.get_value("D");
     return true;
   }
 };
 
+/** 
+ * @brief Implements a T (toggle) flip flop.
+ *
+ */
 class TFlipFlopImplementation : public ClockedFlipFlopImplementation {
 public:
+  /**
+   * @brief constructor for a TFlipFlopImplementation
+   * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
+   */
   TFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
+
+  /**
+   * @brief compute the output state according to the logic table on the raising edge of the clock
+   * @param input a map of named input lines
+   * @param q a reference to a boolean variable to receive the output state
+   * @return true if the state has changed, false otherwise
+   */
   virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) {
     q = !q;
     return true;
   }
 };
 
+/** 
+ * @brief Implements a monostable flip flop
+ *
+ * The stable output state is false.
+ *
+ */
 class MonoFlopImplementation : public JKFlipFlopImplementation {
 protected:
-  virtual bool configure( const std::string & nodeName, SGPropertyNode_ptr configNode );
+  virtual bool configure( SGPropertyNode& cfg_node,
+                          const std::string& cfg_name,
+                          SGPropertyNode& prop_root );
   InputValueList _time;
   double _t;
 public:
-  MonoFlopImplementation( bool rIsDominant = true ) : JKFlipFlopImplementation( rIsDominant ) {}
+  /**
+   * @brief constructor for a MonoFlopImplementation
+   * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
+   */
+  MonoFlopImplementation( bool rIsDominant = true ) : JKFlipFlopImplementation( rIsDominant ), _t(0.0) {}
+  /**
+   * @brief evaluates the output state from the input lines and returns to the stable state 
+   * after expiry of the internal timer
+   * @param dt the elapsed time in seconds from since the last call
+   * @param input a map of named input lines
+   * @param q a reference to a boolean variable to receive the output state
+   * @return true if the state has changed, false otherwise
+   */
   virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
 };
 
-bool MonoFlopImplementation::configure( const std::string & nodeName, SGPropertyNode_ptr configNode )
+} // namespace
+
+using namespace FGXMLAutopilot;
+
+//------------------------------------------------------------------------------
+bool MonoFlopImplementation::configure( SGPropertyNode& cfg_node,
+                                        const std::string& cfg_name,
+                                        SGPropertyNode& prop_root )
 {
-  if( JKFlipFlopImplementation::configure( nodeName, configNode ) )
+  if( JKFlipFlopImplementation::configure(cfg_node, cfg_name, prop_root) )
     return true;
 
-  if (nodeName == "time") {
-    _time.push_back( new InputValue( configNode ) );
+  if (cfg_name == "time") {
+    _time.push_back( new InputValue(prop_root, cfg_node) );
     return true;
   } 
 
@@ -133,14 +344,15 @@ bool RSFlipFlopImplementation::getState( double dt, DigitalComponent::InputMap i
 
 bool ClockedFlipFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
 {
-  if( RSFlipFlopImplementation::getState( dt, input, q ) )
-    return true;
-
   bool c = input.get_value("clock");
   bool raisingEdge = c && !_clock;
     
   _clock = c;
 
+  if( RSFlipFlopImplementation::getState( dt, input, q ) )
+    return true;
+
+
   if( !raisingEdge ) return false; //signal no change
   return onRaisingEdge( input, q );
 }
@@ -164,18 +376,18 @@ bool JKFlipFlopImplementation::onRaisingEdge( DigitalComponent::InputMap input,
   return false; // signal no change
 }
 
-bool FlipFlopImplementation::configure( SGPropertyNode_ptr configNode )
+//------------------------------------------------------------------------------
+bool FlipFlopImplementation::configure( SGPropertyNode& prop_root,
+                                        SGPropertyNode& cfg )
 {
-  for (int i = 0; i < configNode->nChildren(); ++i ) {
-    SGPropertyNode_ptr prop;
-
-    SGPropertyNode_ptr child = configNode->getChild(i);
+  for( int i = 0; i < cfg.nChildren(); ++i )
+  {
+    SGPropertyNode_ptr child = cfg.getChild(i);
     string cname(child->getName());
 
-    if( configure( cname, child ) )
+    if( configure(*child, cname, prop_root) )
       continue;
-
-  } // for configNode->nChildren()
+  }
 
   return true;
 }
@@ -183,7 +395,10 @@ bool FlipFlopImplementation::configure( SGPropertyNode_ptr configNode )
 
 static map<string,FunctorBase<FlipFlopImplementation> *> componentForge;
 
-bool FlipFlop::configure( const std::string & nodeName, SGPropertyNode_ptr configNode ) 
+//------------------------------------------------------------------------------
+bool FlipFlop::configure( SGPropertyNode& cfg_node,
+                          const std::string& cfg_name,
+                          SGPropertyNode& prop_root )
 { 
   if( componentForge.empty() ) {
     componentForge["RS"] = new CreateAndConfigureFunctor<RSFlipFlopImplementation,FlipFlopImplementation>();
@@ -194,46 +409,51 @@ bool FlipFlop::configure( const std::string & nodeName, SGPropertyNode_ptr confi
     componentForge["monostable"]  = new CreateAndConfigureFunctor<MonoFlopImplementation, FlipFlopImplementation>();
   }
 
-  if( DigitalComponent::configure( nodeName, configNode ) )
+  if( DigitalComponent::configure(cfg_node, cfg_name, prop_root) )
     return true;
 
-  if( nodeName == "type" ) {
-    string type(configNode->getStringValue());
+  if( cfg_name == "type" ) {
+    string type(cfg_node.getStringValue());
     if( componentForge.count(type) == 0 ) {
-      SG_LOG( SG_AUTOPILOT, SG_BULK, "unhandled flip-flop type <" << type << ">" << endl );
+      SG_LOG
+      (
+        SG_AUTOPILOT,
+        SG_BULK,
+        "unhandled flip-flop type <" << type << ">"
+      );
       return true;
     }
-    _implementation = (*componentForge[type])( configNode->getParent() );
+    _implementation = (*componentForge[type])(prop_root, *cfg_node.getParent());
     return true;
   }
 
-  if (nodeName == "set"||nodeName == "S") {
-    _input["S"] = sgReadCondition( fgGetNode("/"), configNode );
+  if (cfg_name == "set"||cfg_name == "S") {
+    _input["S"] = sgReadCondition(&prop_root, &cfg_node);
     return true;
   }
 
-  if (nodeName == "reset" || nodeName == "R" ) {
-    _input["R"] = sgReadCondition( fgGetNode("/"), configNode );
+  if (cfg_name == "reset" || cfg_name == "R" ) {
+    _input["R"] = sgReadCondition(&prop_root, &cfg_node);
     return true;
   } 
 
-  if (nodeName == "J") {
-    _input["J"] = sgReadCondition( fgGetNode("/"), configNode );
+  if (cfg_name == "J") {
+    _input["J"] = sgReadCondition(&prop_root, &cfg_node);
     return true;
   } 
 
-  if (nodeName == "K") {
-    _input["K"] = sgReadCondition( fgGetNode("/"), configNode );
+  if (cfg_name == "K") {
+    _input["K"] = sgReadCondition(&prop_root, &cfg_node);
     return true;
   } 
 
-  if (nodeName == "D") {
-    _input["D"] = sgReadCondition( fgGetNode("/"), configNode );
+  if (cfg_name == "D") {
+    _input["D"] = sgReadCondition(&prop_root, &cfg_node);
     return true;
   } 
 
-  if (nodeName == "clock") {
-    _input["clock"] = sgReadCondition( fgGetNode("/"), configNode );
+  if (cfg_name == "clock") {
+    _input["clock"] = sgReadCondition(&prop_root, &cfg_node);
     return true;
   }
 
@@ -251,13 +471,13 @@ void FlipFlop::update( bool firstTime, double dt )
 
   q0 = q = get_output();
 
-  if( _implementation->getState( dt, _input, q ) ) {
+  if( _implementation->getState( dt, _input, q ) && q0 != q ) {
     set_output( q );
 
     if(_debug) {
       cout << "updating flip-flop \"" << get_name() << "\"" << endl;
       cout << "prev. Output:" << q0 << endl;
-      for( InputMap::const_iterator it = _input.begin(); it != _input.end(); it++ ) 
+      for( InputMap::const_iterator it = _input.begin(); it != _input.end(); ++it ) 
         cout << "Input \"" << (*it).first << "\":" << (*it).second->test() << endl;
       cout << "new Output:" << q << endl;
     }