]> git.mxchange.org Git - flightgear.git/blob - src/Autopilot/flipflop.cxx
Clean-up cmake (linker) dependencies.
[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( const std::string & nodeName, SGPropertyNode_ptr configNode );
268   InputValueList _time;
269   double _t;
270 public:
271   /**
272    * @brief constructor for a MonoFlopImplementation
273    * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
274    */
275   MonoFlopImplementation( bool rIsDominant = true ) : JKFlipFlopImplementation( rIsDominant ) {}
276   /**
277    * @brief evaluates the output state from the input lines and returns to the stable state 
278    * after expiry of the internal timer
279    * @param dt the elapsed time in seconds from since the last call
280    * @param input a map of named input lines
281    * @param q a reference to a boolean variable to receive the output state
282    * @return true if the state has changed, false otherwise
283    */
284   virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
285 };
286
287 } // namespace
288
289 using namespace FGXMLAutopilot;
290
291 bool MonoFlopImplementation::configure( const std::string & nodeName, SGPropertyNode_ptr configNode )
292 {
293   if( JKFlipFlopImplementation::configure( nodeName, configNode ) )
294     return true;
295
296   if (nodeName == "time") {
297     _time.push_back( new InputValue( configNode ) );
298     return true;
299   } 
300
301   return false;
302 }
303
304 bool MonoFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
305 {
306   if( JKFlipFlopImplementation::getState( dt, input, q ) ) {
307     _t = q ? _time.get_value() : 0;
308     return true;
309   }
310
311   _t -= dt;
312   if( _t <= 0.0 ) {
313     q = 0;
314     return true;
315   }
316
317   return false;
318 }
319
320
321 bool RSFlipFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
322 {
323   bool s = input.get_value("S");
324   bool r = input.get_value("R");
325
326   // s == false && q == false: no change, keep state
327   if( s || r ) {
328     if( _rIsDominant ) { // RS: reset is dominant
329       if( s ) q = true; // set
330       if( r ) q = false; // reset
331     } else { // SR: set is dominant
332       if( r ) q = false; // reset
333       if( s ) q = true; // set
334     }
335     return true; // signal state changed
336   }
337   return false; // signal state unchagned
338 }
339
340 bool ClockedFlipFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
341 {
342   bool c = input.get_value("clock");
343   bool raisingEdge = c && !_clock;
344     
345   _clock = c;
346
347   if( RSFlipFlopImplementation::getState( dt, input, q ) )
348     return true;
349
350
351   if( !raisingEdge ) return false; //signal no change
352   return onRaisingEdge( input, q );
353 }
354
355 bool JKFlipFlopImplementation::onRaisingEdge( DigitalComponent::InputMap input, bool & q )
356 {
357   bool j = input.get_value("J");
358   bool k = input.get_value("K");
359     
360   // j == false && k == false: no change, keep state
361   if( (j || k) ) {
362     if( j && k ) {
363       q = !q; // toggle
364     } else {
365       if( j ) q = true;  // set
366       if( k ) q = false; // reset
367     }
368     return true; // signal state changed
369   }
370
371   return false; // signal no change
372 }
373
374 bool FlipFlopImplementation::configure( SGPropertyNode_ptr configNode )
375 {
376   for (int i = 0; i < configNode->nChildren(); ++i ) {
377     SGPropertyNode_ptr prop;
378
379     SGPropertyNode_ptr child = configNode->getChild(i);
380     string cname(child->getName());
381
382     if( configure( cname, child ) )
383       continue;
384
385   } // for configNode->nChildren()
386
387   return true;
388 }
389
390
391 static map<string,FunctorBase<FlipFlopImplementation> *> componentForge;
392
393 bool FlipFlop::configure( const std::string & nodeName, SGPropertyNode_ptr configNode ) 
394
395   if( componentForge.empty() ) {
396     componentForge["RS"] = new CreateAndConfigureFunctor<RSFlipFlopImplementation,FlipFlopImplementation>();
397     componentForge["SR"] = new CreateAndConfigureFunctor<SRFlipFlopImplementation,FlipFlopImplementation>();
398     componentForge["JK"] = new CreateAndConfigureFunctor<JKFlipFlopImplementation,FlipFlopImplementation>();
399     componentForge["D"]  = new CreateAndConfigureFunctor<DFlipFlopImplementation, FlipFlopImplementation>();
400     componentForge["T"]  = new CreateAndConfigureFunctor<TFlipFlopImplementation, FlipFlopImplementation>();
401     componentForge["monostable"]  = new CreateAndConfigureFunctor<MonoFlopImplementation, FlipFlopImplementation>();
402   }
403
404   if( DigitalComponent::configure( nodeName, configNode ) )
405     return true;
406
407   if( nodeName == "type" ) {
408     string type(configNode->getStringValue());
409     if( componentForge.count(type) == 0 ) {
410       SG_LOG( SG_AUTOPILOT, SG_BULK, "unhandled flip-flop type <" << type << ">" << endl );
411       return true;
412     }
413     _implementation = (*componentForge[type])( configNode->getParent() );
414     return true;
415   }
416
417   if (nodeName == "set"||nodeName == "S") {
418     _input["S"] = sgReadCondition( fgGetNode("/"), configNode );
419     return true;
420   }
421
422   if (nodeName == "reset" || nodeName == "R" ) {
423     _input["R"] = sgReadCondition( fgGetNode("/"), configNode );
424     return true;
425   } 
426
427   if (nodeName == "J") {
428     _input["J"] = sgReadCondition( fgGetNode("/"), configNode );
429     return true;
430   } 
431
432   if (nodeName == "K") {
433     _input["K"] = sgReadCondition( fgGetNode("/"), configNode );
434     return true;
435   } 
436
437   if (nodeName == "D") {
438     _input["D"] = sgReadCondition( fgGetNode("/"), configNode );
439     return true;
440   } 
441
442   if (nodeName == "clock") {
443     _input["clock"] = sgReadCondition( fgGetNode("/"), configNode );
444     return true;
445   }
446
447   return false; 
448 }
449
450 void FlipFlop::update( bool firstTime, double dt )
451 {
452   if( _implementation == NULL ) {
453     SG_LOG( SG_AUTOPILOT, SG_ALERT, "No flip-flop implementation for " << get_name() << endl );
454     return;
455   }
456
457   bool q0, q;
458
459   q0 = q = get_output();
460
461   if( _implementation->getState( dt, _input, q ) && q0 != q ) {
462     set_output( q );
463
464     if(_debug) {
465       cout << "updating flip-flop \"" << get_name() << "\"" << endl;
466       cout << "prev. Output:" << q0 << endl;
467       for( InputMap::const_iterator it = _input.begin(); it != _input.end(); it++ ) 
468         cout << "Input \"" << (*it).first << "\":" << (*it).second->test() << endl;
469       cout << "new Output:" << q << endl;
470     }
471   }
472 }
473
474