Index: src/acft.py
===================================================================
--- src/acft.py	(revision 7)
+++ src/acft.py	(revision 8)
@@ -3,7 +3,8 @@
 #---------------------------------------------------------------------------------------
 
+import const
+import checks
+
 import time
-
-import const
 
 #---------------------------------------------------------------------------------------
@@ -11,175 +12,87 @@
 class Aircraft(object):
     """Base class for aircraft."""
-    def __init__(self, type):
+    @staticmethod
+    def create(flight):
+        """Create an aircraft instance for the type in the given flight."""
+        return _classes[flight.aircraftType](flight)
+
+    def __init__(self, flight):
         """Construct the aircraft for the given type."""
-        self._type = type
+        self._flight = flight
         self._aircraftState = None
+
+        self._checkers = []
+
+        self._checkers.append(checks.StageChecker())
+
+        self._checkers.append(checks.AltimeterLogger())
+        
+        self._checkers.append(checks.NAV1Logger())
+        self._checkers.append(checks.NAV2Logger())
+        self._checkers.append(checks.SquawkLogger())
+
+        self._checkers.append(checks.AnticollisionLightsLogger())
+        self._checkers.append(checks.LandingLightsLogger())
+        self._checkers.append(checks.StrobeLightsLogger())
+        self._checkers.append(checks.NavLightsLogger())
+
+        self._checkers.append(checks.FlapsLogger())
+
+        self._checkers.append(checks.GearsLogger())
 
     @property
     def type(self):
         """Get the type of the aircraft."""
-        return self._type
-
-    def modelChanged(self, aircraftName, modelName):
+        return self._flight.aircraftType
+
+    @property
+    def flight(self):
+        """Get the flight the aircraft belongs to."""
+        return self._flight
+
+    @property
+    def logger(self):
+        """Get the logger to use for the aircraft."""
+        return self._flight.logger
+
+    def modelChanged(self, timestamp, aircraftName, modelName):
         """Called when the simulator's aircraft changes."""
-        print "Aircraft.modelChanged: aircraftName='%s', modelName='%s'" % \
-            (aircraftName, modelName)
+        self._flight.logger.message(timestamp,
+                                    "Aircraft: name='%s', model='%s'" % \
+                                    (aircraftName, modelName))
 
     def handleState(self, aircraftState):
         """Called when the state of the aircraft changes."""
-        timeStr = time.ctime(aircraftState.timestamp)
-
-        if self._aircraftState is None or \
-           self._aircraftState.paused != aircraftState.paused:
-            print "Aircraft.handleState: %s: paused=%d" % \
-                (timeStr, aircraftState.paused)
-
-        if self._aircraftState is None or \
-           self._aircraftState.trickMode != aircraftState.trickMode:
-            print "Aircraft.handleState: %s: trickMode=%d" % \
-                (timeStr, aircraftState.trickMode)
-
-        if self._aircraftState is None or \
-           self._aircraftState.overspeed != aircraftState.overspeed:
-            print "Aircraft.handleState: %s: overspeed=%d" % \
-                (timeStr, aircraftState.overspeed)
-
-        if self._aircraftState is None or \
-           self._aircraftState.stalled != aircraftState.stalled:
-            print "Aircraft.handleState: %s: stalled=%d" % \
-                (timeStr, aircraftState.stalled)
-
-        if self._aircraftState is None or \
-           self._aircraftState.onTheGround != aircraftState.onTheGround:
-            print "Aircraft.handleState: %s: onTheGround=%d" % \
-                (timeStr, aircraftState.onTheGround)
-
-        if self._aircraftState is None or \
-           self._aircraftState.grossWeight != aircraftState.grossWeight:
-            print "Aircraft.handleState: %s: grossWeight=%f" % \
-                (timeStr, aircraftState.grossWeight)
-
-        if self._aircraftState is None or \
-           self._aircraftState.heading != aircraftState.heading:
-            print "Aircraft.handleState: %s: heading=%f" % \
-                (timeStr, aircraftState.heading)
-
-        if self._aircraftState is None or \
-           self._aircraftState.pitch != aircraftState.pitch:
-            print "Aircraft.handleState: %s: pitch=%f" % \
-                (timeStr, aircraftState.pitch)
-
-        if self._aircraftState is None or \
-           self._aircraftState.bank != aircraftState.bank:
-            print "Aircraft.handleState: %s: bank=%f" % \
-                (timeStr, aircraftState.bank)
-
-        if self._aircraftState is None or \
-           self._aircraftState.ias != aircraftState.ias:
-            print "Aircraft.handleState: %s: ias=%f" % \
-                (timeStr, aircraftState.ias)
-
-        if self._aircraftState is None or \
-           self._aircraftState.groundSpeed != aircraftState.groundSpeed:
-            print "Aircraft.handleState: %s: groundSpeed=%f" % \
-                (timeStr, aircraftState.groundSpeed)
-
-        if self._aircraftState is None or \
-           self._aircraftState.vs != aircraftState.vs:
-            print "Aircraft.handleState: %s: vs=%f" % \
-                (timeStr, aircraftState.vs)
-
-        if self._aircraftState is None or \
-           self._aircraftState.altitude != aircraftState.altitude:
-            print "Aircraft.handleState: %s: altitude=%f" % \
-                (timeStr, aircraftState.altitude)
-
-        if self._aircraftState is None or \
-           self._aircraftState.gLoad != aircraftState.gLoad:
-            print "Aircraft.handleState: %s: gLoad=%f" % \
-                (timeStr, aircraftState.gLoad)
-
-        if self._aircraftState is None or \
-           self._aircraftState.flapsSet != aircraftState.flapsSet:
-            print "Aircraft.handleState: %s: flapsSet=%f" % \
-                (timeStr, aircraftState.flapsSet)
-
-        if self._aircraftState is None or \
-           self._aircraftState.flaps != aircraftState.flaps:
-            print "Aircraft.handleState: %s: flaps=%f" % \
-                (timeStr, aircraftState.flaps)
-
-        if self._aircraftState is None or \
-           self._aircraftState.navLightsOn != aircraftState.navLightsOn:
-            print "Aircraft.handleState: %s: navLightsOn=%d" % \
-                (timeStr, aircraftState.navLightsOn)
-
-        if self._aircraftState is None or \
-           self._aircraftState.antiCollisionLightsOn != aircraftState.antiCollisionLightsOn:
-            print "Aircraft.handleState: %s: antiCollisionLightsOn=%d" % \
-                (timeStr, aircraftState.antiCollisionLightsOn)
-
-        if self._aircraftState is None or \
-           self._aircraftState.strobeLightsOn != aircraftState.strobeLightsOn:
-            print "Aircraft.handleState: %s: strobeLightsOn=%d" % \
-                (timeStr, aircraftState.strobeLightsOn)
-
-        if self._aircraftState is None or \
-           self._aircraftState.landingLightsOn != aircraftState.landingLightsOn:
-            print "Aircraft.handleState: %s: landingLightsOn=%d" % \
-                (timeStr, aircraftState.landingLightsOn)
-
-        if self._aircraftState is None or \
-           self._aircraftState.pitotHeatOn != aircraftState.pitotHeatOn:
-            print "Aircraft.handleState: %s: pitotHeatOn=%d" % \
-                (timeStr, aircraftState.pitotHeatOn)
-
-        if self._aircraftState is None or \
-           self._aircraftState.gearsDown != aircraftState.gearsDown:
-            print "Aircraft.handleState: %s: gearsDown=%f" % \
-                (timeStr, aircraftState.gearsDown)
-
-        if self._aircraftState is None or \
-           self._aircraftState.spoilersArmed != aircraftState.spoilersArmed:
-            print "Aircraft.handleState: %s: spoilersArmed=%f" % \
-                (timeStr, aircraftState.spoilersArmed)
-
-        if self._aircraftState is None or \
-           self._aircraftState.spoilersExtension != aircraftState.spoilersExtension:
-            print "Aircraft.handleState: %s: spoilersExtension=%f" % \
-                (timeStr, aircraftState.spoilersExtension)
-
-        if self._aircraftState is None or \
-           self._aircraftState.altimeter != aircraftState.altimeter:
-            print "Aircraft.handleState: %s: altimeter=%f" % \
-                (timeStr, aircraftState.altimeter)
-
-        if self._aircraftState is None or \
-           self._aircraftState.nav1 != aircraftState.nav1:
-            print "Aircraft.handleState: %s: nav1=%s" % \
-                (timeStr, aircraftState.nav1)
-
-        if self._aircraftState is None or \
-           self._aircraftState.nav2 != aircraftState.nav2:
-            print "Aircraft.handleState: %s: nav2=%s" % \
-                (timeStr, aircraftState.nav2)
-
-        if self._aircraftState is None or \
-           self._aircraftState.fuel != aircraftState.fuel:
-            print "Aircraft.handleState: %s: fuel=%s" % \
-                (timeStr, aircraftState.fuel)
-
-        if self._aircraftState is None or \
-           self._aircraftState.n1 != aircraftState.n1:
-            print "Aircraft.handleState: %s: n1=%s" % \
-                (timeStr, aircraftState.n1)
-
-        if self._aircraftState is None or \
-           self._aircraftState.reverser != aircraftState.reverser:
-            print "Aircraft.handleState: %s: reverser=%s" % \
-                (timeStr, aircraftState.reverser)
+        for checker in self._checkers:
+            checker.check(self._flight, self, self._flight.logger,
+                          self._aircraftState, aircraftState)
 
         self._aircraftState = aircraftState
