source: src/mlx/pyuipc_sim.py@ 52:09c8dec95072

Last change on this file since 52:09c8dec95072 was 52:09c8dec95072, checked in by István Váradi <ivaradi@…>, 12 years ago

Basic FSUIP simulation works

File size: 15.6 KB
Line 
1# Simulator for the pyuipc module
2#------------------------------------------------------------------------------
3
4import const
5
6import time
7
8#------------------------------------------------------------------------------
9
10# Version constants
11SIM_ANY=0
12SIM_FS98=1
13SIM_FS2K=2
14SIM_CFS2=3
15SIM_CFS1=4
16SIM_FLY=5
17SIM_FS2K2=6
18SIM_FS2K4=7
19
20#------------------------------------------------------------------------------
21
22# Error constants
23ERR_OK=0
24ERR_OPEN=1
25ERR_NOFS=2
26ERR_REGMSG=3
27ERR_ATOM=4
28ERR_MAP=5
29ERR_VIEW=6
30ERR_VERSION=7
31ERR_WRONGFS=8
32ERR_NOTOPEN=9
33ERR_NODATA=10
34ERR_TIMEOUT=11
35ERR_SENDMSG=12
36ERR_DATA=13
37ERR_RUNNING=14
38ERR_SIZE=15
39
40#------------------------------------------------------------------------------
41
42# The version of FSUIPC
43fsuipc_version=0x0401
44lib_version=0x0302
45fs_version=SIM_FS2K4
46
47#------------------------------------------------------------------------------
48
49class FSUIPCException(Exception):
50 """FSUIPC exception class.
51
52 It contains a member variable named errorCode. The string is a text
53 describing the error."""
54
55 errors=["OK",
56 "Attempt to Open when already Open",
57 "Cannot link to FSUIPC or WideClient",
58 "Failed to Register common message with Windows",
59 "Failed to create Atom for mapping filename",
60 "Failed to create a file mapping object",
61 "Failed to open a view to the file map",
62 "Incorrect version of FSUIPC, or not FSUIPC",
63 "Sim is not version requested",
64 "Call cannot execute, link not Open",
65 "Call cannot execute: no requests accumulated",
66 "IPC timed out all retries",
67 "IPC sendmessage failed all retries",
68 "IPC request contains bad data",
69 "Maybe running on WideClient, but FS not running on Server, or wrong FSUIPC",
70 "Read or Write request cannot be added, memory for Process is full"]
71
72 def __init__(self, errorCode):
73 """
74 Construct the exception
75 """
76 if errorCode<len(self.errors):
77 self.errorString = self.errors[errorCode]
78 else:
79 self.errorString = "Unknown error"
80 Exception.__init__(self, self.errorString)
81 self.errorCode = errorCode
82
83 def __str__(self):
84 """
85 Convert the excption to string
86 """
87 return "FSUIPC error: %d (%s)" % (self.errorCode, self.errorString)
88
89#------------------------------------------------------------------------------
90
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
418def open(request):
419 """Open the connection."""
420 return True
421
422#------------------------------------------------------------------------------
423
424def prepare_data(pattern, forRead = True):
425 """Prepare the given pattern for reading and/or writing."""
426 return pattern
427
428#------------------------------------------------------------------------------
429
430def read(data):
431 """Read the given data."""
432 return [values.read(offset) for (offset, type) in data]
433
434#------------------------------------------------------------------------------
435
436def close():
437 """Close the connection."""
438 pass
439
440#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.