Index: TODO
===================================================================
--- TODO	(revision 7)
+++ TODO	(revision 9)
@@ -1,1 +1,6 @@
 - Check how piston engines report RPM
+
+- Test:
+   - Mach cruise speed
+   - ZFW at takeoff
+   - Speed, heading, pitch at takeoff
Index: src/acft.py
===================================================================
--- src/acft.py	(revision 8)
+++ src/acft.py	(revision 9)
@@ -21,8 +21,12 @@
         self._flight = flight
         self._aircraftState = None
+       
+        self._maxVS = -10000.0
+        self._minVS = 10000.0
 
         self._checkers = []
 
         self._checkers.append(checks.StageChecker())
+        self._checkers.append(checks.TakeOffLogger())
 
         self._checkers.append(checks.AltimeterLogger())
@@ -41,4 +45,6 @@
         self._checkers.append(checks.GearsLogger())
 
+        self._checkers.append(checks.CruiseSpeedLogger())
+
     @property
     def type(self):
@@ -68,4 +74,7 @@
                           self._aircraftState, aircraftState)
 
+        self._maxVS = max(self._maxVS, aircraftState.vs)
+        self._minVS = min(self._minVS, aircraftState.vs)
+
         self._aircraftState = aircraftState
     
@@ -73,16 +82,78 @@
         """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.
+        if self._flight.setStage(aircraftState.timestamp, newStage):
+            if newStage==const.STAGE_PUSHANDTAXI:
+                self.logger.message(aircraftState.timestamp, "Block time start")
+                self.logFuel(aircraftState)
+                self.logger.message(aircraftState.timestamp, 
+                                    "Zero-fuel weight: %.0f kg" % (aircraftState.zfw))
+            elif newStage==const.STAGE_TAKEOFF:
+                self.logger.message(aircraftState.timestamp, "Flight time start")
+                self.logger.message(aircraftState.timestamp, 
+                                    "Takeoff weight: %.0f kg, MTOW: %.0f kg" % \
+                                    (aircraftState.grossWeight, self.mtow))
+                self.logger.message(aircraftState.timestamp,
+                                    "Wind %03.0f degrees at %.0f knots" % \
+                                    (aircraftState.windDirection, 
+                                     aircraftState.windSpeed))
+            elif newStage==const.STAGE_TAXIAFTERLAND:
+                self.logger.message(aircraftState.timestamp, "Flight time end")
+                self.logFuel(aircraftState)
+                self.logger.message(aircraftState.timestamp,
+                                    "Landing weight: %.0f kg, MLW: %.0f" % \
+                                    (aircraftState.grossWeight, self.mlw))
+                self.logger.message(aircraftState.timestamp,
+                                    "Vertical speed range: %.0f..%.0f feet/min" % \
+                                    (self._maxVS, self._minVS))
+            elif newStage==const.STAGE_PARKING:
+                self.logger.message(aircraftState.timestamp, "Block time end")
+
+    def prepareFlare(self):
+        """Called when it is detected that we will soon flare.
 
         On the first call, it should start monitoring some parameters more
         closely to determine flare time."""
-        pass
+        self.flight.simulator.startFlare()
+
+    def flareStarted(self, windSpeed, windDirection, visibility, 
+                     flareStart, flareStartFS):
+        """Called when the flare has started."""
+        self.logger.message(self._aircraftState.timestamp, "The flare has begun")
+        self.logger.message(self._aircraftState.timestamp,
+                            "Wind %03.0f degrees at %.0f knots" % \
+                            (windDirection, windSpeed))
+        self.logger.message(self._aircraftState.timestamp,
+                            "Visibility: %.0f metres" % (visibility,))
+        self.logger.message(self._aircraftState.timestamp,
+                            "Altimeter setting: %.0f hPa" % \
+                            (self._aircraftState.altimeter,))
+        self.flight.flareStarted(flareStart, flareStartFS)
+         
+    def flareFinished(self, flareEnd, flareEndFS, tdRate, tdRateCalculatedByFS,
+                      ias, pitch, bank, heading):
+        """Called when the flare has finished."""
+        (flareTimeFromFS, flareTime) = self.flight.flareFinished(flareEnd,
+                                                                 flareEndFS)
+        self.logger.message(self._aircraftState.timestamp,
+                            "Flare time: %.1f s (from %s)" % \
+                            (flareTime, 
+                             "the simulator" if flareTimeFromFS else "real time",))
+        self.logger.message(self._aircraftState.timestamp,
+                            "Touchdown rate: %.0f feet/min" % (tdRate,))
+        self.logger.message(self._aircraftState.timestamp,
+                            "Touchdown rate was calculated by the %s" % \
+                            ("simulator" if tdRateCalculatedByFS else "logger",))
+        self.logger.message(self._aircraftState.timestamp,
+                            "Touchdown speed: %.0f knots" % (ias,))
+        self.logger.message(self._aircraftState.timestamp,
+                            "Touchdown pitch: %.1f degrees" % (pitch,))
+        self.logger.message(self._aircraftState.timestamp,
+                            "Touchdown bank: %.1f degrees" % (bank,))
+        self.logger.message(self._aircraftState.timestamp,
+                            "Touchdown heading: %03.0f degrees" % (heading,))
 
     def cancelFlare(self):
         """Cancel flare, if it has started."""
-        pass
+        self.flight.simulator.cancelFlare()
 
     def checkFlightEnd(self, aircraftState):
@@ -105,6 +176,25 @@
     - n1: left, right
     - reverser: left, right"""