-            
+    
+    def setStage(self, aircraftState, newStage):
+        """Set the given stage as the new one and do whatever should be
+        done."""
+        self._flight.setStage(aircraftState.timestamp, newStage)
+
+    def flare(self):
+        """Called when it is detected that we are during flare.
+
+        On the first call, it should start monitoring some parameters more
+        closely to determine flare time."""
+        pass
+
+    def cancelFlare(self):
+        """Cancel flare, if it has started."""
+        pass
+
+    def checkFlightEnd(self, aircraftState):
+        """Check if the end of the flight has arrived.
+
+        This default implementation checks the N1 values, but for
+        piston-powered aircraft you need to check the RPMs."""
+        for n1 in aircraftState.n1:
+            if n1>=0.5: return False
+        return True
+
 #---------------------------------------------------------------------------------------
 
@@ -198,6 +111,6 @@
 class B736(Boeing737):
     """Boeing 737-600 aircraft."""
-    def __init__(self):
-        super(B736, self).__init__(const.AIRCRAFT_B736)
+    def __init__(self, flight):
+        super(B736, self).__init__(flight)
 
 #---------------------------------------------------------------------------------------
@@ -205,6 +118,6 @@
 class B737(Boeing737):
     """Boeing 737-700 aircraft."""
-    def __init__(self):
-        super(B737, self).__init__(const.AIRCRAFT_B737)
+    def __init__(self, flight):
+        super(B737, self).__init__(flight)
 
 #---------------------------------------------------------------------------------------
@@ -212,6 +125,6 @@
 class B738(Boeing737):
     """Boeing 737-800 aircraft."""
-    def __init__(self):
-        super(B738, self).__init__(const.AIRCRAFT_B738)
+    def __init__(self, flight):
+        super(B738, self).__init__(flight)
 
 #---------------------------------------------------------------------------------------
@@ -219,6 +132,6 @@
 class B733(Boeing737):
     """Boeing 737-300 aircraft."""
-    def __init__(self):
-        super(B733, self).__init__(const.AIRCRAFT_B733)
+    def __init__(self, flight):
+        super(B733, self).__init__(flight)
 
 #---------------------------------------------------------------------------------------
@@ -226,6 +139,6 @@
 class B734(Boeing737):
     """Boeing 737-400 aircraft."""
-    def __init__(self):
-        super(B734, self).__init__(const.AIRCRAFT_B734)
+    def __init__(self, flight):
+        super(B734, self).__init__(flight)
 
 #---------------------------------------------------------------------------------------
@@ -233,6 +146,6 @@
 class B735(Boeing737):
     """Boeing 737-500 aircraft."""
-    def __init__(self):
-        super(B735, self).__init__(const.AIRCRAFT_B735)
+    def __init__(self, flight):
+        super(B735, self).__init__(flight)
 
 #---------------------------------------------------------------------------------------
@@ -246,6 +159,6 @@
     - n1: left, right
     - reverser: left, right."""
-    def __init__(self):
-        super(DH8D, self).__init__(const.AIRCRAFT_DH8D)
+    def __init__(self, flight):
+        super(DH8D, self).__init__(flight)
 
 #---------------------------------------------------------------------------------------
@@ -264,6 +177,6 @@
 class B762(Boeing767):
     """Boeing 767-200 aircraft."""
-    def __init__(self):
-        super(B762, self).__init__(const.AIRCRAFT_B762)
+    def __init__(self, flight):
+        super(B762, self).__init__(flight)
 
 #---------------------------------------------------------------------------------------
@@ -271,6 +184,6 @@
 class B763(Boeing767):
     """Boeing 767-300 aircraft."""
-    def __init__(self):
-        super(B763, self).__init__(const.AIRCRAFT_B763)
+    def __init__(self, flight):
+        super(B763, self).__init__(cflight)
 
 #---------------------------------------------------------------------------------------
@@ -284,6 +197,6 @@
     - n1: left, right
     - reverser: left, right."""
