Index: src/mlx/acft.py
===================================================================
--- src/mlx/acft.py	(revision 86)
+++ src/mlx/acft.py	(revision 89)
@@ -5,4 +5,5 @@
 import const
 import checks
+import util
 
 import time
@@ -112,4 +113,6 @@
             checker.check(self._flight, self, self._flight.logger,
                           self._aircraftState, aircraftState)
+
+        self._flight.handleState(self._aircraftState, aircraftState)
 
         self._maxVS = max(self._maxVS, aircraftState.vs)
@@ -155,4 +158,16 @@
             elif newStage==const.STAGE_PARKING:
                 self.logger.message(aircraftState.timestamp, "Block time end")
+            elif newStage==const.STAGE_END:
+                flightLength = self._flight.flightTimeEnd - self._flight.flightTimeStart
+                self.logger.message(aircraftState.timestamp,
+                                    "Flight time: " +
+                                    util.getTimeIntervalString(flightLength))
+                self.logger.message(aircraftState.timestamp,
+                                    "Flown distance: %.2f NM" % \
+                                    (self._flight.flownDistance,))                
+                blockLength = self._flight.blockTimeEnd - self._flight.blockTimeStart
+                self.logger.message(aircraftState.timestamp,
+                                    "Block time: " +
+                                    util.getTimeIntervalString(blockLength))
 
     def prepareFlare(self):
Index: src/mlx/flight.py
===================================================================
--- src/mlx/flight.py	(revision 86)
+++ src/mlx/flight.py	(revision 89)
@@ -4,4 +4,5 @@
 
 import const
+import util
 
 import threading
@@ -39,4 +40,17 @@
         self.aircraft = None
         self.simulator = None
+
+        self.blockTimeStart = None
+        self.flightTimeStart = None
+        self.flightTimeEnd = None
+        self.blockTimeEnd = None
+
+        self._lastDistanceTime = None
+        self._previousLatitude = None
+        self._previousLongitude = None
+        self.flownDistance = 0.0
+
+        self.startFuel = None
+        self.endFuel = None
 
         self._endCondition = threading.Condition()
@@ -80,4 +94,12 @@
         return self._gui.vref
 
