]> git.mxchange.org Git - flightgear.git/blob - src/Autopilot/flipflop.cxx
Cleanup, no functional change
[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 std::map;
28 using std::string;
29 using std::endl;
30 using std::cout;
31
32 namespace FGXMLAutopilot {
33
34 /**
35  * @brief Flip flop implementation for a RS flip flop with dominant RESET
36  *
37  * RS (reset-set) flip flops act as a fundamental latch. It has two input lines, 
38  * S (set) and R (reset). Activating the set input sets the output while activating
39  * the reset input resets the output. If both inputs are activated, the output
40  * is deactivated, too. This is why the RESET line is called dominant. Use a
41  * SRFlipFlopImplementation for a dominant SET line.
42  *
43  * <table>
44  * <tr>
45  * <td colspan="3">Logictable</td>
46  * </tr>
47  * <tr>
48  *   <td>S</td><td>R</td><td>Q</td>
49  * </tr>
50  * <tr>
51  *   <td>false</td><td>false</td><td>unchanged</td>
52  * </tr>
53  * <tr>
54  *   <td>false</td><td>true</td><td>false</td>
55  * </tr>
56  * <tr>
57  *   <td>true</td><td>false</td><td>true</td>
58  * </tr>
59  * <tr>
60  *   <td>true</td><td>true</td><td>false</td>
61  * </tr>
62  * </table>
63  */
64 class RSFlipFlopImplementation : public FlipFlopImplementation {
65 protected:
66   bool _rIsDominant;
67 public:
68   RSFlipFlopImplementation( bool rIsDominant = true ) : _rIsDominant( rIsDominant ) {}
69   virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
70 };
71
72 /**
73  * @brief Flip flop implementation for a RS flip flop with dominant SET
74  *
75  * SR (set-reset) flip flops act as a fundamental latch. It has two input lines, 
76  * S (set) and R (reset). Activating the set input sets the output while activating
77  * the reset input resets the output. If both inputs are activated, the output
78  * is activated, too. This is why the SET line is called dominant. Use a
79  * RSFlipFlopImplementation for a dominant RESET line.
80  * 
81  * <table>
82  * <tr>
83  * <td colspan="3">Logictable</td>
84  * </tr>
85  * <tr>
86  *   <td>S</td><td>R</td><td>Q</td>
87  * </tr>
88  * <tr>
89  *   <td>false</td><td>false</td><td>unchanged</td>
90  * </tr>
91  * <tr>
92  *   <td>false</td><td>true</td><td>false</td>
93  * </tr>
94  * <tr>
95  *   <td>true</td><td>false</td><td>true</td>
96  * </tr>
97  * <tr>
98  *   <td>true</td><td>true</td><td>true</td>
99  * </tr>
100  * </table>
101  */
102 class SRFlipFlopImplementation : public RSFlipFlopImplementation {
103 public:
104   SRFlipFlopImplementation() : RSFlipFlopImplementation( false ) {}
105 };
106
107 /**
108  * @brief Base class for clocked flip flop implementation
109  *
110  * A clocked flip flop computes it's output on the raising edge (false/true transition)
111  * of the clock input. If such a transition is detected, the onRaisingEdge method is called
112  * by this implementation. All clocked flip flops inherit from the RS flip flop and may
113  * be set or reset by the respective set/reset lines. Note that the RS implementation 
114  * ignores the clock, The output is set immediately, regardless of the state of the clock
115  * input. The "clock" input is mandatory for clocked flip flops.
116  * 
117  */
118 class ClockedFlipFlopImplementation : public RSFlipFlopImplementation {
119 private:
120   /** 
121    * @brief the previous state of the clock input 
122    */
123   bool _clock;
124 protected:
125
126   /**
127    * @brief pure virtual function to be implemented from the implementing class, gets called
128    * from the update method if the raising edge of the clock input was detected. 
129    * @param input a map of named input lines
130    * @param q a reference to a boolean variable to receive the output state
131    * @return true if the state has changed, false otherwise
132    */
133   virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) = 0;
134 public:
135
136   /**
137    * @brief constructor for a ClockedFlipFlopImplementation
138    * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
139    */
140   ClockedFlipFlopImplementation( bool rIsDominant = true ) : RSFlipFlopImplementation( rIsDominant ), _clock(false) {}
141
142   /**
143    * @brief evaluates the output state from the input lines. 
144    * This method basically waits for a raising edge and calls onRaisingEdge
145    * @param dt the elapsed time in seconds from since the last call
146    * @param input a map of named input lines
147    * @param q a reference to a boolean variable to receive the output state
148    * @return true if the state has changed, false otherwise
149    */
150   virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
151 };
152
153 /**
154  * @brief Implements a JK flip flop as a clocked flip flop
155  *
156  * The JK flip flop has five input lines: R, S, clock, J and K. The R and S lines work as described
157  * in the RS flip flop. Setting the J line to true sets the output to true on the next raising
158  * edge of the clock line. Setting the K line to true sets the output to false on the next raising
159  * edge of the clock line. If both, J and K are true, the output is toggled at with every raising
160  * edge of the clock line. 
161  *
162  * Undefined inputs default to false.
163  *
164  * <table>
165  * <tr>
166  * <td colspan="7">Logictable</td>
167  * </tr>
168  * <tr>
169  *   <td>S</td><td>R</td><td>J</td><td>K</td><td>clock</td><td>Q (previous)</td><td>Q</td>
170  * </tr>
171  * <tr>
172  *   <td>false</td><td>false</td><td>false</td><td>false</td><td>any</td><td>any</td><td>unchanged</td>
173  * </tr>
174  * <tr>
175  *   <td>true</td><td>false</td><td>any</td><td>any</td><td>any</td><td>any</td><td>true</td>
176  * </tr>
177  * <tr>
178  *   <td>any</td><td>true</td><td>any</td><td>any</td><td>any</td><td>any</td><td>false</td>
179  * </tr>
180  * <tr>
181  *   <td>false</td><td>false</td><td>true</td><td>false</td><td>^</td><td>any</td><td>true</td>
182  * </tr>
183  * <tr>
184  *   <td>false</td><td>false</td><td>false</td><td>true</td><td>^</td><td>any</td><td>false</td>
185  * </tr>
186  * <tr>
187  *   <td>false</td><td>false</td><td>true</td><td>true</td><td>^</td><td>false</td><td>true</td>
188  * </tr>
189  * <tr>
190  *   <td>false</td><td>false</td><td>true</td><td>true</td><td>^</td><td>true</td><td>false</td>
191  * </tr>
192  * </table>
193  */
194 class JKFlipFlopImplementation : public ClockedFlipFlopImplementation {
195 public:
196   /**
197    * @brief constructor for a JKFlipFlopImplementation
198    * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
199    */
200   JKFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
201
202   /**
203    * @brief compute the output state according to the logic table on the raising edge of the clock
204    * @param input a map of named input lines
205    * @param q a reference to a boolean variable to receive the output state
206    * @return true if the state has changed, false otherwise
207    */
208   virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q );
209 };
210
211 /** 
212  * @brief Implements a D (delay) flip flop.
213  *
214  */
215 class DFlipFlopImplementation : public ClockedFlipFlopImplementation {
216 public:
217   /**
218    * @brief constructor for a DFlipFlopImplementation
219    * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
220    */
221   DFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
222
223   /**
224    * @brief compute the output state according to the logic table on the raising edge of the clock
225    * @param input a map of named input lines
226    * @param q a reference to a boolean variable to receive the output state
227    * @return true if the state has changed, false otherwise
228    */
229   virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) {
230     q = input.get_value("D");
231     return true;
232   }
233 };
234
235 /** 
236  * @brief Implements a T (toggle) flip flop.
237  *
238  */
239 class TFlipFlopImplementation : public ClockedFlipFlopImplementation {
240 public:
241   /**
242    * @brief constructor for a TFlipFlopImplementation
243    * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
244    */
245   TFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
246
247   /**
248    * @brief compute the output state according to the logic table on the raising edge of the clock
249    * @param input a map of named input lines
250    * @param q a reference to a boolean variable to receive the output state
251    * @return true if the state has changed, false otherwise
252    */
253   virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) {
254     q = !q;
255     return true;
256   }
257 };
258
259 /** 
260  * @brief Implements a monostable flip flop
261  *
262  * The stable output state is false.
263  *
264  */
265 class MonoFlopImplementation : public JKFlipFlopImplementation {
266 protected:
267   virtual bool configure( SGPropertyNode& cfg_node,
268                           const std::string& cfg_name,
269                           SGPropertyNode& prop_root );
270   InputValueList _time;
271   double _t;
272 public:
273   /**
274    * @brief constructor for a MonoFlopImplementation
275    * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
276    */
277   MonoFlopImplementation( bool rIsDominant = true ) : JKFlipFlopImplementation( rIsDominant ), _t(0.0) {}
278   /**
279    * @brief evaluates the output state from the input lines and returns to the stable state 
280    * after expiry of the internal timer
281    * @param dt the elapsed time in seconds from since the last call
282    * @param input a map of named input lines
283    * @param q a reference to a boolean variable to receive the output state
284    * @return true if the state has changed, false otherwise
285    */
286   virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
287 };
288
289 } // namespace
290
291 using namespace FGXMLAutopilot;
292
293 //------------------------------------------------------------------------------
294 bool MonoFlopImplementation::configure( SGPropertyNode& cfg_node,
295                                         const std::string& cfg_name,
296                                         SGPropertyNode& prop_root )
297 {
298   if( JKFlipFlopImplementation::configure(cfg_node, cfg_name, prop_root) )
299     return true;
300
301   if (cfg_name == "time") {
302     _time.push_back( new InputValue(prop_root, cfg_node) );
303     return true;
304   } 
305
306   return false;
307 }
308
309 bool MonoFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
310 {
311   if( JKFlipFlopImplementation::getState( dt, input, q ) ) {
312     _t = q ? _time.get_value() : 0;
313     return true;
314   }
315
316   _t -= dt;
317   if( _t <= 0.0 ) {
318     q = 0;
319     return true;
320   }
321
322   return false;
323 }
324
325
326 bool RSFlipFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
327 {
328   bool s = input.get_value("S");
329   bool r = input.get_value("R");
330
331   // s == false && q == false: no change, keep state
332   if( s || r ) {
333     if( _rIsDominant ) { // RS: reset is dominant
334       if( s ) q = true; // set
335       if( r ) q = false; // reset
336     } else { // SR: set is dominant
337       if( r ) q = false; // reset
338       if( s ) q = true; // set
339     }
340     return true; // signal state changed
341   }
342   return false; // signal state unchagned
343 }
344
345 bool ClockedFlipFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
346 {
347   bool c = input.get_value("clock");
348   bool raisingEdge = c && !_clock;
349     
350   _clock = c;
351
352   if( RSFlipFlopImplementation::getState( dt, input, q ) )
353     return true;
354
355
356   if( !raisingEdge ) return false; //signal no change
357   return onRaisingEdge( input, q );
358 }
359
360 bool JKFlipFlopImplementation::onRaisingEdge( DigitalComponent::InputMap input, bool & q )
361 {
362   bool j = input.get_value("J");
363   bool k = input.get_value("K");
364     
365   // j == false && k == false: no change, keep state
366   if( (j || k) ) {
367     if( j && k ) {
368       q = !q; // toggle
369     } else {
370       if( j ) q = true;  // set
371       if( k ) q = false; // reset
372     }
373     return true; // signal state changed
374   }
375
376   return false; // signal no change
377 }
378
379 //------------------------------------------------------------------------------
380 bool FlipFlopImplementation::configure( SGPropertyNode& prop_root,
381                                         SGPropertyNode& cfg )
382 {
383   for( int i = 0; i < cfg.nChildren(); ++i )
384   {
385     SGPropertyNode_ptr child = cfg.getChild(i);
386     string cname(child->getName());
387
388     if( configure(*child, cname, prop_root) )
389       continue;
390   }
391
392   return true;
393 }
394
395
396 static map<string,FunctorBase<FlipFlopImplementation> *> componentForge;
397
398 //------------------------------------------------------------------------------
399 bool FlipFlop::configure( SGPropertyNode& cfg_node,
400                           const std::string& cfg_name,
401                           SGPropertyNode& prop_root )
402
403   if( componentForge.empty() ) {
404     componentForge["RS"] = new CreateAndConfigureFunctor<RSFlipFlopImplementation,FlipFlopImplementation>();
405     componentForge["SR"] = new CreateAndConfigureFunctor<SRFlipFlopImplementation,FlipFlopImplementation>();
406     componentForge["JK"] = new CreateAndConfigureFunctor<JKFlipFlopImplementation,FlipFlopImplementation>();
407     componentForge["D"]  = new CreateAndConfigureFunctor<DFlipFlopImplementation, FlipFlopImplementation>();
408     componentForge["T"]  = new CreateAndConfigureFunctor<TFlipFlopImplementation, FlipFlopImplementation>();
409     componentForge["monostable"]  = new CreateAndConfigureFunctor<MonoFlopImplementation, FlipFlopImplementation>();
410   }
411
412   if( DigitalComponent::configure(cfg_node, cfg_name, prop_root) )
413     return true;
414
415   if( cfg_name == "type" ) {
416     string type(cfg_node.getStringValue());
417     if( componentForge.count(type) == 0 ) {
418       SG_LOG
419       (
420         SG_AUTOPILOT,
421         SG_BULK,
422         "unhandled flip-flop type <" << type << ">"
423       );
424       return true;
425     }
426     _implementation = (*componentForge[type])(prop_root, *cfg_node.getParent());
427     return true;
428   }
429
430   if (cfg_name == "set"||cfg_name == "S") {
431     _input["S"] = sgReadCondition(&prop_root, &cfg_node);
432     return true;
433   }
434
435   if (cfg_name == "reset" || cfg_name == "R" ) {
436     _input["R"] = sgReadCondition(&prop_root, &cfg_node);
437     return true;
438   } 
439
440   if (cfg_name == "J") {
441     _input["J"] = sgReadCondition(&prop_root, &cfg_node);
442     return true;
443   } 
444
445   if (cfg_name == "K") {
446     _input["K"] = sgReadCondition(&prop_root, &cfg_node);
447     return true;
448   } 
449
450   if (cfg_name == "D") {
451     _input["D"] = sgReadCondition(&prop_root, &cfg_node);
452     return true;
453   } 
454
455   if (cfg_name == "clock") {
456     _input["clock"] = sgReadCondition(&prop_root, &cfg_node);
457     return true;
458   }
459
460   return false; 
461 }
462
463 void FlipFlop::update( bool firstTime, double dt )
464 {
465   if( _implementation == NULL ) {
466     SG_LOG( SG_AUTOPILOT, SG_ALERT, "No flip-flop implementation for " << get_name() << endl );
467     return;
468   }
469
470   bool q0, q;
471
472   q0 = q = get_output();
473
474   if( _implementation->getState( dt, _input, q ) && q0 != q ) {
475     set_output( q );
476
477     if(_debug) {
478       cout << "updating flip-flop \"" << get_name() << "\"" << endl;
479       cout << "prev. Output:" << q0 << endl;
480       for( InputMap::const_iterator it = _input.begin(); it != _input.end(); ++it ) 
481         cout << "Input \"" << (*it).first << "\":" << (*it).second->test() << endl;
482       cout << "new Output:" << q << endl;
483     }
484   }
485 }
486
487