-    def __init__(self):
-        super(CRJ2, self).__init__(const.AIRCRAFT_CRJ2)
+    def __init__(self, flight):
+        super(CRJ2, self).__init__(flight)
 
 #---------------------------------------------------------------------------------------
@@ -297,6 +210,6 @@
     - n1: left, right
     - reverser: left, right."""
-    def __init__(self):
-        super(F70, self).__init__(const.AIRCRAFT_F70)
+    def __init__(self, flight):
+        super(F70, self).__init__(flight)
 
 #---------------------------------------------------------------------------------------
@@ -310,6 +223,14 @@
     - rpm: left, right
     - reverser: left, right."""
-    def __init__(self):
-        super(DC3, self).__init__(const.AIRCRAFT_DC3)
+    def __init__(self, flight):
+        super(DC3, self).__init__(flight)
+
+    def _checkFlightEnd(self, aircraftState):
+        """Check if the end of the flight has arrived.
+
+        This implementation checks the RPM values to be 0."""
+        for rpm in aircraftState.rpm:
+            if rpm>0: return
+        self._setStage(aircraftState, const.STAGE_END)
 
 #---------------------------------------------------------------------------------------
@@ -324,6 +245,6 @@
     - n1: left, right
     - reverser: left, right."""
-    def __init__(self):
-        super(T134, self).__init__(const.AIRCRAFT_T134)
+    def __init__(self, flight):
+        super(T134, self).__init__(flight)
 
 #---------------------------------------------------------------------------------------
@@ -337,6 +258,6 @@
     - n1: left, centre, right
     - reverser: left, right"""
-    def __init__(self):
-        super(T154, self).__init__(const.AIRCRAFT_T154)
+    def __init__(self, flight):
+        super(T154, self).__init__(flight)
 
 #---------------------------------------------------------------------------------------