+    def handleState(self, oldState, currentState):
+        """Handle a new state information."""
+        self._updateFlownDistance(currentState)
+        
+        self.endFuel = sum(currentState.fuel)
+        if self.startFuel is None:
+            self.startFuel = self.endFuel
+
     def setStage(self, timestamp, stage):
         """Set the flight stage.
@@ -88,5 +110,13 @@
             self._gui.setStage(stage)
             self.logger.stage(timestamp, stage)
-            if stage==const.STAGE_END:
+            if stage==const.STAGE_PUSHANDTAXI:
+                self.blockTimeStart = timestamp
+            elif stage==const.STAGE_TAKEOFF:
+                self.flightTimeStart = timestamp
+            elif stage==const.STAGE_TAXIAFTERLAND:
+                self.flightTimeEnd = timestamp
+            elif stage==const.STAGE_PARKING:
+                self.blockTimeEnd = timestamp
+            elif stage==const.STAGE_END:
                 with self._endCondition:
                     self._endCondition.notify()
@@ -133,3 +163,32 @@
                 self._endCondition.wait(1)
 
+    def _updateFlownDistance(self, currentState):
+        """Update the flown distance."""
+        if not currentState.onTheGround:
+            updateData = False
+            if self._lastDistanceTime is None or \
+               self._previousLatitude is None or \
+               self._previousLongitude is None:
+                updateData = True
+            elif currentState.timestamp >= (self._lastDistanceTime + 30.0):
+                updateData = True
+                self.flownDistance += self._getDistance(currentState)
+
+            if updateData:
+                self._previousLatitude = currentState.latitude
+                self._previousLongitude = currentState.longitude
+                self._lastDistanceTime = currentState.timestamp
+        else:
+            if self._lastDistanceTime is not None and \
+               self._previousLatitude is not None and \
+               self._previousLongitude is not None:
+                self.flownDistance += self._getDistance(currentState)
+                
+            self._lastDistanceTime = None
+
+    def _getDistance(self, currentState):
+        """Get the distance between the previous and the current state."""
+        return util.getDistCourse(self._previousLatitude, self._previousLongitude,
+                                  currentState.latitude, currentState.longitude)[0]
+
 #---------------------------------------------------------------------------------------
Index: src/mlx/fs.py
===================================================================
--- src/mlx/fs.py	(revision 59)
+++ src/mlx/fs.py	(revision 89)
@@ -51,4 +51,6 @@
     - timestamp: the simulator time of the measurement in seconds since the
     epoch (float)
+    - latitude (in degrees, North is positive)
+    - longitude (in degrees, East is positive)
     - paused: a boolean indicating if the flight simulator is paused for
     whatever reason (it could be a pause mode, or a menu, a dialog, or a
@@ -58,5 +60,5 @@
     - overspeed: a boolean indicating if the aircraft is in overspeed
     - stalled: a boolean indicating if the aircraft is stalled
-    - onTheGround: a boolean indicating if the aircraft is on the ground
+    - 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)
Index: src/mlx/fsuipc.py
===================================================================
--- src/mlx/fsuipc.py	(revision 61)
+++ src/mlx/fsuipc.py	(revision 89)
@@ -746,4 +746,6 @@
     object describing the aircraft's state."""
     monitoringData = [("paused", 0x0264, "H"),
+                      ("latitude", 0x0560, "l"),
+                      ("longitude", 0x0568, "l"),
                       ("frozen", 0x3364, "H"),
                       ("replay", 0x0628, "d"),
@@ -872,4 +874,11 @@
         
         state.timestamp = timestamp
+
+        state.latitude = data[self._monidx_latitude] * \
+                         90.0 / 10001750.0 / 65536.0 / 65536.0
+
+        state.longitude = data[self._monidx_longitude] * \
+                          360.0 / 65536.0 / 65536.0 / 65536.0 / 65536.0
+        if state.longitude>180.0: state.longitude = 360.0 - state.longitude
         
         state.paused = data[self._monidx_paused]!=0 or \
Index: src/mlx/gui/flight.py
===================================================================
--- src/mlx/gui/flight.py	(revision 88)
+++ src/mlx/gui/flight.py	(revision 89)
@@ -6,4 +6,5 @@
 import mlx.fs as fs
 from mlx.checks import PayloadChecker
+import mlx.util as util
 
 import datetime
@@ -1477,6 +1478,4 @@
     def activate(self):
         """Called when the page is activated."""
-        self._flightEnded = False
-        
         self._starButton.set_sensitive(True)
         self._starButton.set_active(False)
@@ -1516,4 +1515,7 @@
 
         self._vref.set_sensitive(False)
+        # FIXME: Perhaps a separate initialize() call which would set up
+        # defaults?
+        self._flightEnded = False
 
     def _starButtonClicked(self, button):
@@ -1557,6 +1559,123 @@
     def _forwardClicked(self, button):
         """Called when the forward button is clicked."""
-        #self._wizard.nextPage()
-        self.finalize()
+        self._wizard.nextPage()
+
+#-----------------------------------------------------------------------------
+
+class FinishPage(Page):
+    """Flight finish page."""
+    def __init__(self, wizard):
+        """Construct the finish page."""
+        help = "There are some statistics about your flight below.\n\n" \
+               "Review the data, also on earlier pages, and if you are\n" \
+               "satisfied, you can save or send your PIREP."
+
+        super(FinishPage, self).__init__(wizard, "Finish", help)
+        
+        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
+                                  xscale = 0.0, yscale = 0.0)
+
+        table = gtk.Table(5, 2)
+        table.set_row_spacings(4)
+        table.set_col_spacings(16)
+        table.set_homogeneous(True)
+        alignment.add(table)
+        self.setMainWidget(alignment)
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label("Flight rating:")
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 0, 1)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._flightRating = gtk.Label()
+        self._flightRating.set_width_chars(7)
+        self._flightRating.set_alignment(0.0, 0.5)
+        self._flightRating.set_use_markup(True)
+        labelAlignment.add(self._flightRating)
+        table.attach(labelAlignment, 1, 2, 0, 1)
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label("Flight time:")
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 1, 2)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._flightTime = gtk.Label()
+        self._flightTime.set_width_chars(10)
+        self._flightTime.set_alignment(0.0, 0.5)
+        self._flightTime.set_use_markup(True)
+        labelAlignment.add(self._flightTime)
+        table.attach(labelAlignment, 1, 2, 1, 2)
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label("Block time:")
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 2, 3)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._blockTime = gtk.Label()
+        self._blockTime.set_width_chars(10)
+        self._blockTime.set_alignment(0.0, 0.5)
+        self._blockTime.set_use_markup(True)
+        labelAlignment.add(self._blockTime)
+        table.attach(labelAlignment, 1, 2, 2, 3)
+
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label("Distance flown:")
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 3, 4)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._distanceFlown = gtk.Label()
+        self._distanceFlown.set_width_chars(10)
+        self._distanceFlown.set_alignment(0.0, 0.5)
+        self._distanceFlown.set_use_markup(True)
+        labelAlignment.add(self._distanceFlown)
+        table.attach(labelAlignment, 1, 2, 3, 4)
+        
+        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
+        label = gtk.Label("Fuel used:")
+        labelAlignment.add(label)
+        table.attach(labelAlignment, 0, 1, 4, 5)
+
+        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
+        self._fuelUsed = gtk.Label()
+        self._fuelUsed.set_width_chars(10)
+        self._fuelUsed.set_alignment(0.0, 0.5)
+        self._fuelUsed.set_use_markup(True)
+        labelAlignment.add(self._fuelUsed)
+        table.attach(labelAlignment, 1, 2, 4, 5)
+
+        self._saveButton = self.addButton("S_ave PIREP...")
+        self._saveButton.set_use_underline(True)
+        #self._saveButton.connect("clicked", self._saveClicked)
+        
+        self._sendButton = self.addButton("_Send PIREP...", True)
+        self._sendButton.set_use_underline(True)
+        #self._sendButton.connect("clicked", self._sendClicked)
+
+    def activate(self):
+        """Activate the page."""
+        flight = self._wizard.gui._flight
+        rating = flight.logger.getRating()
+        if rating<0:
+            self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
+        else:
+            self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
+
+        flightLength = flight.flightTimeEnd - flight.flightTimeStart
+        self._flightTime.set_markup("<b>%s</b>" % \
+                                    (util.getTimeIntervalString(flightLength),))
+        
+        blockLength = flight.blockTimeEnd - flight.blockTimeStart
+        self._blockTime.set_markup("<b>%s</b>" % \
+                                   (util.getTimeIntervalString(blockLength),))
+
+        self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
+                                       (flight.flownDistance,))
+        
+        self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
+                                  (flight.endFuel - flight.startFuel,))
 
 #-----------------------------------------------------------------------------
