Changeset 52:09c8dec95072


Ignore:
Timestamp:
04/01/12 09:58:25 (12 years ago)
Author:
István Váradi <ivaradi@…>
Branch:
default
Phase:
public
Message:

Basic FSUIP simulation works

Location:
src/mlx
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • src/mlx/acft.py

    r29 r52  
    462462    The aircraft type-specific values in the aircraft state have the following
    463463    structure:
    464     - fuel: left, right, left aux, right aix
     464    - fuel: left, right, left aux, right aux
    465465    - rpm: left, right
    466466    - reverser: left, right."""
  • src/mlx/const.py

    r51 r52  
    1010# The ratio between lbs and kg
    1111LBSTOKG=0.4536
     12
     13# The ratio between kgs and lbs
     14KGSTOLB=1/LBSTOKG
    1215
    1316# The ratio between feet and metre
  • src/mlx/pyuipc_sim.py

    r51 r52  
    11# Simulator for the pyuipc module
    22#------------------------------------------------------------------------------
     3
     4import const
    35
    46import time
     
    8789#------------------------------------------------------------------------------
    8890
     91class Values(object):
     92    """The values that can be read from 'FSUIPC'."""
     93    # Fuel data index: centre tank
     94    FUEL_CENTRE = 0
     95
     96    # Fuel data index: left main tank
     97    FUEL_LEFT = 1
     98
     99    # Fuel data index: right main tank
     100    FUEL_RIGHT = 2
     101
     102    # Fuel data index: left aux tank
     103    FUEL_LEFT_AUX = 3
     104
     105    # Fuel data index: right aux tank
     106    FUEL_RIGHT_AUX = 4
     107
     108    # Fuel data index: left tip tank
     109    FUEL_LEFT_TIP = 5
     110
     111    # Fuel data index: right tip tank
     112    FUEL_RIGHT_AUX = 6
     113
     114    # Fuel data index: external 1 tank
     115    FUEL_EXTERNAL_1 = 7
     116
     117    # Fuel data index: external 2 tank
     118    FUEL_EXTERNAL_2 = 8
     119
     120    # Fuel data index: centre 2 tank
     121    FUEL_CENTRE_2 = 9
     122
     123    # The number of fuel tank entries
     124    NUM_FUEL = FUEL_CENTRE_2 + 1
     125
     126    # Engine index: engine #1
     127    ENGINE_1 = 0
     128
     129    # Engine index: engine #2
     130    ENGINE_2 = 1
     131
     132    # Engine index: engine #3
     133    ENGINE_3 = 2
     134
     135    @staticmethod
     136    def _convertFrequency(frequency):
     137        """Convert the given frequency into BCD."""
     138        return Values._convertBCD(int(frequency-100.0)*100)
     139
     140    @staticmethod
     141    def _convertBCD(value):
     142        """Convert the given value into BCD format."""
     143        bcd = (value/1000) % 10
     144        bcd <<= 4
     145        bcd |= (value/100) & 10
     146        bcd <<= 4
     147        bcd |= (value/10) % 10
     148        bcd <<= 4
     149        bcd |= value % 10
     150        return bcd
     151       
     152    def __init__(self):
     153        """Construct the values with defaults."""
     154        self._timeOffset = 0
     155        self.airPath = "C:\\Program Files\\Microsoft Games\\" \
     156                       "FS9\\Aircraft\\Cessna\\cessna172.air"
     157        self.aircraftName = "Cessna 172SP"
     158        self.flapsNotches = [0, 1, 2, 5, 10, 15, 25, 30, 40]
     159        self.fuelCapacities = [10000.0, 5000.0, 5000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
     160       
     161        self.paused = False
     162        self.frozen = False
     163        self.replay = False
     164        self.slew = False
     165        self.overspeed = False
     166        self.stalled = False
     167        self.onTheGround = True
     168       
     169        self.zfw = 50000.0
     170       
     171        self.fuelWeights = [0.0, 3000.0, 3000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
     172        # FIXME: check for realistic values
     173        self.fuelWeight = 3.5
     174
     175        self.heading = 220.0
     176        self.pitch = 0.0
     177        self.bank = 0.0
     178
     179        self.ias = 0.0
     180        self.vs = 0.0
     181
     182        self.radioAltitude = None
     183        self.altitude = 513.0
     184
     185        self.gLoad = 1.0
     186
     187        self.flapsControl = 0.0
     188        self.flaps = 0.0
     189
     190        self.navLightsOn = True
     191        self.antiCollisionLightsOn = False
     192        self.landingLightsOn = False
     193        self.strobeLightsOn = False
     194
     195        self.pitot = False
     196        self.parking = True
     197
     198        self.noseGear = 1.0
     199
     200        self.spoilersArmed = False
     201        self.spoilers = 0.0
     202
     203        self.altimeter = 1013.0
     204
     205        self.nav1 = 117.3
     206        self.nav2 = 109.5
     207        self.squawk = 2200
     208
     209        self.windSpeed = 8.0
     210        self.windDirection = 300.0
     211       
     212        self.n1 = [0.0, 0.0, 0.0]
     213        self.throttles = [0.0, 0.0, 0.0]
     214
     215    def read(self, offset):
     216        """Read the value at the given offset."""
     217        try:
     218            return self._read(offset)
     219        except Exception, e:
     220            print "failed to read offset %04x: %s" % (offset, str(e))
     221            raise FSUIPCException(ERR_DATA)
     222
     223    def _read(self, offset):
     224        """Read the value at the given offset."""
     225        if offset==0x023a:         # Second of time
     226            return self._readUTC().tm_sec
     227        elif offset==0x023b:       # Hour of Zulu time
     228            return self._readUTC().tm_hour
     229        elif offset==0x023c:       # Minute of Zulu time
     230            return self._readUTC().tm_min
     231        elif offset==0x023e:       # Day number on year
     232            return self._readUTC().tm_yday
     233        elif offset==0x0240:       # Year in FS
     234            return self._readUTC().tm_year
     235        elif offset==0x0264:       # Paused
     236            return 1 if self.paused else 0
     237        elif offset==0x029c:       # Pitot
     238            return 1 if self.pitot else 0
     239        elif offset==0x02b4:       # Ground speed
     240            # FIXME: calculate TAS first, then from the heading and
     241            # wind the GS
     242            return int(self.ias * 65536.0 * 1852.0 / 3600.0)
     243        elif offset==0x02bc:       # IAS
     244            return int(self.ias * 128.0)
     245        elif offset==0x02c8:       # VS
     246            return int(self.vs * const.FEETTOMETRES * 256.0 / 60.0)
     247        elif offset==0x0330:       # Altimeter
     248            return int(self.altimeter * 16.0)
     249        elif offset==0x0350:       # NAV1
     250            return Values._convertFrequency(self.nav1)
     251        elif offset==0x0352:       # NAV2
     252            return Values._convertFrequency(self.nav2)
     253        elif offset==0x0354:       # Squawk
     254            return Values._convertBCD(self.squawk)
     255        elif offset==0x0366:       # Stalled
     256            return 1 if self.stalled else 0
     257        elif offset==0x036c:       # Stalled
     258            return 1 if self.stalled else 0
     259        elif offset==0x036d:       # Overspeed
     260            return 1 if self.overspeed else 0
     261        elif offset==0x0570:       # Altitude
     262            return long(self.altitude * const.FEETTOMETRES * 65536.0 * 65536.0)
     263        elif offset==0x0578:       # Pitch
     264            return int(self.pitch * 65536.0 * 65536.0 / 360.0)
     265        elif offset==0x057c:       # Bank
     266            return int(self.bank * 65536.0 * 65536.0 / 360.0)
     267        elif offset==0x0580:       # Heading
     268            return int(self.heading * 65536.0 * 65536.0 / 360.0)
     269        elif offset==0x05dc:       # Slew
     270            return 1 if self.slew else 0
     271        elif offset==0x0628:       # Replay
     272            return 1 if self.replay else 0
     273        elif offset==0x088c:       # Engine #1 throttle
     274            return self._getThrottle(self.ENGINE_1)
     275        elif offset==0x0924:       # Engine #2 throttle
     276            return self._getThrottle(self.ENGINE_2)
     277        elif offset==0x09bc:       # Engine #3 throttle
     278            return self._getThrottle(self.ENGINE_3)
     279        elif offset==0x0af4:       # Fuel weight
     280            return int(self.fuelWeight * 256.0)
     281        elif offset==0x0b74:       # Centre tank level
     282            return self._getFuelLevel(self.FUEL_CENTRE)
     283        elif offset==0x0b78:       # Centre tank capacity
     284            return self._getFuelCapacity(self.FUEL_CENTRE)
     285        elif offset==0x0b7c:       # Left tank level
     286            return self._getFuelLevel(self.FUEL_LEFT)
     287        elif offset==0x0b80:       # Left tank capacity
     288            return self._getFuelCapacity(self.FUEL_LEFT)
     289        elif offset==0x0b84:       # Left aux tank level
     290            return self._getFuelLevel(self.FUEL_LEFT_AUX)
     291        elif offset==0x0b88:       # Left aux tank capacity
     292            return self._getFuelCapacity(self.FUEL_LEFT_AUX)
     293        elif offset==0x0b8c:       # Left tip tank level
     294            return self._getFuelLevel(self.FUEL_LEFT_TIP)
     295        elif offset==0x0b90:       # Left tip tank capacity
     296            return self._getFuelCapacity(self.FUEL_LEFT_TIP)
     297        elif offset==0x0b94:       # Right aux tank level
     298            return self._getFuelLevel(self.FUEL_RIGHT_AUX)
     299        elif offset==0x0b98:       # Right aux tank capacity
     300            return self._getFuelCapacity(self.FUEL_RIGHT_AUX)
     301        elif offset==0x0b9c:       # Right tank level
     302            return self._getFuelLevel(self.FUEL_RIGHT)
     303        elif offset==0x0ba0:       # Right tank capacity
     304            return self._getFuelCapacity(self.FUEL_RIGHT)
     305        elif offset==0x0ba4:       # Right tip tank level
     306            return self._getFuelLevel(self.FUEL_RIGHT_TIP)
     307        elif offset==0x0ba8:       # Right tip tank capacity
     308            return self._getFuelCapacity(self.FUEL_RIGHT_TIP)
     309        elif offset==0x0bc8:       # Parking
     310            return 1 if self.parking else 0
     311        elif offset==0x0bcc:       # Spoilers armed
     312            return 1 if self.spoilersArmed else 0
     313        elif offset==0x0bd0:       # Spoilers
     314            return 0 if self.spoilers == 0 \
     315                else int(self.spoilers * (16383 - 4800) + 4800)
     316        elif offset==0x0bdc:       # Flaps control
     317            numNotchesM1 = len(self.flapsNotches) - 1
     318            flapsIncrement = 16383.0 / numNotchesM1
     319            index = 0
     320            while index<numNotchesM1 and \
     321                  self.flapsControl<self.flapsNotches[index]:
     322                index += 1
     323               
     324            if index==numNotchesM1:
     325                return 16383
     326            else:
     327                return int((self.flapsControl-self.flapsNotches[index]) * \
     328                           flapsIncrement / \
     329                           (self.flapsNotches[index+1] - self.flapsNotches[index]))
     330        elif offset==0x0be0 or offset==0x0be4:    # Flaps left and  right
     331            return self.flaps * 16383.0 / self.flapsNotches[-1]       
     332        elif offset==0x0bec:       # Nose gear
     333            return int(self.noseGear * 16383.0)
     334        elif offset==0x0d0c:       # Lights
     335            lights = 0
     336            if self.navLightsOn: lights |= 0x01
     337            if self.antiCollisionLightsOn: lights |= 0x02
     338            if self.landingLightsOn: lights |= 0x04
     339            if self.strobeLightsOn: lights |= 0x10
     340            return lights
     341        elif offset==0x0e90:       # Wind speed
     342            return int(self.windSpeed)
     343        elif offset==0x0e92:       # Wind direction
     344            return int(self.windDirection * 65536.0 / 360.0)
     345        elif offset==0x11ba:       # G-Load
     346            return int(self.gLoad * 625.0)
     347        elif offset==0x11c6:       # Mach
     348            # FIXME: calculate from IAS, altitude and QNH
     349            return int(self.ias * 0.05 * 20480.)
     350        elif offset==0x1244:       # Centre 2 tank level
     351            return self._getFuelLevel(self.FUEL_CENTRE_2)
     352        elif offset==0x1248:       # Centre 2 tank capacity
     353            return self._getFuelCapacity(self.FUEL_CENTRE_2)
     354        elif offset==0x1254:       # External 1 tank level
     355            return self._getFuelLevel(self.FUEL_EXTERNAL_1)
     356        elif offset==0x1258:       # External 1 tank capacity
     357            return self._getFuelCapacity(self.FUEL_EXTERNAL_1)
     358        elif offset==0x125c:       # External 2 tank level
     359            return self._getFuelLevel(self.FUEL_EXTERNAL_2)
     360        elif offset==0x1260:       # External 2 tank capacity
     361            return self._getFuelCapacity(self.FUEL_EXTERNAL_2)
     362        elif offset==0x2000:       # Engine #1 N1
     363            return self.n1[self.ENGINE_1]
     364        elif offset==0x2100:       # Engine #2 N1
     365            return self.n1[self.ENGINE_2]
     366        elif offset==0x2200:       # Engine #3 N1
     367            return self.n1[self.ENGINE_3]
     368        elif offset==0x30c0:       # Grossweight
     369            return (self.zfw + sum(self.fuelWeights)) * const.KGSTOLB
     370        elif offset==0x31e4:       # Radio altitude
     371            # FIXME: if self.radioAltitude is None, calculate from the
     372            # altitude with some, perhaps random, ground altitude
     373            # value
     374            radioAltitude = (self.altitude - 517) \
     375                if self.radioAltitude is None else self.radioAltitude
     376            return (radioAltitude * const.FEETTOMETRES * 65536.0)
     377        elif offset==0x3364:       # Frozen
     378            return 1 if self.frozen else 0
     379        elif offset==0x3bfc:       # ZFW
     380            return int(self.zfw) * 256.0 * const.KGSTOLB
     381        elif offset==0x3c00:       # Path of the current AIR file
     382            return self.airPath
     383        elif offset==0x3d00:       # Name of the current aircraft
     384            return self.aircraftName
     385        else:
     386            print "Unhandled offset: %04x" % (offset,)
     387            raise FSUIPCException(ERR_DATA)
     388       
     389    def _readUTC(self):
     390        """Read the UTC time.
     391       
     392        The current offset is added to it."""
     393        return time.gmtime(time.time() + self._timeOffset)
     394       
     395    def _getFuelLevel(self, index):
     396        """Get the fuel level for the fuel tank with the given
     397        index."""
     398        # FIXME: check if the constants are correct
     399        return 0 if self.fuelCapacities[index]==0.0 else \
     400            int(self.fuelWeights[index] * 65536.0 / self.fuelCapacities[index])
     401   
     402    def _getFuelCapacity(self, index):
     403        """Get the capacity of the fuel tank with the given index."""
     404        # FIXME: check if the constants are correct
     405        return int(self.fuelCapacities[index] * const.KGSTOLB * 128.0 /
     406                   self.fuelWeight)
     407
     408    def _getThrottle(self, index):
     409        """Get the throttle value for the given index."""
     410        return int(self.throttles[index] * 16383.0)
     411   
     412#------------------------------------------------------------------------------
     413
     414values = Values()
     415
     416#------------------------------------------------------------------------------
     417
    89418def open(request):
    90419    """Open the connection."""
     
    101430def read(data):
    102431    """Read the given data."""
    103     result = []
    104     for (offset, type) in data:
    105         if offset==0x023a:         # Second of time
    106             result.append(time.gmtime().tm_sec)
    107         elif offset==0x023b:       # Hour of Zulu time
    108             result.append(time.gmtime().tm_hour)
    109         elif offset==0x023c:       # Minute of Zulu time
    110             result.append(time.gmtime().tm_min)
    111         elif offset==0x023e:       # Day number on year
    112             result.append(time.gmtime().tm_yday)
    113         elif offset==0x0240:       # Year in FS
    114             result.append(time.gmtime().tm_year)
    115         elif offset==0x3c00:       # Path of the current AIR file
    116             result.append("c:\\Program Files\\Microsoft Games\\FS9\\Aircraft\\kutya")
    117         elif offset==0x3d00:       # Name of the current aircraft
    118             result.append("Cessna 172")
    119         else:
    120             print "Unhandled offset: %04x" % (offset,)
    121             raise FSUIPCException(ERR_DATA)
    122     return result
     432    return [values.read(offset) for (offset, type) in data]
    123433           
    124434#------------------------------------------------------------------------------
Note: See TracChangeset for help on using the changeset viewer.