]> git.mxchange.org Git - flightgear.git/blob - src/Autopilot/flipflop.cxx
Some code documentation added to the A/P flip flop implementation
[flightgear.git] / src / Autopilot / flipflop.cxx
1 // flipflop.hxx - implementation of multiple flip flop types
2 //
3 // Written by Torsten Dreyer
4 //
5 // Copyright (C) 2010  Torsten Dreyer - Torsten (at) t3r (dot) de
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
22 #include "flipflop.hxx"
23 #include "functor.hxx"
24 #include "inputvalue.hxx"
25 #include <Main/fg_props.hxx>
26
27 using namespace FGXMLAutopilot;
28
29 /**
30  * @brief Flip flop implementation for a RS flip flop with dominant RESET
31  *
32  * <table>
33  * <tr>
34  * <td colspan="3">Logictable</td>
35  * </tr>
36  * <tr>
37  *   <td>S</td><td>R</td><td>Q</td>
38  * </tr>
39  * <tr>
40  *   <td>false</td><td>false</td><td>unchanged</td>
41  * </tr>
42  * <tr>
43  *   <td>false</td><td>true</td><td>false</td>
44  * </tr>
45  * <tr>
46  *   <td>true</td><td>false</td><td>true</td>
47  * </tr>
48  * <tr>
49  *   <td>true</td><td>true</td><td>false</td>
50  * </tr>
51  * </table>
52  */
53 class RSFlipFlopImplementation : public FlipFlopImplementation {
54 protected:
55   bool _rIsDominant;
56 public:
57   RSFlipFlopImplementation( bool rIsDominant = true ) : _rIsDominant( rIsDominant ) {}
58   virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
59 };
60
61 /**
62  * @brief Flip flop implementation for a RS flip flop with dominant SET
63  *
64  * <table>
65  * <tr>
66  * <td colspan="3">Logictable</td>
67  * </tr>
68  * <tr>
69  *   <td>S</td><td>R</td><td>Q</td>
70  * </tr>
71  * <tr>
72  *   <td>false</td><td>false</td><td>unchanged</td>
73  * </tr>
74  * <tr>
75  *   <td>false</td><td>true</td><td>false</td>
76  * </tr>
77  * <tr>
78  *   <td>true</td><td>false</td><td>true</td>
79  * </tr>
80  * <tr>
81  *   <td>true</td><td>true</td><td>true</td>
82  * </tr>
83  * </table>
84  */
85 class SRFlipFlopImplementation : public RSFlipFlopImplementation {
86 public:
87   SRFlipFlopImplementation() : RSFlipFlopImplementation( false ) {}
88 };
89
90 /**
91  * @brief Base class for clocked flip flop implementation
92  *
93  * A clocked flip flop computes it's output on the raising edge (false/true transition)
94  * of the clock input. If such a transition is detected, the onRaisingEdge method is called
95  * by this implementation
96  */
97 class ClockedFlipFlopImplementation : public RSFlipFlopImplementation {
98 private:
99   /** 
100    * @brief the previous state of the clock input 
101    */
102   bool _clock;
103 protected:
104
105   /**
106    * @brief pure virtual function to be implemented from the implementing class, gets called
107    * from the update method if the raising edge of the clock input was detected. 
108    * @param input a map of named input lines
109    * @param q a reference to a boolean variable to receive the output state
110    * @return true if the state has changed, false otherwise
111    */
112   virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) = 0;
113 public:
114
115   /**
116    * @brief constructor for a ClockedFlipFlopImplementation
117    * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
118    */
119   ClockedFlipFlopImplementation( bool rIsDominant = true ) : RSFlipFlopImplementation( rIsDominant ), _clock(false) {}
120
121   /**
122    * @brief evaluates the output state from the input lines, basically waits for a raising edge and calls onRaisingEdge
123    * @param dt the elapsed time in seconds from since the last call
124    * @param input a map of named input lines
125    * @param q a reference to a boolean variable to receive the output state
126    * @return true if the state has changed, false otherwise
127    */
128   virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
129 };
130
131 class JKFlipFlopImplementation : public ClockedFlipFlopImplementation {
132 public:
133   JKFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
134   virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q );
135 };
136
137 class DFlipFlopImplementation : public ClockedFlipFlopImplementation {
138 public:
139   DFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
140   virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) {
141     q = input.get_value("D");
142     return true;
143   }
144 };
145
146 class TFlipFlopImplementation : public ClockedFlipFlopImplementation {
147 public:
148   TFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
149   virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) {
150     q = !q;
151     return true;
152   }
153 };
154
155 class MonoFlopImplementation : public JKFlipFlopImplementation {
156 protected:
157   virtual bool configure( const std::string & nodeName, SGPropertyNode_ptr configNode );
158   InputValueList _time;
159   double _t;
160 public:
161   MonoFlopImplementation( bool rIsDominant = true ) : JKFlipFlopImplementation( rIsDominant ) {}
162   virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
163 };
164
165 bool MonoFlopImplementation::configure( const std::string & nodeName, SGPropertyNode_ptr configNode )
166 {
167   if( JKFlipFlopImplementation::configure( nodeName, configNode ) )
168     return true;
169
170   if (nodeName == "time") {
171     _time.push_back( new InputValue( configNode ) );
172     return true;
173   } 
174
175   return false;
176 }
177
178 bool MonoFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
179 {
180   if( JKFlipFlopImplementation::getState( dt, input, q ) ) {
181     _t = q ? _time.get_value() : 0;
182     return true;
183   }
184
185   _t -= dt;
186   if( _t <= 0.0 ) {
187     q = 0;
188     return true;
189   }
190
191   return false;
192 }
193
194
195 bool RSFlipFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
196 {
197   bool s = input.get_value("S");
198   bool r = input.get_value("R");
199
200   // s == false && q == false: no change, keep state
201   if( s || r ) {
202     if( _rIsDominant ) { // RS: reset is dominant
203       if( s ) q = true; // set
204       if( r ) q = false; // reset
205     } else { // SR: set is dominant
206       if( r ) q = false; // reset
207       if( s ) q = true; // set
208     }
209     return true; // signal state changed
210   }
211   return false; // signal state unchagned
212 }
213
214 bool ClockedFlipFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
215 {
216   if( RSFlipFlopImplementation::getState( dt, input, q ) )
217     return true;
218
219   bool c = input.get_value("clock");
220   bool raisingEdge = c && !_clock;
221     
222   _clock = c;
223
224   if( !raisingEdge ) return false; //signal no change
225   return onRaisingEdge( input, q );
226 }
227
228 bool JKFlipFlopImplementation::onRaisingEdge( DigitalComponent::InputMap input, bool & q )
229 {
230   bool j = input.get_value("J");
231   bool k = input.get_value("K");
232     
233   // j == false && k == false: no change, keep state
234   if( (j || k) ) {
235     if( j && k ) {
236       q = !q; // toggle
237     } else {
238       if( j ) q = true;  // set
239       if( k ) q = false; // reset
240     }
241     return true; // signal state changed
242   }
243
244   return false; // signal no change
245 }
246
247 bool FlipFlopImplementation::configure( SGPropertyNode_ptr configNode )
248 {
249   for (int i = 0; i < configNode->nChildren(); ++i ) {
250     SGPropertyNode_ptr prop;
251
252     SGPropertyNode_ptr child = configNode->getChild(i);
253     string cname(child->getName());
254
255     if( configure( cname, child ) )
256       continue;
257
258   } // for configNode->nChildren()
259
260   return true;
261 }
262
263
264 static map<string,FunctorBase<FlipFlopImplementation> *> componentForge;
265
266 bool FlipFlop::configure( const std::string & nodeName, SGPropertyNode_ptr configNode ) 
267
268   if( componentForge.empty() ) {
269     componentForge["RS"] = new CreateAndConfigureFunctor<RSFlipFlopImplementation,FlipFlopImplementation>();
270     componentForge["SR"] = new CreateAndConfigureFunctor<SRFlipFlopImplementation,FlipFlopImplementation>();
271     componentForge["JK"] = new CreateAndConfigureFunctor<JKFlipFlopImplementation,FlipFlopImplementation>();
272     componentForge["D"]  = new CreateAndConfigureFunctor<DFlipFlopImplementation, FlipFlopImplementation>();
273     componentForge["T"]  = new CreateAndConfigureFunctor<TFlipFlopImplementation, FlipFlopImplementation>();
274     componentForge["monostable"]  = new CreateAndConfigureFunctor<MonoFlopImplementation, FlipFlopImplementation>();
275   }
276
277   if( DigitalComponent::configure( nodeName, configNode ) )
278     return true;
279
280   if( nodeName == "type" ) {
281     string type(configNode->getStringValue());
282     if( componentForge.count(type) == 0 ) {
283       SG_LOG( SG_AUTOPILOT, SG_BULK, "unhandled flip-flop type <" << type << ">" << endl );
284       return true;
285     }
286     _implementation = (*componentForge[type])( configNode->getParent() );
287     return true;
288   }
289
290   if (nodeName == "set"||nodeName == "S") {
291     _input["S"] = sgReadCondition( fgGetNode("/"), configNode );
292     return true;
293   }
294
295   if (nodeName == "reset" || nodeName == "R" ) {
296     _input["R"] = sgReadCondition( fgGetNode("/"), configNode );
297     return true;
298   } 
299
300   if (nodeName == "J") {
301     _input["J"] = sgReadCondition( fgGetNode("/"), configNode );
302     return true;
303   } 
304
305   if (nodeName == "K") {
306     _input["K"] = sgReadCondition( fgGetNode("/"), configNode );
307     return true;
308   } 
309
310   if (nodeName == "D") {
311     _input["D"] = sgReadCondition( fgGetNode("/"), configNode );
312     return true;
313   } 
314
315   if (nodeName == "clock") {
316     _input["clock"] = sgReadCondition( fgGetNode("/"), configNode );
317     return true;
318   }
319
320   return false; 
321 }
322
323 void FlipFlop::update( bool firstTime, double dt )
324 {
325   if( _implementation == NULL ) {
326     SG_LOG( SG_AUTOPILOT, SG_ALERT, "No flip-flop implementation for " << get_name() << endl );
327     return;
328   }
329
330   bool q0, q;
331
332   q0 = q = get_output();
333
334   if( _implementation->getState( dt, _input, q ) ) {
335     set_output( q );
336
337     if(_debug) {
338       cout << "updating flip-flop \"" << get_name() << "\"" << endl;
339       cout << "prev. Output:" << q0 << endl;
340       for( InputMap::const_iterator it = _input.begin(); it != _input.end(); it++ ) 
341         cout << "Input \"" << (*it).first << "\":" << (*it).second->test() << endl;
342       cout << "new Output:" << q << endl;
343     }
344   }
345 }
346
347