@@ -1588,4 +1707,5 @@
         self._landingPage = LandingPage(self) 
         self._pages.append(self._landingPage)
+        self._pages.append(FinishPage(self))
         
         maxWidth = 0
Index: src/mlx/gui/monitor.py
===================================================================
--- src/mlx/gui/monitor.py	(revision 77)
+++ src/mlx/gui/monitor.py	(revision 89)
@@ -4,4 +4,5 @@
 
 import mlx.const as const
+import mlx.util as util
 
 import time
@@ -170,4 +171,8 @@
         table.attach(label, 4, 5, 6, 7)
         table.attach(self._windDirection, 5, 6, 6, 7)
+
+        (label, self._position) = self._createLabeledEntry("Position:", 25)
+        table.attach(label, 6, 7, 6, 7)
+        table.attach(self._position, 7, 10, 6, 7)
 
         alignment.add(table)
@@ -240,4 +245,5 @@
             self._windSpeed.set_text("-")
             self._windDirection.set_text("-")
+            self._position.set_text("-")
         else:
             self._timestamp.set_text(time.strftime("%H:%M:%S",
@@ -304,4 +310,6 @@
             self._windSpeed.set_text("%.0f" % (aircraftState.windSpeed,))
             self._windDirection.set_text("%03.0f" % (aircraftState.windDirection,))
+            self._position.set_text(util.getCoordinateString((aircraftState.latitude,
+                                                              aircraftState.longitude)))
 
 #------------------------------------------------------------------------------
Index: src/mlx/pyuipc_sim.py
===================================================================
--- src/mlx/pyuipc_sim.py	(revision 88)
+++ src/mlx/pyuipc_sim.py	(revision 89)
@@ -184,4 +184,7 @@
         self.flapsNotches = [0, 1, 2, 5, 10, 15, 25, 30, 40]
         self.fuelCapacities = [10000.0, 5000.0, 5000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+
+        self.latitude = 47.5
+        self.longitude = 19.05
         
         self.paused = False
@@ -291,4 +294,8 @@
         elif offset==0x036d:       # Overspeed
             return 1 if self.overspeed else 0
+        elif offset==0x0560:       # Latitude
+            return long(self.latitude * 10001750.0 * 65536.0 * 65536.0 / 90.0)
+        elif offset==0x0568:       # Longitude
+            return long(self.longitude * 65536.0 * 65536.0 * 65536.0 * 65536.0 / 360.0)
         elif offset==0x0570:       # Altitude
             return long(self.altitude * const.FEETTOMETRES * 65536.0 * 65536.0)
@@ -472,4 +479,8 @@
         elif offset==0x036d:       # Overspeed
             self.overspeed = value!=0
+        elif offset==0x0560:       # Latitude
+            self.latitude = value * 90.0 / 10001750.0 / 65536.0 / 65536.0
+        elif offset==0x0568:       # Longitude
+            self.longitude = value * 360.0 / 65536.0 / 65536.0 / 65536.0 / 65536.0
         elif offset==0x0570:       # Altitude
             self.altitude = value / const.FEETTOMETRES / 65536.0 / 65536.0
@@ -908,4 +919,17 @@
         self._valueHandlers["airPath"] = (0x3c00, -256,  lambda value: value,
                                           lambda word: word)
+        self._valueHandlers["latitude"] = (0x0560, "l",
+                                           lambda value: value * 90.0 /
+                                           10001750.0 / 65536.0 / 65536.0,
+                                           lambda word: long(float(word) *
+                                                             10001750.0 *
+                                                             65536.0 * 65536.0 / 90.0))
+        self._valueHandlers["longitude"] = (0x0568, "l",
+                                            lambda value: value * 360.0 /
+                                            65536.0 / 65536.0 / 65536.0 / 65536.0,
+                                            lambda word: long(float(word) *
+                                                              65536.0 * 65536.0 *
+                                                              65536.0 * 65536.0 /
+                                                              360.0))
         self._valueHandlers["paused"] = (0x0264, "H", CLI.bool2str, CLI.str2bool)
         self._valueHandlers["frozen"] = (0x3364, "H", CLI.bool2str, CLI.str2bool)
Index: src/mlx/util.py
===================================================================
--- src/mlx/util.py	(revision 27)
+++ src/mlx/util.py	(revision 89)
@@ -1,2 +1,143 @@
 # Various utilities
 
+import math
+
+#------------------------------------------------------------------------------
+
+# The average of the radius at the poles and a the equator, in metres
+#EARTH_RADIUS=6367467.4
+EARTH_RADIUS=6371000
+#EARTH_RADIUS=6378137
+
+#------------------------------------------------------------------------------
+
+def getDegMinSec(degrees):
+    """Break up the given floating point degrees value into a tuple.
+
+    The tuple contains 4 items:
+    - the degrees as an integer
+    - the minutes as an integer
+    - the seconds as an integer
+    - 1.0 if the value was non-negative, -1.0 if it was negative."""
+    
+    if degrees<0:
+        degrees = -degrees
+        mul = -1.0
+    else:
+        mul = 1.0
+        
+    deg = int(degrees)
+    min = int((degrees*60.0)%60.0)
+    sec = int((degrees*3600.0)%60.0)
+
+    return (deg, min, sec, mul)
+
+#------------------------------------------------------------------------------
+
+def getCoordinateString((latitude, longitude)):
+    """Get the string representation of the given coordinate pair."""
+
+    latitude_str = getLatitudeString(latitude)
+    longitude_str = getLongitudeString(longitude)
+
+    return latitude_str + " " + longitude_str
+
+#------------------------------------------------------------------------------
+
+def getLatitudeString(latitude):
+    """Get a string representation of the given latitude."""
+    return getDegreeString(latitude, ["N", "S"])
+
+#------------------------------------------------------------------------------
+
+def getLongitudeString(longitude):
+    """Get a string representation of the given longitude."""
+
+    return getDegreeString(longitude, ["E", "W"])
+
+#------------------------------------------------------------------------------
+
+def getDegreeString(degree, prefixes):
+    """Get a string representation of the given degree.
+
+    If the sign is positive, prefixes[0], otherwise prefixes[1] will be
+    prepended to the string."""
+
+    if degree<0:
+        prefix = prefixes[1]
+    else:
+        prefix = prefixes[0]
+
+    (deg, min, sec, _sign) = getDegMinSec(degree)
+
+    return u"%s%d\u00b0%02d\u2032%02d\u2033" % (prefix, deg, min, sec)
+
+#------------------------------------------------------------------------------
+
+def getTimeIntervalString(seconds):
+    """Get a more human-friendly representation of the given time interval
+    expressed in seconds."""
+    hours = int(seconds / 3600)
+    minutes = int((seconds / 60) % 60)
+    seconds = int(seconds % 60)
+    return "%d:%02d:%02d" % (hours, minutes, seconds)
+
+#------------------------------------------------------------------------------
+
+def km2nm(km):
+    """Convert the given kilometres into nautical miles."""
+    return km/1.852
+
+#------------------------------------------------------------------------------
+
+def nm2km(nm):
+    """Convert the given nautical miles into kilometres."""
+    return nm*1.852
+
+#------------------------------------------------------------------------------
+
+def radians2km(radians):
+    """Convert the given radians into kilometres"""
+    return radians * EARTH_RADIUS / 1000.0
+
+#------------------------------------------------------------------------------
+
+def radians2nm(radians):
+    """Convert the given radians into nautical miles."""
+    return km2nm(radians2km(radians))
+
+#------------------------------------------------------------------------------
+
+def getDistCourse(latitude1, longitude1, latitude2, longitude2):
+    """Get the distance and course between the two geographical coordinates.
+
+    This function calculates the rhumb distance."""
+    
+    latitude1 = math.radians(latitude1)
+    longitude1 = math.radians(longitude1)
+
+    latitude2 = math.radians(latitude2)
+    longitude2 = math.radians(longitude2)
+
+    dlon_W = (longitude1 - longitude2) % (math.pi*2)
+    dlon_E = (longitude2 - longitude1) % (math.pi*2)
+
+    dphi = math.log(math.tan(latitude2/2 + math.pi/4)/
+                    math.tan(latitude1/2 + math.pi/4))
+
+    if abs(latitude1-latitude2) < math.sqrt(1e-15):
+        q = math.cos(latitude1)
+    else:
+        q = (latitude1-latitude2)/dphi
+
+    if dlon_W < dlon_E:
+        tc = math.atan2(-dlon_W, dphi) % (math.pi*2)
+        d = math.sqrt(math.pow(q*dlon_W, 2) +
+                      math.pow(latitude1-latitude2, 2))
+    else:
+        tc = math.atan2(dlon_E, dphi) % (math.pi*2)
+        d = math.sqrt(math.pow(q*dlon_E, 2) +
+                      math.pow(latitude1-latitude2, 2))
+
+    return (radians2nm(d), math.degrees(tc))
+