@@ -350,7 +271,24 @@
     - n1: left, right
     - reverser: left, right"""
-    def __init__(self):
-        super(YK40, self).__init__(const.AIRCRAFT_YK40)
-
-#---------------------------------------------------------------------------------------
-
+    def __init__(self, flight):
+        super(YK40, self).__init__(flight)
+
+#---------------------------------------------------------------------------------------
+
+_classes = { const.AIRCRAFT_B736 : B736,
+             const.AIRCRAFT_B737 : B737,
+             const.AIRCRAFT_B738 : B738,
+             const.AIRCRAFT_B733 : B733,
+             const.AIRCRAFT_B734 : B734,
+             const.AIRCRAFT_B735 : B735,
+             const.AIRCRAFT_DH8D : DH8D,
+             const.AIRCRAFT_B762 : B762,
+             const.AIRCRAFT_B763 : B763,
+             const.AIRCRAFT_CRJ2 : CRJ2,
+             const.AIRCRAFT_F70 : F70,
+             const.AIRCRAFT_DC3 : DC3,
+             const.AIRCRAFT_T134 : T134,
+             const.AIRCRAFT_T154 : T154,
+             const.AIRCRAFT_YK40 : YK40 }
+
+#---------------------------------------------------------------------------------------
Index: src/checks.py
===================================================================
--- src/checks.py	(revision 8)
+++ src/checks.py	(revision 8)
@@ -0,0 +1,306 @@
+# The various checks that may be performed during flight
+
+#---------------------------------------------------------------------------------------
+
+import const
+
+#---------------------------------------------------------------------------------------
+
+class StateChecker(object):
+    """Base class for classes the instances of which check the aircraft's state
+    from some aspect.
+
+    As a result of the check they may log something, or notify of some fault, etc."""
+    def check(self, flight, aircraft, logger, oldState, state):
+        """Perform the check and do whatever is needed.
+
+        This default implementation raises a NotImplementedError."""
+        raise NotImplementedError()
+    
+#---------------------------------------------------------------------------------------
+
+class StageChecker(StateChecker):
+    """Check the flight stage transitions."""
+    def check(self, flight, aircraft, logger, oldState, state):
+        """Check the stage of the aircraft."""
+        stage = flight.stage
+        if stage==None:
+            aircraft.setStage(state, const.STAGE_BOARDING)
+        elif stage==const.STAGE_BOARDING:
+            if not state.parking or \
+               (not state.trickMode and state.groundSpeed>5.0):
+                aircraft.setStage(state, const.STAGE_PUSHANDTAXI)
+        elif stage==const.STAGE_PUSHANDTAXI or stage==const.STAGE_RTO:
+            if state.landingLightsOn or state.strobeLightsOn or \
+               state.groundSpeed>80.0:
+                aircraft.setStage(state, const.STAGE_TAKEOFF)
+        elif stage==const.STAGE_TAKEOFF:
+            if not state.gearsDown or \
+               (state.radioAltitude>3000.0 and state.vs>0):
+                aircraft.setStage(state, const.STAGE_CLIMB)
+            elif not state.landingLightsOn and \
+                 not state.strobeLightsOn and \
+                 state.onTheGround and \
+                 state.groundSpeed<50.0:
+                aircraft.setStage(state, const.STAGE_RTO)
+        elif stage==const.STAGE_CLIMB:
+            if (state.altitude+2000) > flight.cruiseAltitude:
+                aircraft.setStage(state, const.STAGE_CRUISE)
+            elif state.radioAltitude<2000.0 and \
+                 state.vs < 0.0 and state.gearsDown:
+                aircraft.setStage(state, const.STAGE_LANDING)
+        elif stage==const.STAGE_CRUISE:
+            if (state.altitude+2000) < flight.cruiseAltitude:
+                aircraft.setStage(state, const.STAGE_DESCENT)
+        elif stage==const.STAGE_DESCENT or stage==const.STAGE_GOAROUND:
+            if state.gearsDown and state.radioAltitude<2000.0:
+                aircraft.setStage(state, const.STAGE_LANDING)
+            elif (state.altitude+2000) > flight.cruiseAltitude:
+                aircraft.setStage(state, const.STAGE_CRUISE)
+        elif stage==const.STAGE_LANDING:
+            if state.onTheGround and state.groundSpeed<50.0:
+                aircraft.setStage(state, const.STAGE_TAXIAFTERLAND)
+            elif not state.gearsDown:
+                aircraft.setStage(state, const.STAGE_GOAROUND)
+            elif state.radioAltitude>200:
+                aircraft.cancelFlare()
+            elif state.radioAltitude<150 and not state.onTheGround:
+                aircraft.flare()
+        elif stage==const.STAGE_TAXIAFTERLAND:
+            if state.parking:
+                aircraft.setStage(state, const.STAGE_PARKING)
+        elif stage==const.STAGE_PARKING:
+            if aircraft.checkFlightEnd(state):
+                aircraft.setStage(state, const.STAGE_END)
+
+#---------------------------------------------------------------------------------------
+
+class StateChangeLogger(StateChecker):
+    """Base class for classes the instances of which check if a specific change has
+    occured in the aircraft's state, and log such change."""
+    def __init__(self, logInitial = True):
+        """Construct the logger.
+
+        If logInitial is True, the initial value will be logged, not just the
+        changes later.
+
+        Child classes should define the following functions:
+        - _changed(self, oldState, state): returns a boolean indicating if the
+        value has changed or not
+        - _getMessage(self, state): return a strings containing the message to log
+        with the new value
+        """
+        self._logInitial = logInitial    
+
+    def check(self, flight, aircraft, logger, oldState, state):
+        """Check if the state has changed, and if so, log the new state."""
+        shouldLog = False
+        if oldState is None:
+            shouldLog = self._logInitial
+        else:
+            shouldLog = self._changed(oldState, state)
+        
+        if shouldLog:
+            logger.message(state.timestamp, self._getMessage(state))
+        
+#---------------------------------------------------------------------------------------
+
+class SimpleChangeMixin(object):
+    """A mixin that defines a _changed() function which simply calls a function
+    to retrieve the value two monitor for both states and compares them.
+
+    Child classes should define the following function:
+    - _getValue(state): get the value we are interested in."""
+    def _changed(self, oldState, state):
+        """Determine if the value has changed."""
+        return self._getValue(oldState)!=self._getValue(state)
+
+#---------------------------------------------------------------------------------------
+
+class SingleValueMixin(object):
+    """A mixin that provides a _getValue() function to query a value from the
+    state using the name of the attribute."""
+    def __init__(self, attrName):
+        """Construct the mixin with the given attribute name."""
+        self._attrName = attrName
+
+    def _getValue(self, state):
+        """Get the value of the attribute from the state."""
+        return getattr(state, self._attrName)
+    
+#---------------------------------------------------------------------------------------
+
+class DelayedChangeMixin(object):
+    """A mixin to a StateChangeLogger that stores the old value and reports a
+    change only if the change has been there for a certain amount of time.
+
+    Child classes should define the following function:
+    - _getValue(state): get the value we are interested in."""
+    def __init__(self, delay = 10.0):
+        """Construct the mixin with the given delay in seconds."""
+        self._delay = delay
+        self._oldValue = None
+        self._firstChange = None
+
+    def _changed(self, oldState, state):
+        """Determine if the value has changed."""
+        if self._oldValue is None:
+            self._oldValue = self._getValue(oldState)
+            
+        newValue = self._getValue(state)
+        if newValue!=self._oldValue:
+            if self._firstChange is None:
+                self._firstChange = state.timestamp
+            if state.timestamp >= (self._firstChange + self._delay):
+                self._oldValue = newValue
+                self._firstChange = None
+                return True
+        else:
+            self._firstChange = None
+
+        return False
+
+#---------------------------------------------------------------------------------------
+
+class TemplateMessageMixin(object):
+    """Mixin to generate a message based on a template.
+
+    Child classes should define the following function:
+    - _getValue(state): get the value we are interested in."""
+    def __init__(self, template):
+        """Construct the mixin."""
+        self._template = template
+
+    def _getMessage(self, state):
+        """Get the message."""
+        return self._template % (self._getValue(state),)
+
+#---------------------------------------------------------------------------------------
+
+class GenericStateChangeLogger(StateChangeLogger, SingleValueMixin,
+                               DelayedChangeMixin, TemplateMessageMixin):
+    """Base for generic state change loggers that monitor a single value in the
+    state possibly with a delay and the logged message comes from a template"""
+    def __init__(self, attrName, template, logInitial = True, delay = 0.0):
+        """Construct the object."""
+        StateChangeLogger.__init__(self, logInitial = logInitial)
+        SingleValueMixin.__init__(self, attrName)
+        DelayedChangeMixin.__init__(self, delay = delay)
+        TemplateMessageMixin.__init__(self, template)
+
+#---------------------------------------------------------------------------------------
+
+class AltimeterLogger(StateChangeLogger, SingleValueMixin,
+                      DelayedChangeMixin):
+    """Logger for the altimeter setting."""
+    def __init__(self):
+        """Construct the logger."""
+        StateChangeLogger.__init__(self, logInitial = True)
+        SingleValueMixin.__init__(self, "altimeter")
+        DelayedChangeMixin.__init__(self)
+
+    def _getMessage(self, state):
+        """Get the message to log on a change."""
+        return "Altimeter: %.0f hPa at %.0f feet" % (state.altimeter, state.altitude)
+
+#---------------------------------------------------------------------------------------
+
+class NAV1Logger(GenericStateChangeLogger):
+    """Logger for the NAV1 radio setting."""
+    def __init__(self):
+        """Construct the logger."""
+        super(NAV1Logger, self).__init__("nav1", "NAV1 frequency: %s MHz")
+
+#---------------------------------------------------------------------------------------
+
+class NAV2Logger(GenericStateChangeLogger):
+    """Logger for the NAV2 radio setting."""
+    def __init__(self):
+        """Construct the logger."""
+        super(NAV2Logger, self).__init__("nav2", "NAV2 frequency: %s MHz")
+
+#---------------------------------------------------------------------------------------
+
+class SquawkLogger(GenericStateChangeLogger):
+    """Logger for the squawk setting."""
+    def __init__(self):
+        """Construct the logger."""
+        super(SquawkLogger, self).__init__("squawk", "Squawk code: %s",
+                                           delay = 10.0)
+
+#---------------------------------------------------------------------------------------
+
+class LightsLogger(StateChangeLogger, SingleValueMixin, SimpleChangeMixin):
+    """Base class for the loggers of the various lights."""
+    def __init__(self, attrName, template):
+        """Construct the logger."""
+        StateChangeLogger.__init__(self)
+        SingleValueMixin.__init__(self, attrName)
+        
+        self._template = template
+
+    def _getMessage(self, state):
+        """Get the message from the given state."""
+        return self._template % ("ON" if self._getValue(state) else "OFF")
+        
+#---------------------------------------------------------------------------------------
+
+class AnticollisionLightsLogger(LightsLogger):
+    """Logger for the anti-collision lights."""
+    def __init__(self):
+        LightsLogger.__init__(self, "antiCollisionLightsOn",
+                              "Anti-collision lights: %s")
+
+#---------------------------------------------------------------------------------------
+
+class LandingLightsLogger(LightsLogger):
+    """Logger for the landing lights."""
+    def __init__(self):
+        LightsLogger.__init__(self, "landingLightsOn",
+                              "Landing lights: %s")
+
+#---------------------------------------------------------------------------------------
+
+class StrobeLightsLogger(LightsLogger):
+    """Logger for the strobe lights."""
+    def __init__(self):
+        LightsLogger.__init__(self, "strobeLightsOn",
+                              "Strobe lights: %s")
+
+#---------------------------------------------------------------------------------------
+
+class NavLightsLogger(LightsLogger):
+    """Logger for the navigational lights."""
+    def __init__(self):
+        LightsLogger.__init__(self, "navLightsOn",
+                              "Navigational lights: %s")
+
+#---------------------------------------------------------------------------------------
+
+class FlapsLogger(StateChangeLogger, SingleValueMixin, SimpleChangeMixin):
+    """Logger for the flaps setting."""
+    def __init__(self):
+        """Construct the logger."""
+        StateChangeLogger.__init__(self, logInitial = True)
+        SingleValueMixin.__init__(self, "flapsSet")
+
+    def _getMessage(self, state):
+        """Get the message to log on a change."""
+        speed = state.groundSpeed if state.groundSpeed<80.0 else state.ias
+        return "Flaps set to %.0f at %.0f knots" % (state.flapsSet, speed)
+
+#---------------------------------------------------------------------------------------
+
+class GearsLogger(StateChangeLogger, SingleValueMixin, SimpleChangeMixin):
+    """Logger for the gears state."""
+    def __init__(self):
+        """Construct the logger."""
+        StateChangeLogger.__init__(self, logInitial = True)
+        SingleValueMixin.__init__(self, "gearsDown")
+
+    def _getMessage(self, state):
+        """Get the message to log on a change."""
+        return "Gears %s at %.0f knots, %f feet" % \
+            ("DOWN" if state.gearsDown else "UP", state.ias, state.altitude)
+
+#---------------------------------------------------------------------------------------
Index: src/const.py
===================================================================
--- src/const.py	(revision 7)
+++ src/const.py	(revision 8)
@@ -70,2 +70,39 @@
 AIRCRAFT_YK40 = 15
 
