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