Changeset 89:ef4711a984fe


Ignore:
Timestamp:
04/17/12 18:00:09 (13 years ago)
Author:
István Váradi <ivaradi@…>
Branch:
default
Phase:
public
Message:

Added the collection of some further statistics and the finish page

Files:
2 added
8 edited

Legend:

Unmodified
Added
Removed
  • src/mlx/acft.py

    r86 r89  
    55import const
    66import checks
     7import util
    78
    89import time
     
    112113            checker.check(self._flight, self, self._flight.logger,
    113114                          self._aircraftState, aircraftState)
     115
     116        self._flight.handleState(self._aircraftState, aircraftState)
    114117
    115118        self._maxVS = max(self._maxVS, aircraftState.vs)
     
    155158            elif newStage==const.STAGE_PARKING:
    156159                self.logger.message(aircraftState.timestamp, "Block time end")
     160            elif newStage==const.STAGE_END:
     161                flightLength = self._flight.flightTimeEnd - self._flight.flightTimeStart
     162                self.logger.message(aircraftState.timestamp,
     163                                    "Flight time: " +
     164                                    util.getTimeIntervalString(flightLength))
     165                self.logger.message(aircraftState.timestamp,
     166                                    "Flown distance: %.2f NM" % \
     167                                    (self._flight.flownDistance,))               
     168                blockLength = self._flight.blockTimeEnd - self._flight.blockTimeStart
     169                self.logger.message(aircraftState.timestamp,
     170                                    "Block time: " +
     171                                    util.getTimeIntervalString(blockLength))
    157172
    158173    def prepareFlare(self):
  • src/mlx/flight.py

    r86 r89  
    44
    55import const
     6import util
    67
    78import threading
     
    3940        self.aircraft = None
    4041        self.simulator = None
     42
     43        self.blockTimeStart = None
     44        self.flightTimeStart = None
     45        self.flightTimeEnd = None
     46        self.blockTimeEnd = None
     47
     48        self._lastDistanceTime = None
     49        self._previousLatitude = None
     50        self._previousLongitude = None
     51        self.flownDistance = 0.0
     52
     53        self.startFuel = None
     54        self.endFuel = None
    4155
    4256        self._endCondition = threading.Condition()
     
    8094        return self._gui.vref
    8195
     96    def handleState(self, oldState, currentState):
     97        """Handle a new state information."""
     98        self._updateFlownDistance(currentState)
     99       
     100        self.endFuel = sum(currentState.fuel)
     101        if self.startFuel is None:
     102            self.startFuel = self.endFuel
     103
    82104    def setStage(self, timestamp, stage):
    83105        """Set the flight stage.
     
    88110            self._gui.setStage(stage)
    89111            self.logger.stage(timestamp, stage)
    90             if stage==const.STAGE_END:
     112            if stage==const.STAGE_PUSHANDTAXI:
     113                self.blockTimeStart = timestamp
     114            elif stage==const.STAGE_TAKEOFF:
     115                self.flightTimeStart = timestamp
     116            elif stage==const.STAGE_TAXIAFTERLAND:
     117                self.flightTimeEnd = timestamp
     118            elif stage==const.STAGE_PARKING:
     119                self.blockTimeEnd = timestamp
     120            elif stage==const.STAGE_END:
    91121                with self._endCondition:
    92122                    self._endCondition.notify()
     
    133163                self._endCondition.wait(1)
    134164
     165    def _updateFlownDistance(self, currentState):
     166        """Update the flown distance."""
     167        if not currentState.onTheGround:
     168            updateData = False
     169            if self._lastDistanceTime is None or \
     170               self._previousLatitude is None or \
     171               self._previousLongitude is None:
     172                updateData = True
     173            elif currentState.timestamp >= (self._lastDistanceTime + 30.0):
     174                updateData = True
     175                self.flownDistance += self._getDistance(currentState)
     176
     177            if updateData:
     178                self._previousLatitude = currentState.latitude
     179                self._previousLongitude = currentState.longitude
     180                self._lastDistanceTime = currentState.timestamp
     181        else:
     182            if self._lastDistanceTime is not None and \
     183               self._previousLatitude is not None and \
     184               self._previousLongitude is not None:
     185                self.flownDistance += self._getDistance(currentState)
     186               
     187            self._lastDistanceTime = None
     188
     189    def _getDistance(self, currentState):
     190        """Get the distance between the previous and the current state."""
     191        return util.getDistCourse(self._previousLatitude, self._previousLongitude,
     192                                  currentState.latitude, currentState.longitude)[0]
     193
    135194#---------------------------------------------------------------------------------------
  • src/mlx/fs.py

    r59 r89  
    5151    - timestamp: the simulator time of the measurement in seconds since the
    5252    epoch (float)
     53    - latitude (in degrees, North is positive)
     54    - longitude (in degrees, East is positive)
    5355    - paused: a boolean indicating if the flight simulator is paused for
    5456    whatever reason (it could be a pause mode, or a menu, a dialog, or a
     
    5860    - overspeed: a boolean indicating if the aircraft is in overspeed
    5961    - stalled: a boolean indicating if the aircraft is stalled
    60     - onTheGround: a boolean indicating if the aircraft is on the ground
     62    - onTheGround: a boolean indicating if the aircraft is on the ground   
    6163    - zfw: the zero-fuel weight in kilograms (float)
    6264    - grossWeight: the gross weight in kilograms (float)
  • src/mlx/fsuipc.py

    r61 r89  
    746746    object describing the aircraft's state."""
    747747    monitoringData = [("paused", 0x0264, "H"),
     748                      ("latitude", 0x0560, "l"),
     749                      ("longitude", 0x0568, "l"),
    748750                      ("frozen", 0x3364, "H"),
    749751                      ("replay", 0x0628, "d"),
     
    872874       
    873875        state.timestamp = timestamp
     876
     877        state.latitude = data[self._monidx_latitude] * \
     878                         90.0 / 10001750.0 / 65536.0 / 65536.0
     879
     880        state.longitude = data[self._monidx_longitude] * \
     881                          360.0 / 65536.0 / 65536.0 / 65536.0 / 65536.0
     882        if state.longitude>180.0: state.longitude = 360.0 - state.longitude
    874883       
    875884        state.paused = data[self._monidx_paused]!=0 or \
  • src/mlx/gui/flight.py

    r88 r89  
    66import mlx.fs as fs
    77from mlx.checks import PayloadChecker
     8import mlx.util as util
    89
    910import datetime
     
    14771478    def activate(self):
    14781479        """Called when the page is activated."""
    1479         self._flightEnded = False
    1480        
    14811480        self._starButton.set_sensitive(True)
    14821481        self._starButton.set_active(False)
     
    15161515
    15171516        self._vref.set_sensitive(False)
     1517        # FIXME: Perhaps a separate initialize() call which would set up
     1518        # defaults?
     1519        self._flightEnded = False
    15181520
    15191521    def _starButtonClicked(self, button):
     
    15571559    def _forwardClicked(self, button):
    15581560        """Called when the forward button is clicked."""
    1559         #self._wizard.nextPage()
    1560         self.finalize()
     1561        self._wizard.nextPage()
     1562
     1563#-----------------------------------------------------------------------------
     1564
     1565class FinishPage(Page):
     1566    """Flight finish page."""
     1567    def __init__(self, wizard):
     1568        """Construct the finish page."""
     1569        help = "There are some statistics about your flight below.\n\n" \
     1570               "Review the data, also on earlier pages, and if you are\n" \
     1571               "satisfied, you can save or send your PIREP."
     1572
     1573        super(FinishPage, self).__init__(wizard, "Finish", help)
     1574       
     1575        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
     1576                                  xscale = 0.0, yscale = 0.0)
     1577
     1578        table = gtk.Table(5, 2)
     1579        table.set_row_spacings(4)
     1580        table.set_col_spacings(16)
     1581        table.set_homogeneous(True)
     1582        alignment.add(table)
     1583        self.setMainWidget(alignment)
     1584
     1585        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
     1586        label = gtk.Label("Flight rating:")
     1587        labelAlignment.add(label)
     1588        table.attach(labelAlignment, 0, 1, 0, 1)
     1589
     1590        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
     1591        self._flightRating = gtk.Label()
     1592        self._flightRating.set_width_chars(7)
     1593        self._flightRating.set_alignment(0.0, 0.5)
     1594        self._flightRating.set_use_markup(True)
     1595        labelAlignment.add(self._flightRating)
     1596        table.attach(labelAlignment, 1, 2, 0, 1)
     1597
     1598        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
     1599        label = gtk.Label("Flight time:")
     1600        labelAlignment.add(label)
     1601        table.attach(labelAlignment, 0, 1, 1, 2)
     1602
     1603        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
     1604        self._flightTime = gtk.Label()
     1605        self._flightTime.set_width_chars(10)
     1606        self._flightTime.set_alignment(0.0, 0.5)
     1607        self._flightTime.set_use_markup(True)
     1608        labelAlignment.add(self._flightTime)
     1609        table.attach(labelAlignment, 1, 2, 1, 2)
     1610
     1611        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
     1612        label = gtk.Label("Block time:")
     1613        labelAlignment.add(label)
     1614        table.attach(labelAlignment, 0, 1, 2, 3)
     1615
     1616        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
     1617        self._blockTime = gtk.Label()
     1618        self._blockTime.set_width_chars(10)
     1619        self._blockTime.set_alignment(0.0, 0.5)
     1620        self._blockTime.set_use_markup(True)
     1621        labelAlignment.add(self._blockTime)
     1622        table.attach(labelAlignment, 1, 2, 2, 3)
     1623
     1624        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
     1625        label = gtk.Label("Distance flown:")
     1626        labelAlignment.add(label)
     1627        table.attach(labelAlignment, 0, 1, 3, 4)
     1628
     1629        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
     1630        self._distanceFlown = gtk.Label()
     1631        self._distanceFlown.set_width_chars(10)
     1632        self._distanceFlown.set_alignment(0.0, 0.5)
     1633        self._distanceFlown.set_use_markup(True)
     1634        labelAlignment.add(self._distanceFlown)
     1635        table.attach(labelAlignment, 1, 2, 3, 4)
     1636       
     1637        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
     1638        label = gtk.Label("Fuel used:")
     1639        labelAlignment.add(label)
     1640        table.attach(labelAlignment, 0, 1, 4, 5)
     1641
     1642        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
     1643        self._fuelUsed = gtk.Label()
     1644        self._fuelUsed.set_width_chars(10)
     1645        self._fuelUsed.set_alignment(0.0, 0.5)
     1646        self._fuelUsed.set_use_markup(True)
     1647        labelAlignment.add(self._fuelUsed)
     1648        table.attach(labelAlignment, 1, 2, 4, 5)
     1649
     1650        self._saveButton = self.addButton("S_ave PIREP...")
     1651        self._saveButton.set_use_underline(True)
     1652        #self._saveButton.connect("clicked", self._saveClicked)
     1653       
     1654        self._sendButton = self.addButton("_Send PIREP...", True)
     1655        self._sendButton.set_use_underline(True)
     1656        #self._sendButton.connect("clicked", self._sendClicked)
     1657
     1658    def activate(self):
     1659        """Activate the page."""
     1660        flight = self._wizard.gui._flight
     1661        rating = flight.logger.getRating()
     1662        if rating<0:
     1663            self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
     1664        else:
     1665            self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
     1666
     1667        flightLength = flight.flightTimeEnd - flight.flightTimeStart
     1668        self._flightTime.set_markup("<b>%s</b>" % \
     1669                                    (util.getTimeIntervalString(flightLength),))
     1670       
     1671        blockLength = flight.blockTimeEnd - flight.blockTimeStart
     1672        self._blockTime.set_markup("<b>%s</b>" % \
     1673                                   (util.getTimeIntervalString(blockLength),))
     1674
     1675        self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
     1676                                       (flight.flownDistance,))
     1677       
     1678        self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
     1679                                  (flight.endFuel - flight.startFuel,))
    15611680
    15621681#-----------------------------------------------------------------------------
     
    15881707        self._landingPage = LandingPage(self)
    15891708        self._pages.append(self._landingPage)
     1709        self._pages.append(FinishPage(self))
    15901710       
    15911711        maxWidth = 0
  • src/mlx/gui/monitor.py

    r77 r89  
    44
    55import mlx.const as const
     6import mlx.util as util
    67
    78import time
     
    170171        table.attach(label, 4, 5, 6, 7)
    171172        table.attach(self._windDirection, 5, 6, 6, 7)
     173
     174        (label, self._position) = self._createLabeledEntry("Position:", 25)
     175        table.attach(label, 6, 7, 6, 7)
     176        table.attach(self._position, 7, 10, 6, 7)
    172177
    173178        alignment.add(table)
     
    240245            self._windSpeed.set_text("-")
    241246            self._windDirection.set_text("-")
     247            self._position.set_text("-")
    242248        else:
    243249            self._timestamp.set_text(time.strftime("%H:%M:%S",
     
    304310            self._windSpeed.set_text("%.0f" % (aircraftState.windSpeed,))
    305311            self._windDirection.set_text("%03.0f" % (aircraftState.windDirection,))
     312            self._position.set_text(util.getCoordinateString((aircraftState.latitude,
     313                                                              aircraftState.longitude)))
    306314
    307315#------------------------------------------------------------------------------
  • src/mlx/pyuipc_sim.py

    r88 r89  
    184184        self.flapsNotches = [0, 1, 2, 5, 10, 15, 25, 30, 40]
    185185        self.fuelCapacities = [10000.0, 5000.0, 5000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
     186
     187        self.latitude = 47.5
     188        self.longitude = 19.05
    186189       
    187190        self.paused = False
     
    291294        elif offset==0x036d:       # Overspeed
    292295            return 1 if self.overspeed else 0
     296        elif offset==0x0560:       # Latitude
     297            return long(self.latitude * 10001750.0 * 65536.0 * 65536.0 / 90.0)
     298        elif offset==0x0568:       # Longitude
     299            return long(self.longitude * 65536.0 * 65536.0 * 65536.0 * 65536.0 / 360.0)
    293300        elif offset==0x0570:       # Altitude
    294301            return long(self.altitude * const.FEETTOMETRES * 65536.0 * 65536.0)
     
    472479        elif offset==0x036d:       # Overspeed
    473480            self.overspeed = value!=0
     481        elif offset==0x0560:       # Latitude
     482            self.latitude = value * 90.0 / 10001750.0 / 65536.0 / 65536.0
     483        elif offset==0x0568:       # Longitude
     484            self.longitude = value * 360.0 / 65536.0 / 65536.0 / 65536.0 / 65536.0
    474485        elif offset==0x0570:       # Altitude
    475486            self.altitude = value / const.FEETTOMETRES / 65536.0 / 65536.0
     
    908919        self._valueHandlers["airPath"] = (0x3c00, -256,  lambda value: value,
    909920                                          lambda word: word)
     921        self._valueHandlers["latitude"] = (0x0560, "l",
     922                                           lambda value: value * 90.0 /
     923                                           10001750.0 / 65536.0 / 65536.0,
     924                                           lambda word: long(float(word) *
     925                                                             10001750.0 *
     926                                                             65536.0 * 65536.0 / 90.0))
     927        self._valueHandlers["longitude"] = (0x0568, "l",
     928                                            lambda value: value * 360.0 /
     929                                            65536.0 / 65536.0 / 65536.0 / 65536.0,
     930                                            lambda word: long(float(word) *
     931                                                              65536.0 * 65536.0 *
     932                                                              65536.0 * 65536.0 /
     933                                                              360.0))
    910934        self._valueHandlers["paused"] = (0x0264, "H", CLI.bool2str, CLI.str2bool)
    911935        self._valueHandlers["frozen"] = (0x3364, "H", CLI.bool2str, CLI.str2bool)
  • src/mlx/util.py

    r27 r89  
    11# Various utilities
    22
     3import math
     4
     5#------------------------------------------------------------------------------
     6
     7# The average of the radius at the poles and a the equator, in metres
     8#EARTH_RADIUS=6367467.4
     9EARTH_RADIUS=6371000
     10#EARTH_RADIUS=6378137
     11
     12#------------------------------------------------------------------------------
     13
     14def getDegMinSec(degrees):
     15    """Break up the given floating point degrees value into a tuple.
     16
     17    The tuple contains 4 items:
     18    - the degrees as an integer
     19    - the minutes as an integer
     20    - the seconds as an integer
     21    - 1.0 if the value was non-negative, -1.0 if it was negative."""
     22   
     23    if degrees<0:
     24        degrees = -degrees
     25        mul = -1.0
     26    else:
     27        mul = 1.0
     28       
     29    deg = int(degrees)
     30    min = int((degrees*60.0)%60.0)
     31    sec = int((degrees*3600.0)%60.0)
     32
     33    return (deg, min, sec, mul)
     34
     35#------------------------------------------------------------------------------
     36
     37def getCoordinateString((latitude, longitude)):
     38    """Get the string representation of the given coordinate pair."""
     39
     40    latitude_str = getLatitudeString(latitude)
     41    longitude_str = getLongitudeString(longitude)
     42
     43    return latitude_str + " " + longitude_str
     44
     45#------------------------------------------------------------------------------
     46
     47def getLatitudeString(latitude):
     48    """Get a string representation of the given latitude."""
     49    return getDegreeString(latitude, ["N", "S"])
     50
     51#------------------------------------------------------------------------------
     52
     53def getLongitudeString(longitude):
     54    """Get a string representation of the given longitude."""
     55
     56    return getDegreeString(longitude, ["E", "W"])
     57
     58#------------------------------------------------------------------------------
     59
     60def getDegreeString(degree, prefixes):
     61    """Get a string representation of the given degree.
     62
     63    If the sign is positive, prefixes[0], otherwise prefixes[1] will be
     64    prepended to the string."""
     65
     66    if degree<0:
     67        prefix = prefixes[1]
     68    else:
     69        prefix = prefixes[0]
     70
     71    (deg, min, sec, _sign) = getDegMinSec(degree)
     72
     73    return u"%s%d\u00b0%02d\u2032%02d\u2033" % (prefix, deg, min, sec)
     74
     75#------------------------------------------------------------------------------
     76
     77def getTimeIntervalString(seconds):
     78    """Get a more human-friendly representation of the given time interval
     79    expressed in seconds."""
     80    hours = int(seconds / 3600)
     81    minutes = int((seconds / 60) % 60)
     82    seconds = int(seconds % 60)
     83    return "%d:%02d:%02d" % (hours, minutes, seconds)
     84
     85#------------------------------------------------------------------------------
     86
     87def km2nm(km):
     88    """Convert the given kilometres into nautical miles."""
     89    return km/1.852
     90
     91#------------------------------------------------------------------------------
     92
     93def nm2km(nm):
     94    """Convert the given nautical miles into kilometres."""
     95    return nm*1.852
     96
     97#------------------------------------------------------------------------------
     98
     99def radians2km(radians):
     100    """Convert the given radians into kilometres"""
     101    return radians * EARTH_RADIUS / 1000.0
     102
     103#------------------------------------------------------------------------------
     104
     105def radians2nm(radians):
     106    """Convert the given radians into nautical miles."""
     107    return km2nm(radians2km(radians))
     108
     109#------------------------------------------------------------------------------
     110
     111def getDistCourse(latitude1, longitude1, latitude2, longitude2):
     112    """Get the distance and course between the two geographical coordinates.
     113
     114    This function calculates the rhumb distance."""
     115   
     116    latitude1 = math.radians(latitude1)
     117    longitude1 = math.radians(longitude1)
     118
     119    latitude2 = math.radians(latitude2)
     120    longitude2 = math.radians(longitude2)
     121
     122    dlon_W = (longitude1 - longitude2) % (math.pi*2)
     123    dlon_E = (longitude2 - longitude1) % (math.pi*2)
     124
     125    dphi = math.log(math.tan(latitude2/2 + math.pi/4)/
     126                    math.tan(latitude1/2 + math.pi/4))
     127
     128    if abs(latitude1-latitude2) < math.sqrt(1e-15):
     129        q = math.cos(latitude1)
     130    else:
     131        q = (latitude1-latitude2)/dphi
     132
     133    if dlon_W < dlon_E:
     134        tc = math.atan2(-dlon_W, dphi) % (math.pi*2)
     135        d = math.sqrt(math.pow(q*dlon_W, 2) +
     136                      math.pow(latitude1-latitude2, 2))
     137    else:
     138        tc = math.atan2(dlon_E, dphi) % (math.pi*2)
     139        d = math.sqrt(math.pow(q*dlon_E, 2) +
     140                      math.pow(latitude1-latitude2, 2))
     141
     142    return (radians2nm(d), math.degrees(tc))
     143
Note: See TracChangeset for help on using the changeset viewer.