+#-------------------------------------------------------------------------------
+
+# Flight stage: boarding
+STAGE_BOARDING = 1
+
+# Flight stage: pushback, startup and taxi
+STAGE_PUSHANDTAXI = 2
+
+# Flight stage: takeoff
+STAGE_TAKEOFF = 3
+
+# Flight stage: RTO
+STAGE_RTO = 4
+
+# Flight stage: climb
+STAGE_CLIMB = 5
+
+# Flight stage: cruise
+STAGE_CRUISE = 6
+
+# Flight stage: descent
+STAGE_DESCENT = 7
+
+# Flight stage: landing
+STAGE_LANDING = 8
+
+# Flight stage: taxi after landing
+STAGE_TAXIAFTERLAND = 9
+
+# Flight stage: parking
+STAGE_PARKING = 10
+
+# Flight stage: go-around
+STAGE_GOAROUND = 11
+
+# Flight stage: end
+STAGE_END = 12
Index: src/flight.py
===================================================================
--- src/flight.py	(revision 8)
+++ src/flight.py	(revision 8)
@@ -0,0 +1,47 @@
+# Module related to the high-level tracking of the flight
+
+#---------------------------------------------------------------------------------------
+
+import const
+
+import threading
+
+#---------------------------------------------------------------------------------------
+
+class Flight(object):
+    """The object with the global flight state.
+    
+    It is also the hub for the other main objects participating in the handling of
+    the flight."""
+    def __init__(self, logger):
+        """Construct the flight."""
+        self._stage = None
+        self.logger = logger
+        self.cruiseAltitude = None
+        self.aircraftType = None
+        self.aircraft = None
+        self.simulator = None
+
+        self._endCondition = threading.Condition()
+
+    @property
+    def stage(self):
+        """Get the flight stage."""
+        return self._stage
+
+    def setStage(self, timestamp, stage):
+        """Set the flight stage."""
+        if stage!=self._stage:
+            self._stage = stage
+            self.logger.stage(timestamp, stage)
+            if stage==const.STAGE_END:
+                with self._endCondition:
+                    self._endCondition.notify()
+
+    def wait(self):
+        """Wait for the flight to end."""
+        with self._endCondition:
+            while self._stage!=const.STAGE_END:
+                self._endCondition.wait()
+
+#---------------------------------------------------------------------------------------
Index: src/fs.py
===================================================================
--- src/fs.py	(revision 6)
+++ src/fs.py	(revision 8)
@@ -62,4 +62,5 @@
     - groundSpeed: the ground speed (float)
     - vs: the vertical speed in feet/minutes (float)
+    - radioAltitude: the radio altitude of the aircraft in feet (float)
     - altitude: the altitude of the aircraft in feet (float)
     - gLoad: G-load (float)
@@ -82,4 +83,5 @@
     - langingLightsOn: a boolean indicating if the landing lights are on
     - pitotHeatOn: a boolean indicating if the pitot heat is on
+    - parking: a boolean indicating if the parking brake is set
     - gearsDown: a boolean indicating if the gears are down
     - spoilersArmed: a boolean indicating if the spoilers have been armed for
@@ -90,4 +92,5 @@
     - nav1: the frequency of the NAV1 radio in MHz (string)
     - nav2: the frequency of the NAV1 radio in MHz (string)
+    - squawk: the transponder code
 
     FIXME: needed when taxiing only:
Index: src/fsuipc.py
===================================================================
--- src/fsuipc.py	(revision 7)
+++ src/fsuipc.py	(revision 8)
@@ -341,6 +341,12 @@
     via FSUIPC."""
     # The basic data that should be queried all the time once we are connected
-    normalData = [ (0x3d00, -256) ]
-
+    normalData = [ (0x0240, "H"),
+                   (0x023e, "H"),
+                   (0x023b, "b"),
+                   (0x023c, "b"),
+                   (0x023a, "b"),
+                   (0x3d00, -256),
+                   (0x3c00, -256) ]
+    
     def __init__(self, connectionListener, aircraft):
         """Construct the simulator.
@@ -407,5 +413,13 @@
         aircraft-specific values.
         """
-        self._setAircraftName(data[0])
+        timestamp = calendar.timegm(time.struct_time([data[0],
+                                                      1, 1, 0, 0, 0, -1, 1, 0]))
+        timestamp += data[1] * 24 * 3600
+        timestamp += data[2] * 3600
+        timestamp += data[3] * 60
+        timestamp += data[4]        
+
+        createdNewModel = self._setAircraftName(timestamp, data[5], data[6])
+        
         if self._monitoringRequested and not self._monitoring:
             self._monitoring = True
@@ -416,23 +430,36 @@
             self._stopNormal()
             self._startDefaultNormal()
-        elif self._monitoring and self._aircraftModel is not None:
-            aircraftState = self._aircraftModel.getAircraftState(self._aircraft, data)
+        elif self._monitoring and self._aircraftModel is not None and \
+             not createdNewModel:
+            aircraftState = self._aircraftModel.getAircraftState(self._aircraft, 
+                                                                 timestamp, data)
             self._aircraft.handleState(aircraftState)
 
-    def _setAircraftName(self, name):
+    def _setAircraftName(self, timestamp, name, airPath):
         """Set the name of the aicraft and if it is different from the
         previous, create a new model for it.
         