-    pass
-
+    def __init__(self, flight):
+        super(Boeing737, self).__init__(flight)
+        self.gearSpeedLimit = 270
+        self.flapSpeedLimits = { 1 : 260,
+                                 2 : 260,
+                                 5 : 250,
+                                 10 : 210,
+                                 15 : 200,
+                                 25 : 190,
+                                 30 : 175,
+                                 40 : 162 }
+
+    def logFuel(self, aircraftState):
+        """Log the amount of fuel"""
+        self.logger.message(aircraftState.timestamp,
+                            "Fuel: left=%.0f kg - centre=%.0f kg - right=%.0f kg" % \
+                            (aircraftState.fuel[1], aircraftState.fuel[0],
+                             aircraftState.fuel[2]))
+        self.logger.message(aircraftState.timestamp,
+                            "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
+                            
 #---------------------------------------------------------------------------------------
 
@@ -113,4 +203,8 @@
     def __init__(self, flight):
         super(B736, self).__init__(flight)
+        self.dow = 38307
+        self.mtow = 58328
+        self.mlw = 54657
+        self.mzfw = 51482
 
 #---------------------------------------------------------------------------------------
@@ -120,4 +214,8 @@
     def __init__(self, flight):
         super(B737, self).__init__(flight)
+        self.dow = 39250
+        self.mtow = 61410
+        self.mlw = 58059
+        self.mzfw = 54657
 
 #---------------------------------------------------------------------------------------
@@ -127,4 +225,16 @@
     def __init__(self, flight):
         super(B738, self).__init__(flight)
+        self.dow = 42690
+        self.mtow = 71709
+        self.mlw = 65317
+        self.mzfw = 61688
+
+#---------------------------------------------------------------------------------------
+
+class B738Charter(B738):
+    """Boeing 737-800 aircraft used for charters."""
+    def __init__(self, flight):
+        super(B738Charter, self).__init__(flight)
+        self.mtow = 77791
 
 #---------------------------------------------------------------------------------------
@@ -134,4 +244,8 @@
     def __init__(self, flight):
         super(B733, self).__init__(flight)
+        self.dow = 32700
+        self.mtow = 62820
+        self.mlw = 51700
+        self.mzfw = 48410
 
 #---------------------------------------------------------------------------------------
@@ -141,4 +255,8 @@
     def __init__(self, flight):
         super(B734, self).__init__(flight)
+        self.dow = 33200
+        self.mtow = 68050
+        self.mlw = 56200
+        self.mzfw = 53100
 
 #---------------------------------------------------------------------------------------
@@ -148,4 +266,8 @@
     def __init__(self, flight):
         super(B735, self).__init__(flight)
+        self.dow = 31300
+        self.mtow = 60550
+        self.mlw = 50000
+        self.mzfw = 46700
 
 #---------------------------------------------------------------------------------------
@@ -161,4 +283,22 @@
     def __init__(self, flight):
         super(DH8D, self).__init__(flight)
+        self.dow = 17185
+        self.mtow = 29257
+        self.mlw = 28009
+        self.mzfw = 25855
+        self.gearSpeedLimit = 215
+        self.flapSpeedLimits = { 5 : 200,
+                                 10 : 181,
+                                 15 : 172,
+                                 35 : 158 }
+
+    def logFuel(self, aircraftState):
+        """Log the amount of fuel"""
+        self.logger.message(aircraftState.timestamp,
+                            "Fuel: left=%.0f kg - centre=%.0f kg - right=%.0f kg" % \
+                            (aircraftState.fuel[1], aircraftState.fuel[0],
+                             aircraftState.fuel[2]))
+        self.logger.message(aircraftState.timestamp,
+                            "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
 
 #---------------------------------------------------------------------------------------
@@ -172,5 +312,23 @@
     - n1: left, right
     - reverser: left, right"""
-
+    def __init__(self, flight):
+        super(Boeing767, self).__init__(flight)
+        self.gearSpeedLimit = 270
+        self.flapSpeedLimits = { 1 : 255,
+                                 5 : 235,
+                                 10 : 215,
+                                 20 : 215,
+                                 25 : 185,
+                                 30 : 175 }
+
+    def logFuel(self, aircraftState):
+        """Log the amount of fuel"""
+        self.logger.message(aircraftState.timestamp,
+                            "Fuel: left=%.0f kg - centre=%.0f kg - right=%.0f kg" % \
+                            (aircraftState.fuel[1], aircraftState.fuel[0],
+                             aircraftState.fuel[2]))
+        self.logger.message(aircraftState.timestamp,
+                            "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
+                            
 #---------------------------------------------------------------------------------------
 
@@ -179,4 +337,8 @@
     def __init__(self, flight):
         super(B762, self).__init__(flight)
+        self.dow = 84507
+        self.mtow = 175540
+        self.mlw = 126098
+        self.mzfw = 114758
 
 #---------------------------------------------------------------------------------------
@@ -186,4 +348,8 @@
     def __init__(self, flight):
         super(B763, self).__init__(cflight)
+        self.dow = 91311
+        self.mtow = 181436
+        self.mlw = 137892
+        self.mzfw = 130635
 
 #---------------------------------------------------------------------------------------
@@ -199,5 +365,23 @@
     def __init__(self, flight):
         super(CRJ2, self).__init__(flight)
-
+        self.dow = 14549
+        self.mtow = 22995
+        self.mlw = 21319
+        self.mzfw = 19958
+        self.gearSpeedLimit = 240
+        self.flapSpeedLimits = { 8 : 260,
+                                 20 : 220,
+                                 30 : 190,
+                                 45 : 175 }
+
+    def logFuel(self, aircraftState):
+        """Log the amount of fuel"""
+        self.logger.message(aircraftState.timestamp,
+                            "Fuel: left=%.0f kg - centre=%.0f kg - right=%.0f kg" % \
+                            (aircraftState.fuel[1], aircraftState.fuel[0],
+                             aircraftState.fuel[2]))
+        self.logger.message(aircraftState.timestamp,
+                            "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
+                            
 #---------------------------------------------------------------------------------------
 
@@ -212,5 +396,24 @@
     def __init__(self, flight):
         super(F70, self).__init__(flight)
-
+        self.dow = 24283
+        self.mtow = 38100 # FIXME: differentiate by registration number,
+                          # MTOW of HA-LMF: 41955
+        self.mlw = 36740
+        self.mzfw = 32655
+        self.gearSpeedLimit = 200
+        self.flapSpeedLimits = { 8 : 250,
+                                 15 : 220,
+                                 25 : 220,
+                                 42 : 180 }
+
+    def logFuel(self, aircraftState):
+        """Log the amount of fuel"""
+        self.logger.message(aircraftState.timestamp,
+                            "Fuel: left=%.0f kg - centre=%.0f kg - right=%.0f kg" % \
+                            (aircraftState.fuel[1], aircraftState.fuel[0],
+                             aircraftState.fuel[2]))
+        self.logger.message(aircraftState.timestamp,
+                            "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
+                            
 #---------------------------------------------------------------------------------------
 
@@ -225,4 +428,12 @@
     def __init__(self, flight):
         super(DC3, self).__init__(flight)
+        self.dow = 8627
+        self.mtow = 11884
+        self.mlw = 11793
+        self.mzfw = 11780
+        self.gearSpeedLimit = 148
+        self.flapSpeedLimits = { 15 : 135,
+                                 30 : 99,
+                                 45 : 97 }
 
     def _checkFlightEnd(self, aircraftState):
@@ -234,4 +445,13 @@
         self._setStage(aircraftState, const.STAGE_END)
 
+    def logFuel(self, aircraftState):
+        """Log the amount of fuel"""
+        self.logger.message(aircraftState.timestamp,
+                            "Fuel: left aux=%.0f kg - left=%.0f kg - right=%.0f kg - right aux=%.0f kg" % \
+                            (aircraftState.fuel[2], aircraftState.fuel[0],
+                             aircraftState.fuel[1], aircraftState.fuel[3]))
+        self.logger.message(aircraftState.timestamp,
+                            "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
+                            
 #---------------------------------------------------------------------------------------
 
@@ -247,5 +467,24 @@
     def __init__(self, flight):
         super(T134, self).__init__(flight)
-
+        self.dow = 29927
+        self.mtow = 47600
+        self.mlw = 43000
+        self.mzfw = 38500
+        self.gearSpeedLimit = 216
+        self.flapSpeedLimits = { 10 : 240,
+                                 20 : 216,
+                                 30 : 161 }
+
+    def logFuel(self, aircraftState):
+        """Log the amount of fuel"""
+        self.logger.message(aircraftState.timestamp,
+                            "Fuel: left aux=%.0f kg - left tip=%.0f kg - centre= %.0f kg - right tip=%.0f kg - right aux=%.0f kg - external 1=%.0f kg - external 2=%.0f kg" % \
+                            (aircraftState.fuel[2], aircraftState.fuel[1],
+                             aircraftState.fuel[0], 
+                             aircraftState.fuel[3], aircraftState.fuel[4],
+                             aircraftState.fuel[5], aircraftState.fuel[6]))
+        self.logger.message(aircraftState.timestamp,
+                            "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
+                            
 #---------------------------------------------------------------------------------------
 
@@ -260,6 +499,25 @@
     def __init__(self, flight):
         super(T154, self).__init__(flight)
-
-#---------------------------------------------------------------------------------------
+        self.dow = 53259
+        self.mtow = 98000
+        self.mlw = 78000
+        self.mzfw = 72000
+        self.gearSpeedLimit = 216
+        self.flapSpeedLimits = { 15 : 227,
+                                 28 : 194,
+                                 45 : 162 }
+
+    def logFuel(self, aircraftState):
+        """Log the amount of fuel"""
+        self.logger.message(aircraftState.timestamp,
+                            "Fuel: left aux=%.0f kg - left=%.0f kg - centre=%.0f kg - centre 2=%.0f kg - right=%.0f kg - right aux=%.0f kg" % \
+                            (aircraftState.fuel[4], aircraftState.fuel[1],
+                             aircraftState.fuel[0], aircraftState.fuel[3], 
+                             aircraftState.fuel[2], aircraftState.fuel[5]))
+        self.logger.message(aircraftState.timestamp,
+                            "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
+
+#---------------------------------------------------------------------------------------
+
 
 class YK40(Aircraft):
@@ -273,4 +531,19 @@
     def __init__(self, flight):
         super(YK40, self).__init__(flight)
+        self.dow = 9400
+        self.mtow = 17200
+        self.mlw = 16800
+        self.mzfw = 12100
+        self.gearSpeedLimit = 165
+        self.flapSpeedLimits = { 20 : 165,
+                                 35 : 135 }
+
+    def logFuel(self, aircraftState):
+        """Log the amount of fuel"""
+        self.logger.message(aircraftState.timestamp,
+                            "Fuel: left=%.0f kg - right=%.0f kg" % \
+                            (aircraftState.fuel[0], aircraftState.fuel[1]))
+        self.logger.message(aircraftState.timestamp,
+                            "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
 
 #---------------------------------------------------------------------------------------
Index: src/checks.py
===================================================================
--- src/checks.py	(revision 8)
+++ src/checks.py	(revision 9)
@@ -66,5 +66,5 @@
                 aircraft.cancelFlare()
             elif state.radioAltitude<150 and not state.onTheGround:
-                aircraft.flare()
+                aircraft.prepareFlare()
         elif stage==const.STAGE_TAXIAFTERLAND:
             if state.parking:
@@ -73,4 +73,45 @@
             if aircraft.checkFlightEnd(state):
                 aircraft.setStage(state, const.STAGE_END)
+
+#---------------------------------------------------------------------------------------
+
+class TakeOffLogger(StateChecker):
+    """Logger for the cruise speed."""
+    def __init__(self):
+        """Construct the logger."""
+        self._onTheGround = True
+    
+    def check(self, flight, aircraft, logger, oldState, state):
+        """Log the cruise speed if necessary."""
+        if flight.stage==const.STAGE_TAKEOFF and \
+           self._onTheGround and not state.onTheGround:
+            logger.message(state.timestamp,
+                           "Takeoff speed: %.0f knots" % (state.ias,))
+            logger.message(state.timestamp,
+                           "Takeoff heading: %03.0f degrees" % (state.heading,))
+            logger.message(state.timestamp,
+                           "Takeoff pitch: %.1f degrees" % (state.pitch,))
+            self._onTheGround = False
+
+#---------------------------------------------------------------------------------------
+
+class CruiseSpeedLogger(StateChecker):
+    """Logger for the cruise speed."""
+    def __init__(self):
+        """Construct the logger."""
+        self._lastTime = None
+    
+    def check(self, flight, aircraft, logger, oldState, state):
+        """Log the cruise speed if necessary."""
+        if flight.stage==const.STAGE_CRUISE and \
+           (self._lastTime is None or \
+            (self._lastTime+800)<=state.timestamp):
+                if state.altitude>24500.0:
+                    logger.message(state.timestamp,
+                                   "Cruise speed: %.2f mach" % (state.mach,))
+                else:
+                    logger.message(state.timestamp,
+                                   "Cruise speed: %.0f knots" % (state.ias,))
+                self._lastTime = state.timestamp
 
 #---------------------------------------------------------------------------------------
@@ -301,5 +342,5 @@
     def _getMessage(self, state):
         """Get the message to log on a change."""
-        return "Gears %s at %.0f knots, %f feet" % \
+        return "Gears %s at %.0f knots, %.0f feet" % \
             ("DOWN" if state.gearsDown else "UP", state.ias, state.altitude)
 
Index: src/flight.py
===================================================================
--- src/flight.py	(revision 8)
+++ src/flight.py	(revision 9)
@@ -19,4 +19,5 @@
         self.logger = logger
         self.cruiseAltitude = None
+        self.flareTimeFromFS = False
         self.aircraftType = None
         self.aircraft = None
@@ -24,4 +25,7 @@
 
         self._endCondition = threading.Condition()
+
+        self._flareStart = None
+        self._flareStartFS = None
 
     @property
@@ -31,5 +35,7 @@
 
     def setStage(self, timestamp, stage):
-        """Set the flight stage."""
+        """Set the flight stage.
+
+        Returns if the stage has really changed."""
         if stage!=self._stage:
             self._stage = stage
@@ -38,4 +44,24 @@
                 with self._endCondition:
                     self._endCondition.notify()
+            return True
+        else:
+            return False
+
+    def flareStarted(self, flareStart, flareStartFS):
+        """Called when the flare time has started."""
+        self._flareStart = flareStart
+        self._flareStartFS = flareStartFS
+
+    def flareFinished(self, flareEnd, flareEndFS):
+        """Called when the flare time has ended.
+        
+        Return a tuple of the following items:
+        - a boolean indicating if FS time is used
+        - the flare time
+        """
+        if self.flareTimeFromFS:
+            return (True, flareEndFS - self._flareStartFS)
+        else:
+            return (False, flareEnd - self._flareStart)
 
     def wait(self):
Index: src/fs.py
===================================================================
--- src/fs.py	(revision 8)
+++ src/fs.py	(revision 9)
@@ -53,4 +53,5 @@
     - stalled: a boolean indicating if the aircraft is stalled
     - onTheGround: a boolean indicating if the aircraft is on the ground
+    - zfw: the zero-fuel weight in kilograms (float)
     - grossWeight: the gross weight in kilograms (float)
     - heading: the heading of the aircraft in degrees (float)
@@ -60,4 +61,5 @@
     negative means bank right (float)
     - ias: the indicated airspeed in knots (float)    
+    - mach: the airspeed in mach (float)    
     - groundSpeed: the ground speed (float)
     - vs: the vertical speed in feet/minutes (float)
@@ -93,13 +95,9 @@
     - nav2: the frequency of the NAV1 radio in MHz (string)
     - squawk: the transponder code
+    - windSpeed: the speed of the wind at the aircraft in knots (float)
+    - windDirection: the direction of the wind at the aircraft in degrees (float)
 
     FIXME: needed when taxiing only:
-    - zfw: the Zero Fuel Weight in klograms (float)
     - payload weight
-
-    FIXME: needed for touchdown only:
-    - ambientWindDirection: the ambient wind direction around the aircraft (float)
-    - ambientWindSpeed: the ambient wind speed around the aircraft in knowns (float)
-    - tdRate: the touchdown rate calculated by FSUIPC (float)
 
     FIXME: needed rarely:
Index: src/fsuipc.py
===================================================================
--- src/fsuipc.py	(revision 8)
+++ src/fsuipc.py	(revision 9)
@@ -22,4 +22,31 @@
 class Handler(threading.Thread):
     """The thread to handle the FSUIPC requests."""
+    @staticmethod
+    def fsuipc2VS(data):
+        """Convert the given vertical speed data read from FSUIPC into feet/min."""
+        return data*60.0/const.FEETTOMETRES/256.0
+
+    @staticmethod
+    def fsuipc2radioAltitude(data):
+        """Convert the given radio altitude data read from FSUIPC into feet."""
+        return data/const.FEETTOMETRES/65536.0
+
+    @staticmethod
+    def fsuipc2Degrees(data):
+        """Convert the given data into degrees."""
+        return data * 360.0 / 65536.0 / 65536.0
+
+    @staticmethod
+    def fsuipc2PositiveDegrees(data):
+        """Convert the given data into positive degrees."""
+        degrees = Handler.fsuipc2Degrees(data)
+        if degrees<0.0: degrees += 360.0
+        return degrees
+
+    @staticmethod
+    def fsuipc2IAS(data):
+        """Convert the given data into indicated airspeed."""
+        return data / 128.0
+
     @staticmethod
     def _callSafe(fun):
@@ -341,12 +368,29 @@
     via FSUIPC."""
     # The basic data that should be queried all the time once we are connected
-    normalData = [ (0x0240, "H"),
-                   (0x023e, "H"),
-                   (0x023b, "b"),
-                   (0x023c, "b"),
-                   (0x023a, "b"),
-                   (0x3d00, -256),
-                   (0x3c00, -256) ]
-    
+    normalData = [ (0x0240, "H"),            # Year
+                   (0x023e, "H"),            # Number of day in year
+                   (0x023b, "b"),            # UTC hour
+                   (0x023c, "b"),            # UTC minute
+                   (0x023a, "b"),            # seconds
+                   (0x3d00, -256),           # The name of the current aircraft
+                   (0x3c00, -256) ]          # The path of the current AIR file
+
+    flareData1 = [ (0x023a, "b"),            # Seconds of time
+                   (0x31e4, "d"),            # Radio altitude
+                   (0x02c8, "d") ]           # Vertical speed
+                   
+    flareStartData = [ (0x0e90, "H"),        # Ambient wind speed
+                       (0x0e92, "H"),        # Ambient wind direction
+                       (0x0e8a, "H") ]       # Visibility
+                       
+    flareData2 = [ (0x023a, "b"),            # Seconds of time
+                   (0x0366, "H"),            # On the ground
+                   (0x02c8, "d"),            # Vertical speed
+                   (0x030c, "d"),            # Touch-down rate
+                   (0x02bc, "d"),            # IAS
+                   (0x0578, "d"),            # Pitch
+                   (0x057c, "d"),            # Bank
+                   (0x0580, "d") ]           # Heading
+
     def __init__(self, connectionListener, aircraft):
         """Construct the simulator.
@@ -356,5 +400,18 @@
         - modelChanged(aircraftName, modelName): called when the model handling
         the aircraft has changed.
-        - handleState(aircraftState): handle the given state."""
+        - handleState(aircraftState): handle the given state.
+        - flareStarted(windSpeed, windDirection, visibility, flareStart,
+                       flareStartFS): called when the flare has
+          started. windSpeed is in knots, windDirection is in degrees and
+          visibility is in metres. flareStart and flareStartFS are two time
+          values expressed in seconds that can be used to calculate the flare
+          time. 
+       - flareFinished(flareEnd, flareEndFS, tdRate, tdRateCalculatedByFS,
+                       ias, pitch, bank, heading): called when the flare has
+         finished, i.e. the aircraft is on the ground. flareEnd and flareEndFS
+         are the two time values corresponding to the touchdown time. tdRate is
+         the touch-down rate, tdRateCalculatedBySim indicates if the data comes
+         from the simulator or was calculated by the adapter. The other data
+         are self-explanatory and expressed in their 'natural' units."""
         self._aircraft = aircraft
 
@@ -370,4 +427,9 @@
         self._aircraftModel = None
 
+        self._flareRequestID = None
+        self._flareRates = []
+        self._flareStart = None
+        self._flareStartFS = None
+        
     def connect(self):
         """Initiate a connection to the simulator."""
@@ -385,4 +447,25 @@
         assert self._monitoringRequested 
         self._monitoringRequested = False
+
+    def startFlare(self):
+        """Start monitoring the flare time.
+
+        At present it is assumed to be called from the FSUIPC thread, hence no
+        protection."""
+        #self._aircraft.logger.debug("startFlare")
+        if self._flareRequestID is None:
+            self._flareRates = []
+            self._flareRequestID = self._handler.requestPeriodicRead(0.1,
+                                                                     Simulator.flareData1,
+                                                                     self._handleFlare1)
+
+    def cancelFlare(self):
+        """Cancel monitoring the flare time.
+
+        At present it is assumed to be called from the FSUIPC thread, hence no
+        protection."""
+        if self._flareRequestID is not None:
+            self._handler.clearPeriodic(self._flareRequestID)
+            self._flareRequestID = None
 
     def disconnect(self):
@@ -485,4 +568,64 @@
                                               self._handleNormal)
 
+    def _addFlareRate(self, data):
+        """Append a flare rate to the list of last rates."""
+        if len(self._flareRates)>=3:
+            del self._flareRates[0]
+        self._flareRates.append(Handler.fsuipc2VS(data))
+
+    def _handleFlare1(self, data, normal):
+        """Handle the first stage of flare monitoring."""
+        #self._aircraft.logger.debug("handleFlare1: " + str(data))
+        if Handler.fsuipc2radioAltitude(data[1])<=50.0:
+            self._flareStart = time.time()
+            self._flareStartFS = data[0]
+            self._handler.clearPeriodic(self._flareRequestID)
+            self._flareRequestID = \
+                self._handler.requestPeriodicRead(0.1, 
+                                                  Simulator.flareData2,
+                                                  self._handleFlare2)
+            self._handler.requestRead(Simulator.flareStartData,
+                                      self._handleFlareStart)
+                
+        self._addFlareRate(data[2])
+
+    def _handleFlareStart(self, data, extra):
+        """Handle the data need to notify the aircraft about the starting of
+        the flare."""
+        #self._aircraft.logger.debug("handleFlareStart: " + str(data))
+        if data is not None:
+            windDirection = data[1]*360.0/65536.0
+            if windDirection<0.0: windDirection += 360.0
+            self._aircraft.flareStarted(data[0], windDirection,
+                                        data[2]*1609.344/100.0,
+                                        self._flareStart, self._flareStartFS)
+
+    def _handleFlare2(self, data, normal):
+        """Handle the first stage of flare monitoring."""
+        #self._aircraft.logger.debug("handleFlare2: " + str(data))
+        if data[1]!=0:
+            flareEnd = time.time()
+            self._handler.clearPeriodic(self._flareRequestID)
+            self._flareRequestID = None
+
+            flareEndFS = data[0]
+            if flareEndFS<self._flareStartFS:
+                flareEndFS += 60
+
+            tdRate = Handler.fsuipc2VS(data[3])
+            tdRateCalculatedByFS = True
+            if tdRate==0 or tdRate>1000.0 or tdRate<-1000.0:
+                tdRate = min(self._flareRates)
+                tdRateCalculatedByFS = False
+
+            self._aircraft.flareFinished(flareEnd, flareEndFS,
+                                         tdRate, tdRateCalculatedByFS,
+                                         Handler.fsuipc2IAS(data[4]),
+                                         Handler.fsuipc2Degrees(data[5]),
+                                         Handler.fsuipc2Degrees(data[6]),
+                                         Handler.fsuipc2PositiveDegrees(data[7]))
+        else:
+            self._addFlareRate(data[2])
+                                                  
 #------------------------------------------------------------------------------
 
@@ -499,4 +642,5 @@
                       ("stalled", 0x036c, "b"),
                       ("onTheGround", 0x0366, "H"),
+                      ("zfw", 0x3bfc, "d"),
                       ("grossWeight", 0x30c0, "f"),
                       ("heading", 0x0580, "d"),
@@ -504,4 +648,5 @@
                       ("bank", 0x057c, "d"),
                       ("ias", 0x02bc, "d"),
+                      ("mach", 0x11c6, "H"),
                       ("groundSpeed", 0x02b4, "d"),
                       ("vs", 0x02c8, "d"),
@@ -521,5 +666,8 @@
                       ("nav1", 0x0350, "H"),
                       ("nav2", 0x0352, "H"),
-                      ("squawk", 0x0354, "H")]
+                      ("squawk", 0x0354, "H"),
+                      ("windSpeed", 0x0e90, "H"),
+                      ("windDirection", 0x0e92, "H")]
+
 
     specialModels = []
@@ -624,17 +772,19 @@
         state.onTheGround = data[self._monidx_onTheGround]!=0
 
+        state.zfw = data[self._monidx_zfw] * const.LBSTOKG / 256.0
         state.grossWeight = data[self._monidx_grossWeight] * const.LBSTOKG
         
-        state.heading = data[self._monidx_heading]*360.0/65536.0/65536.0
-        if state.heading<0.0: state.heading += 360.0
-
-        state.pitch = data[self._monidx_pitch]*360.0/65536.0/65536.0
-        state.bank = data[self._monidx_bank]*360.0/65536.0/65536.0
-
-        state.ias = data[self._monidx_ias]/128.0
+        state.heading = Handler.fsuipc2PositiveDegrees(data[self._monidx_heading])
+
+        state.pitch = Handler.fsuipc2Degrees(data[self._monidx_pitch])
+        state.bank = Handler.fsuipc2Degrees(data[self._monidx_bank])
+
+        state.ias = Handler.fsuipc2IAS(data[self._monidx_ias])
+        state.mach = data[self._monidx_mach] / 20480.0
         state.groundSpeed = data[self._monidx_groundSpeed]* 3600.0/65536.0/1852.0
-        state.vs = data[self._monidx_vs]*60.0/const.FEETTOMETRES/256.0
-
-        state.radioAltitude = data[self._monidx_radioAltitude]/const.FEETTOMETRES/65536.0
+        state.vs = Handler.fsuipc2VS(data[self._monidx_vs])
+
+        state.radioAltitude = \
+            Handler.fsuipc2radioAltitude(data[self._monidx_radioAltitude])
         state.altitude = data[self._monidx_altitude]/const.FEETTOMETRES/65536.0/65536.0
 
@@ -687,4 +837,8 @@
         state.nav2 = AircraftModel.convertFrequency(data[self._monidx_nav2])
         state.squawk = AircraftModel.convertBCD(data[self._monidx_squawk], 4)
+
+        state.windSpeed = data[self._monidx_windSpeed]
+        state.windDirection = data[self._monidx_windDirection]*360.0/65536.0
+        if state.windDirection<0.0: state.windDirection += 360.0
         
         return state
Index: src/logger.py
===================================================================
--- src/logger.py	(revision 8)
+++ src/logger.py	(revision 9)
@@ -41,9 +41,8 @@
         print timeStr + ":", msg        
 
-    def debug(self, timestamp, msg):
+    def debug(self, msg):
         """Log a debug message."""
-        timeStr = Logger._getTimeStr(timestamp)
-        print >> self._output, timeStr + ": [DEBUG] ", msg
-        print timeStr + ": [DEBUG]", msg        
+        print >> self._output, "[DEBUG] ", msg
+        print "[DEBUG]", msg
 
     def stage(self, timestamp, stage):
Index: src/test.py
===================================================================
--- src/test.py	(revision 8)
+++ src/test.py	(revision 9)
@@ -19,4 +19,5 @@
         fl.aircraftType = const.AIRCRAFT_B736
         fl.aircraft = acft.Aircraft.create(fl)
+        #fl._stage = const.STAGE_LANDING
         simulator = fs.createSimulator(const.SIM_MSFS9, fs.ConnectionListener(),
                                        fl.aircraft)
