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