-        If so, also notifty the aircraft about the change."""
-        if name==self._aircraftName:
-            return
-
-        self._aircraftName = name
-        if self._aircraftModel is None or \
-           not self._aircraftModel.doesHandle(name):            
-            self._setAircraftModel(AircraftModel.create(self._aircraft, name))
-        
-        self._aircraft.modelChanged(self._aircraftName, 
-                                    self._aircraftModel.name)
+        If so, also notifty the aircraft about the change.
+
+        Return if a new model was created."""
+        aircraftName = (name, airPath)
+        if aircraftName==self._aircraftName:
+            return False
+
+        self._aircraftName = aircraftName
+        needNew = self._aircraftModel is None
+        needNew = needNew or\
+            not self._aircraftModel.doesHandle(self._aircraft, aircraftName)
+        if not needNew:
+            specialModel = AircraftModel.findSpecial(self._aircraft, aircraftName)
+            needNew = specialModel is not None and \
+                specialModel is not self._aircraftModel.__class__
+
+        if needNew:
+            self._setAircraftModel(AircraftModel.create(self._aircraft, aircraftName))
+        
+        self._aircraft.modelChanged(timestamp, name, self._aircraftModel.name)        
+
+        return needNew
 
     def _setAircraftModel(self, model):
@@ -444,5 +471,5 @@
         
         if self._monitoring:
-            self._handler.clearPeriodic(self._normalRequestID)            
+            self._stopNormal()
             self._startMonitoring()
             
@@ -465,10 +492,5 @@
     Aircraft models handle the data arriving from FSUIPC and turn it into an
     object describing the aircraft's state."""
-    monitoringData = [("year", 0x0240, "H"),
-                      ("dayOfYear", 0x023e, "H"),
-                      ("zuluHour", 0x023b, "b"),
-                      ("zuluMinute", 0x023c, "b"),
-                      ("seconds", 0x023a, "b"),
-                      ("paused", 0x0264, "H"),
+    monitoringData = [("paused", 0x0264, "H"),
                       ("frozen", 0x3364, "H"),
                       ("replay", 0x0628, "d"),
@@ -484,4 +506,5 @@
                       ("groundSpeed", 0x02b4, "d"),
                       ("vs", 0x02c8, "d"),
+                      ("radioAltitude", 0x31e4, "d"),
                       ("altitude", 0x0570, "l"),
                       ("gLoad", 0x11ba, "H"),
@@ -491,4 +514,5 @@
                       ("lights", 0x0d0c, "H"),
                       ("pitot", 0x029c, "b"),
+                      ("parking", 0x0bc8, "H"),
                       ("noseGear", 0x0bec, "d"),
                       ("spoilersArmed", 0x0bcc, "d"),
@@ -496,21 +520,6 @@
                       ("altimeter", 0x0330, "H"),
                       ("nav1", 0x0350, "H"),
-                      ("nav2", 0x0352, "H")]
-
-    genericModels = { const.AIRCRAFT.B736 : B737Model,
-                      const.AIRCRAFT_B737 : B737Model,
-                      const.AIRCRAFT_B738 : B737Model,
-                      const.AIRCRAFT_B733 : B737Model,
-                      const.AIRCRAFT_B734 : B737Model,
-                      const.AIRCRAFT_B735 : B737Model,
-                      const.AIRCRAFT_DH8D : DH8DModel,
-                      const.AIRCRAFT_B762 : B767Model,
-                      const.AIRCRAFT_B763 : B767Model,
-                      const.AIRCRAFT_CRJ2 : B767Model,
-                      const.AIRCRAFT_F79  : F70Model,
-                      const.AIRCRAFT_DC3  : DC3Model,
-                      const.AIRCRAFT_T134 : T134Model,
-                      const.AIRCRAFT_T154 : T154Model,
-                      const.AIRCRAFT_YK40 : YK40Model }
+                      ("nav2", 0x0352, "H"),
+                      ("squawk", 0x0354, "H")]
 
     specialModels = []
@@ -522,27 +531,38 @@
 
     @staticmethod
+    def findSpecial(aircraft, aircraftName):
+        for specialModel in AircraftModel.specialModels:
+            if specialModel.doesHandle(aircraft, aircraftName):
+                return specialModel
+        return None
+
+    @staticmethod
     def create(aircraft, aircraftName):
         """Create the model for the given aircraft name, and notify the
         aircraft about it."""
-        for specialModel in AircraftModel.specialModels:
-            if specialModel.doesHandle(aircraft, aircraftName):
-                return specialModel(aircraft, aircraftName)
-        
-        if aircraft.type in AircraftModel.genericModels:
-            return AircraftModel.genericModels[aircraft.type]()
+        specialModel = AircraftModel.findSpecial(aircraft, aircraftName)
+        if specialModel is not None:
+            return specialModel()
+        if aircraft.type in _genericModels:
+            return _genericModels[aircraft.type]()
         else:
             return GenericModel()
+
+    @staticmethod
+    def convertBCD(data, length):
+        """Convert a data item encoded as BCD into a string of the given number
+        of digits."""
+        bcd = ""
+        for i in range(0, length):
+            digit = chr(ord('0') + (data&0x0f))
+            data >>= 4
+            bcd = digit + bcd
+        return bcd
 
     @staticmethod
     def convertFrequency(data):
         """Convert the given frequency data to a string."""
-        frequency = ""
-        for i in range(0, 4):
-            digit = chr(ord('0') + (data&0x0f))
-            data >>= 4
-            frequency = digit + frequency
-            if i==1:
-                frequency = "." + frequency
-        return "1" + frequency            
+        bcd = AircraftModel.convertBCD(data, 4)
+        return "1" + bcd[0:2] + "." + bcd[2:4]
 
     def __init__(self, flapsNotches):
@@ -557,5 +577,5 @@
         return "FSUIPC/Generic"
     
-    def doesHandle(self, aircraftName):
+    def doesHandle(self, aircraft, aircraftName):
         """Determine if the model handles the given aircraft name.
         
@@ -568,5 +588,5 @@
         dest.append((offset, type))
         if attrName is not None:
-            setattr(self, attrName, len(dest))
+            setattr(self, attrName, len(dest)-1)
 
     def _addDataWithIndexMembers(self, dest, prefix, data):
@@ -586,17 +606,11 @@
     def addMonitoringData(self, data):
         """Add the model-specific monitoring data to the given array."""
-        self.addDataWithIndexMembers(data, "_monidx_",
-                                     AircraftModel.monitoringData)
+        self._addDataWithIndexMembers(data, "_monidx_",
+                                      AircraftModel.monitoringData)
     
-    def getAircraftState(self, aircraft, data):
+    def getAircraftState(self, aircraft, timestamp, data):
         """Get an aircraft state object for the given monitoring data."""
         state = fs.AircraftState()
         
-        timestamp = calendar.timegm(time.struct_time([data[self._monidx_year],
-                                                      1, 1, 0, 0, 0, -1, 1, 0]))
-        timestamp += data[self._monidx_dayOfYear] * 24 * 3600
-        timestamp += data[self._monidx_zuluHour] * 3600
-        timestamp += data[self._monidx_zuluMinute] * 60
-        timestamp += data[self._monidx_seconds]        
         state.timestamp = timestamp
         
@@ -622,4 +636,5 @@
         state.vs = data[self._monidx_vs]*60.0/const.FEETTOMETRES/256.0
 
+        state.radioAltitude = data[self._monidx_radioAltitude]/const.FEETTOMETRES/65536.0
         state.altitude = data[self._monidx_altitude]/const.FEETTOMETRES/65536.0/65536.0
 
@@ -655,4 +670,6 @@
         state.pitotHeatOn = data[self._monidx_pitot]!=0
 
+        state.parking = data[self._monidx_parking]!=0
+
         state.gearsDown = data[self._monidx_noseGear]==16383
 
@@ -669,4 +686,5 @@
         state.nav1 = AircraftModel.convertFrequency(data[self._monidx_nav1])
         state.nav2 = AircraftModel.convertFrequency(data[self._monidx_nav2])
+        state.squawk = AircraftModel.convertBCD(data[self._monidx_squawk], 4)
         
         return state
@@ -699,4 +717,10 @@
         self._isN1 = isN1
 
+    def doesHandle(self, aircraft, aircraftName):
+        """Determine if the model handles the given aircraft name.
+        
+        This implementation returns True."""
+        return True
+
     def addMonitoringData(self, data):
         """Add the model-specific monitoring data to the given array."""
@@ -716,5 +740,5 @@
                 self._addOffsetWithIndexMember(data, 0x088c + i * 0x98, "d")  # throttle lever
         
-    def getAircraftState(self, aircraft, data):
+    def getAircraftState(self, aircraft, timestamp, data):
         """Get the aircraft state.
 
@@ -722,4 +746,5 @@
         the engine parameters."""
         state = super(GenericAircraftModel, self).getAircraftState(aircraft,
+                                                                   timestamp,
                                                                    data)
 
@@ -746,5 +771,5 @@
     def __init__(self):
         """Construct the model."""
-        super(GenericAircraftModel, self).
+        super(GenericModel, self). \
             __init__(flapsNotches = [0, 10, 20, 30],
                      fuelInfo = [0x0b74, 0x0b7c, 0xb94], 
@@ -762,5 +787,5 @@
     def __init__(self):
         """Construct the model."""
-        super(GenericAircraftModel, self).
+        super(B737Model, self). \
             __init__(flapsNotches = [0, 1, 2, 5, 10, 15, 25, 30, 40],
                      fuelInfo = [0x0b74, 0x0b7c, 0xb94], 
@@ -771,4 +796,45 @@
         """Get the name for this aircraft model."""
         return "FSUIPC/Generic Boeing 737"
+
+#------------------------------------------------------------------------------
+
+class PMDGBoeing737NGModel(B737Model):
+    """A model handler for the PMDG Boeing 737NG model."""
+    @staticmethod
+    def doesHandle(aircraft, (name, airPath)):
+        """Determine if this model handler handles the aircraft with the given
+        name."""
+        return aircraft.type in [const.AIRCRAFT_B736,
+                                 const.AIRCRAFT_B737,
+                                 const.AIRCRAFT_B738] and \
+            (name.find("PMDG")!=-1 or airPath.find("PMDG")!=-1) and \
+            (name.find("737")!=-1 or airPath.find("737")!=-1) and \
+            (name.find("600")!=-1 or airPath.find("600")!=-1 or \
+             name.find("700")!=-1 or airPath.find("700")!=-1 or \
+             name.find("800")!=-1 or airPath.find("800")!=-1 or \
+             name.find("900")!=-1 or airPath.find("900")!=-1)
+
+    @property
+    def name(self):
+        """Get the name for this aircraft model."""
+        return "FSUIPC/PMDG Boeing 737NG"
+
+    def addMonitoringData(self, data):
+        """Add the model-specific monitoring data to the given array."""
+        super(PMDGBoeing737NGModel, self).addMonitoringData(data)
+                
+        self._addOffsetWithIndexMember(data, 0x6202, "b", "_pmdgidx_switches")
+
+    def getAircraftState(self, aircraft, timestamp, data):
+        """Get the aircraft state.
+
+        Get it from the parent, and then check some PMDG-specific stuff."""
+        state = super(PMDGBoeing737NGModel, self).getAircraftState(aircraft,
+                                                                   timestamp,
+                                                                   data)
+        if data[self._pmdgidx_switches]&0x01==0x01:
+            state.altimeter = 1013.25
+
+        return state
 
 #------------------------------------------------------------------------------
@@ -778,5 +844,5 @@
     def __init__(self):
         """Construct the model."""
-        super(GenericAircraftModel, self).
+        super(B767Model, self). \
             __init__(flapsNotches = [0, 1, 5, 15, 20, 25, 30],
                      fuelInfo = [0x0b74, 0x0b7c, 0xb94], 
@@ -794,5 +860,5 @@
     def __init__(self):
         """Construct the model."""
-        super(GenericAircraftModel, self).
+        super(DH8DModel, self). \
             __init__(flapsNotches = [0, 5, 10, 15, 35],
                      fuelInfo = [0x0b74, 0x0b7c, 0xb94], 
@@ -810,5 +876,5 @@
     def __init__(self):
         """Construct the model."""
-        super(GenericAircraftModel, self).
+        super(CRJ2Model, self). \
             __init__(flapsNotches = [0, 8, 20, 30, 45],
                      fuelInfo = [0x0b74, 0x0b7c, 0xb94], 
@@ -826,5 +892,5 @@
     def __init__(self):
         """Construct the model."""
-        super(GenericAircraftModel, self).
+        super(F70Model, self). \
             __init__(flapsNotches = [0, 8, 15, 25, 42],
                      fuelInfo = [0x0b74, 0x0b7c, 0xb94], 
@@ -842,5 +908,5 @@
     def __init__(self):
         """Construct the model."""
-        super(GenericAircraftModel, self).
+        super(DC3Model, self). \
             __init__(flapsNotches = [0, 15, 30, 45],
                      fuelInfo = [0x0b7c, 0x0b84, 0x0b94, 0x0b9c], 
@@ -858,5 +924,5 @@
     def __init__(self):
         """Construct the model."""
-        super(GenericAircraftModel, self).
+        super(T134Model, self). \
             __init__(flapsNotches = [0, 10, 20, 30],
                      fuelInfo = [0x0b74, 
@@ -877,8 +943,8 @@
     def __init__(self):
         """Construct the model."""
-        super(GenericAircraftModel, self).
+        super(T154Model, self). \
             __init__(flapsNotches = [0, 15, 28, 45],
                      fuelInfo = [0x0b74, 0x0b7c, 0x0b94, 
-                                 0x1244, 0x0b84, 0x0b9c]
+                                 0x1244, 0x0b84, 0x0b9c],
                      numEngines = 3)
 
@@ -888,9 +954,9 @@
         return "FSUIPC/Generic Tupolev Tu-154"
 
-    def getAircraftState(self, aircraft, data):
+    def getAircraftState(self, aircraft, timestamp, data):
         """Get an aircraft state object for the given monitoring data.
 
         This removes the reverser value for the middle engine."""
-        state = super(T154Model, self).getAircraftState(aircraft, data)
+        state = super(T154Model, self).getAircraftState(aircraft, timestamp, data)
         del state.reverser[1]
         return state
@@ -902,7 +968,7 @@
     def __init__(self):
         """Construct the model."""
-        super(GenericAircraftModel, self).
+        super(YK40Model, self). \
             __init__(flapsNotches = [0, 20, 35],
-                     fuelInfo = [0x0b7c, 0x0b94]
+                     fuelInfo = [0x0b7c, 0x0b94],
                      numEngines = 2)
 
@@ -914,2 +980,24 @@
 #------------------------------------------------------------------------------
 
+_genericModels = { const.AIRCRAFT_B736 : B737Model,
+                   const.AIRCRAFT_B737 : B737Model,
+                   const.AIRCRAFT_B738 : B737Model,
+                   const.AIRCRAFT_B733 : B737Model,
+                   const.AIRCRAFT_B734 : B737Model,
+                   const.AIRCRAFT_B735 : B737Model,
+                   const.AIRCRAFT_DH8D : DH8DModel,
+                   const.AIRCRAFT_B762 : B767Model,
+                   const.AIRCRAFT_B763 : B767Model,
+                   const.AIRCRAFT_CRJ2 : B767Model,
+                   const.AIRCRAFT_F70  : F70Model,
+                   const.AIRCRAFT_DC3  : DC3Model,
+                   const.AIRCRAFT_T134 : T134Model,
+                   const.AIRCRAFT_T154 : T154Model,
+                   const.AIRCRAFT_YK40 : YK40Model }
+
+#------------------------------------------------------------------------------
+
+AircraftModel.registerSpecial(PMDGBoeing737NGModel)
+
+#------------------------------------------------------------------------------
+
Index: src/logger.py
===================================================================
--- src/logger.py	(revision 8)
+++ src/logger.py	(revision 8)
@@ -0,0 +1,64 @@
+# Module for the logging.
+
+#--------------------------------------------------------------------------------------
+
+import const
+
+import sys
+import time
+
+#--------------------------------------------------------------------------------------
+
+class Logger(object):
+    """The class with the interface to log the various events."""
+    _stages = { const.STAGE_BOARDING : "Boarding",
+                const.STAGE_PUSHANDTAXI : "Pushback and Taxi",
+                const.STAGE_TAKEOFF : "Takeoff",
+                const.STAGE_RTO : "RTO",
+                const.STAGE_CLIMB : "Climb",
+                const.STAGE_CRUISE : "Cruise",
+                const.STAGE_DESCENT : "Descent",
+                const.STAGE_LANDING : "Landing",
+                const.STAGE_TAXIAFTERLAND : "Taxi",
+                const.STAGE_PARKING : "Parking",
+                const.STAGE_GOAROUND : "Go-Around",
+                const.STAGE_END : "End" }
+
+    def __init__(self, output = sys.stdout):
+        """Construct the logger."""
+        self._score = 100.0
+        self._output = output
+
+    @staticmethod
+    def _getTimeStr(timestamp):
+        """Get the string representation of the given timestamp."""
+        return time.strftime("%H:%M:%S", time.gmtime(timestamp))
+                
+    def message(self, timestamp, msg):
+        """Put a simple textual message into the log with the given timestamp."""
+        timeStr = Logger._getTimeStr(timestamp)
+        print >> self._output, timeStr + ":", msg
+        print timeStr + ":", msg        
+
+    def debug(self, timestamp, msg):
+        """Log a debug message."""
+        timeStr = Logger._getTimeStr(timestamp)
+        print >> self._output, timeStr + ": [DEBUG] ", msg
+        print timeStr + ": [DEBUG]", msg        
+
+    def stage(self, timestamp, stage):
+        """Report a change in the flight stage."""
+        s = Logger._stages[stage] if stage in Logger._stages else "<Unknown>"
+        self.message(timestamp, "--- %s ---" % (s,))
+        
+    def fault(self, timestamp, what, score):
+        """Report a fault."""
+        self._score -= score
+        self.message(timestamp, "%s (%f)" % (what, score))
+
+    def noGo(self, timestamp, what):
+        """Report a No-Go fault."""
+        self._score = -1
+        self.message(timestamp, "%s (NO GO)" % (what,))
+
+#--------------------------------------------------------------------------------------
Index: src/test.py
===================================================================
--- src/test.py	(revision 6)
+++ src/test.py	(revision 8)
@@ -2,7 +2,11 @@
 
 import fs
+import flight
+import logger
 import acft
 import const
+
 import time
+import sys
 
 def callback(data, extra):
@@ -10,17 +14,20 @@
 
 def main():
-    simulator = fs.createSimulator(const.SIM_MSFS9, fs.ConnectionListener(),
-                                   acft.Aircraft(const.AIRCRAFT_B737))
-    simulator.connect()
+    with open(sys.argv[1], "wt") as output:
+        fl = flight.Flight(logger.Logger(output = output))
+        fl.cruiseAltitude = 18000
+        fl.aircraftType = const.AIRCRAFT_B736
+        fl.aircraft = acft.Aircraft.create(fl)
+        simulator = fs.createSimulator(const.SIM_MSFS9, fs.ConnectionListener(),
+                                       fl.aircraft)
+        fl.simulator = simulator
 
-    time.sleep(10)
-    simulator.startMonitoring()
+        simulator.connect()
+        simulator.startMonitoring()
+        
+        fl.wait()
 
-    while True:
-        time.sleep(1000)
-    simulator.stopMonitoring()
-    simulator.disconnect()
-
-    time.sleep(5)
+        simulator.stopMonitoring()
+        simulator.disconnect()
 
 if __name__ == "__main__":
