source: src/fsuipc.py@ 7:f8d6fda3c66d

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

Added the different type-specific aircraft classes and the type-specific generic FSUIPC-based models

File size: 33.7 KB
Line 
1# Module handling the connection to FSUIPC
2
3#------------------------------------------------------------------------------
4
5import fs
6import const
7import util
8
9import threading
10import os
11import time
12import calendar
13import sys
14
15if os.name == "nt":
16 import pyuipc
17else:
18 import pyuipc_emu as pyuipc
19
20#------------------------------------------------------------------------------
21
22class Handler(threading.Thread):
23 """The thread to handle the FSUIPC requests."""
24 @staticmethod
25 def _callSafe(fun):
26 """Call the given function and swallow any exceptions."""
27 try:
28 return fun()
29 except Exception, e:
30 print >> sys.stderr, str(e)
31 return None
32
33 class Request(object):
34 """A simple, one-shot request."""
35 def __init__(self, forWrite, data, callback, extra):
36 """Construct the request."""
37 self._forWrite = forWrite
38 self._data = data
39 self._callback = callback
40 self._extra = extra
41
42 def process(self, time):
43 """Process the request."""
44 if self._forWrite:
45 pyuipc.write(self._data)
46 Handler._callSafe(lambda: self._callback(True, self._extra))
47 else:
48 values = pyuipc.read(self._data)
49 Handler._callSafe(lambda: self._callback(values, self._extra))
50
51 return True
52
53 def fail(self):
54 """Handle the failure of this request."""
55 if self._forWrite:
56 Handler._callSafe(lambda: self._callback(False, self._extra))
57 else:
58 Handler._callSafe(lambda: self._callback(None, self._extra))
59
60 class PeriodicRequest(object):
61 """A periodic request."""
62 def __init__(self, id, period, data, callback, extra):
63 """Construct the periodic request."""
64 self._id = id
65 self._period = period
66 self._nextFire = time.time() + period
67 self._data = data
68 self._preparedData = None
69 self._callback = callback
70 self._extra = extra
71
72 @property
73 def id(self):
74 """Get the ID of this periodic request."""
75 return self._id
76
77 @property
78 def nextFire(self):
79 """Get the next firing time."""
80 return self._nextFire
81
82 def process(self, time):
83 """Check if this request should be executed, and if so, do so.
84
85 Return a boolean indicating if the request was executed."""
86 if time < self._nextFire:
87 return False
88
89 if self._preparedData is None:
90 self._preparedData = pyuipc.prepare_data(self._data)
91 self._data = None
92
93 values = pyuipc.read(self._preparedData)
94
95 Handler._callSafe(lambda: self._callback(values, self._extra))
96
97 while self._nextFire <= time:
98 self._nextFire += self._period
99
100 return True
101
102 def fail(self):
103 """Handle the failure of this request."""
104 pass
105
106 def __cmp__(self, other):
107 """Compare two periodic requests. They are ordered by their next
108 firing times."""
109 return cmp(self._nextFire, other._nextFire)
110
111 def __init__(self, connectionListener):
112 """Construct the handler with the given connection listener."""
113 threading.Thread.__init__(self)
114
115 self._connectionListener = connectionListener
116
117 self._requestCondition = threading.Condition()
118 self._connectionRequested = False
119
120 self._requests = []
121 self._nextPeriodicID = 1
122 self._periodicRequests = []
123
124 self.daemon = True
125
126 def requestRead(self, data, callback, extra = None):
127 """Request the reading of some data.
128
129 data is a list of tuples of the following items:
130 - the offset of the data as an integer
131 - the type letter of the data as a string
132
133 callback is a function that receives two pieces of data:
134 - the values retrieved or None on error
135 - the extra parameter
136
137 It will be called in the handler's thread!
138 """
139 with self._requestCondition:
140 self._requests.append(Handler.Request(False, data, callback, extra))
141 self._requestCondition.notify()
142
143 def requestWrite(self, data, callback, extra = None):
144 """Request the writing of some data.
145
146 data is a list of tuples of the following items:
147 - the offset of the data as an integer
148 - the type letter of the data as a string
149 - the data to write
150
151 callback is a function that receives two pieces of data:
152 - a boolean indicating if writing was successful
153 - the extra data
154 It will be called in the handler's thread!
155 """
156 with self._requestCondition:
157 self._requests.append(Handler.Request(True, data, callback, extra))
158 self._requestCondition.notify()
159
160 @staticmethod
161 def _readWriteCallback(data, extra):
162 """Callback for the read() and write() calls below."""
163 extra.append(data)
164 with extra[0] as condition:
165 condition.notify()
166
167 def read(self, data):
168 """Read the given data synchronously.
169
170 If a problem occurs, an exception is thrown."""
171 with threading.Condition() as condition:
172 extra = [condition]
173 self._requestRead(data, self._readWriteCallback, extra)
174 while len(extra)<2:
175 condition.wait()
176 if extra[1] is None:
177 raise fs.SimulatorException("reading failed")
178 else:
179 return extra[1]
180
181 def write(self, data):
182 """Write the given data synchronously.
183
184 If a problem occurs, an exception is thrown."""
185 with threading.Condition() as condition:
186 extra = [condition]
187 self._requestWrite(data, self._writeCallback, extra)
188 while len(extra)<2:
189 condition.wait()
190 if extra[1] is None:
191 raise fs.SimulatorException("writing failed")
192
193 def requestPeriodicRead(self, period, data, callback, extra = None):
194 """Request a periodic read of data.
195
196 period is a floating point number with the period in seconds.
197
198 This function returns an identifier which can be used to cancel the
199 request."""
200 with self._requestCondition:
201 id = self._nextPeriodicID
202 self._nextPeriodicID += 1
203 request = Handler.PeriodicRequest(id, period, data, callback, extra)
204 self._periodicRequests.append(request)
205 self._requestCondition.notify()
206 return id
207
208 def clearPeriodic(self, id):
209 """Clear the periodic request with the given ID."""
210 with self._requestCondition:
211 for i in range(0, len(self._periodicRequests)):
212 if self._periodicRequests[i].id==id:
213 del self._periodicRequests[i]
214 return True
215 return False
216
217 def connect(self):
218 """Initiate the connection to the flight simulator."""
219 with self._requestCondition:
220 if not self._connectionRequested:
221 self._connectionRequested = True
222 self._requestCondition.notify()
223
224 def disconnect(self):
225 """Disconnect from the flight simulator."""
226 with self._requestCondition:
227 if self._connectionRequested:
228 self._connectionRequested = False
229 self._requestCondition.notify()
230
231 def run(self):
232 """Perform the operation of the thread."""
233 while True:
234 self._waitConnectionRequest()
235
236 if self._connect():
237 self._handleConnection()
238
239 self._disconnect()
240
241 def _waitConnectionRequest(self):
242 """Wait for a connection request to arrive."""
243 with self._requestCondition:
244 while not self._connectionRequested:
245 self._requestCondition.wait()
246
247 def _connect(self):
248 """Try to connect to the flight simulator via FSUIPC"""
249 while self._connectionRequested:
250 try:
251 pyuipc.open(pyuipc.SIM_FS2K4)
252 description = "(FSUIPC version: 0x%04x, library version: 0x%04x, FS version: %d)" % \
253 (pyuipc.fsuipc_version, pyuipc.lib_version,
254 pyuipc.fs_version)
255 Handler._callSafe(lambda:
256 self._connectionListener.connected(const.SIM_MSFS9,
257 description))
258 return True
259 except Exception, e:
260 print "fsuipc.Handler._connect: connection failed: " + str(e)
261 time.sleep(0.1)
262
263 return False
264
265 def _handleConnection(self):
266 """Handle a living connection."""
267 with self._requestCondition:
268 while self._connectionRequested:
269 if not self._processRequests():
270 return
271 timeout = None
272 if self._periodicRequests:
273 self._periodicRequests.sort()
274 timeout = self._periodicRequests[0].nextFire - time.time()
275 if timeout is None or timeout > 0.0:
276 self._requestCondition.wait(timeout)
277
278 def _disconnect(self):
279 """Disconnect from the flight simulator."""
280 pyuipc.close()
281 Handler._callSafe(lambda: self._connectionListener.disconnected())
282
283 def _failRequests(self, request):
284 """Fail the outstanding, single-shot requuests."""
285 request.fail()
286 with self._requestCondition:
287 for request in self._requests:
288 try:
289 self._requestCondition.release()
290 request.fail()
291 finally:
292 self._requestCondition.acquire()
293 self._requests = []
294
295 def _processRequest(self, request, time):
296 """Process the given request.
297
298 If an exception occurs, we try to reconnect.
299
300 Returns what the request's process() function returned or None if
301 reconnection failed."""
302
303 self._requestCondition.release()
304
305 try:
306 return request.process(time)
307 except Exception as e:
308 print "fsuipc.Handler._processRequest: FSUIPC connection failed (" + \
309 str(e) + ") reconnecting."
310 self._disconnect()
311 self._failRequests(request)
312 if not self._connect(): return None
313 else: return True
314 finally:
315 self._requestCondition.acquire()
316
317 def _processRequests(self):
318 """Process any pending requests.
319
320 Will be called with the request lock held."""
321 while self._connectionRequested and self._periodicRequests:
322 self._periodicRequests.sort()
323 request = self._periodicRequests[0]
324 result = self._processRequest(request, time.time())
325 if result is None: return False
326 elif not result: break
327
328 while self._connectionRequested and self._requests:
329 request = self._requests[0]
330 del self._requests[0]
331
332 if self._processRequest(request, None) is None:
333 return False
334
335 return self._connectionRequested
336
337#------------------------------------------------------------------------------
338
339class Simulator(object):
340 """The simulator class representing the interface to the flight simulator
341 via FSUIPC."""
342 # The basic data that should be queried all the time once we are connected
343 normalData = [ (0x3d00, -256) ]
344
345 def __init__(self, connectionListener, aircraft):
346 """Construct the simulator.
347
348 The aircraft object passed must provide the following members:
349 - type: one of the AIRCRAFT_XXX constants from const.py
350 - modelChanged(aircraftName, modelName): called when the model handling
351 the aircraft has changed.
352 - handleState(aircraftState): handle the given state."""
353 self._aircraft = aircraft
354
355 self._handler = Handler(connectionListener)
356 self._handler.start()
357
358 self._normalRequestID = None
359
360 self._monitoringRequested = False
361 self._monitoring = False
362
363 self._aircraftName = None
364 self._aircraftModel = None
365
366 def connect(self):
367 """Initiate a connection to the simulator."""
368 self._handler.connect()
369 self._startDefaultNormal()
370
371 def startMonitoring(self):
372 """Start the periodic monitoring of the aircraft and pass the resulting
373 state to the aircraft object periodically."""
374 assert not self._monitoringRequested
375 self._monitoringRequested = True
376
377 def stopMonitoring(self):
378 """Stop the periodic monitoring of the aircraft."""
379 assert self._monitoringRequested
380 self._monitoringRequested = False
381
382 def disconnect(self):
383 """Disconnect from the simulator."""
384 assert not self._monitoringRequested
385
386 self._stopNormal()
387 self._handler.disconnect()
388
389 def _startDefaultNormal(self):
390 """Start the default normal periodic request."""
391 assert self._normalRequestID is None
392 self._normalRequestID = self._handler.requestPeriodicRead(1.0,
393 Simulator.normalData,
394 self._handleNormal)
395
396 def _stopNormal(self):
397 """Stop the normal period request."""
398 assert self._normalRequestID is not None
399 self._handler.clearPeriodic(self._normalRequestID)
400 self._normalRequestID = None
401
402 def _handleNormal(self, data, extra):
403 """Handle the reply to the normal request.
404
405 At the beginning the result consists the data for normalData. When
406 monitoring is started, it contains the result also for the
407 aircraft-specific values.
408 """
409 self._setAircraftName(data[0])
410 if self._monitoringRequested and not self._monitoring:
411 self._monitoring = True
412 self._stopNormal()
413 self._startMonitoring()
414 elif self._monitoring and not self._monitoringRequested:
415 self._monitoring = False
416 self._stopNormal()
417 self._startDefaultNormal()
418 elif self._monitoring and self._aircraftModel is not None:
419 aircraftState = self._aircraftModel.getAircraftState(self._aircraft, data)
420 self._aircraft.handleState(aircraftState)
421
422 def _setAircraftName(self, name):
423 """Set the name of the aicraft and if it is different from the
424 previous, create a new model for it.
425
426 If so, also notifty the aircraft about the change."""
427 if name==self._aircraftName:
428 return
429
430 self._aircraftName = name
431 if self._aircraftModel is None or \
432 not self._aircraftModel.doesHandle(name):
433 self._setAircraftModel(AircraftModel.create(self._aircraft, name))
434
435 self._aircraft.modelChanged(self._aircraftName,
436 self._aircraftModel.name)
437
438 def _setAircraftModel(self, model):
439 """Set a new aircraft model.
440
441 It will be queried for the data to monitor and the monitoring request
442 will be replaced by a new one."""
443 self._aircraftModel = model
444
445 if self._monitoring:
446 self._handler.clearPeriodic(self._normalRequestID)
447 self._startMonitoring()
448
449 def _startMonitoring(self):
450 """Start monitoring with the current aircraft model."""
451 assert self._monitoring
452
453 data = Simulator.normalData[:]
454 self._aircraftModel.addMonitoringData(data)
455
456 self._normalRequestID = \
457 self._handler.requestPeriodicRead(1.0, data,
458 self._handleNormal)
459
460#------------------------------------------------------------------------------
461
462class AircraftModel(object):
463 """Base class for the aircraft models.
464
465 Aircraft models handle the data arriving from FSUIPC and turn it into an
466 object describing the aircraft's state."""
467 monitoringData = [("year", 0x0240, "H"),
468 ("dayOfYear", 0x023e, "H"),
469 ("zuluHour", 0x023b, "b"),
470 ("zuluMinute", 0x023c, "b"),
471 ("seconds", 0x023a, "b"),
472 ("paused", 0x0264, "H"),
473 ("frozen", 0x3364, "H"),
474 ("replay", 0x0628, "d"),
475 ("slew", 0x05dc, "H"),
476 ("overspeed", 0x036d, "b"),
477 ("stalled", 0x036c, "b"),
478 ("onTheGround", 0x0366, "H"),
479 ("grossWeight", 0x30c0, "f"),
480 ("heading", 0x0580, "d"),
481 ("pitch", 0x0578, "d"),
482 ("bank", 0x057c, "d"),
483 ("ias", 0x02bc, "d"),
484 ("groundSpeed", 0x02b4, "d"),
485 ("vs", 0x02c8, "d"),
486 ("altitude", 0x0570, "l"),
487 ("gLoad", 0x11ba, "H"),
488 ("flapsControl", 0x0bdc, "d"),
489 ("flapsLeft", 0x0be0, "d"),
490 ("flapsRight", 0x0be4, "d"),
491 ("lights", 0x0d0c, "H"),
492 ("pitot", 0x029c, "b"),
493 ("noseGear", 0x0bec, "d"),
494 ("spoilersArmed", 0x0bcc, "d"),
495 ("spoilers", 0x0bd0, "d"),
496 ("altimeter", 0x0330, "H"),
497 ("nav1", 0x0350, "H"),
498 ("nav2", 0x0352, "H")]
499
500 genericModels = { const.AIRCRAFT.B736 : B737Model,
501 const.AIRCRAFT_B737 : B737Model,
502 const.AIRCRAFT_B738 : B737Model,
503 const.AIRCRAFT_B733 : B737Model,
504 const.AIRCRAFT_B734 : B737Model,
505 const.AIRCRAFT_B735 : B737Model,
506 const.AIRCRAFT_DH8D : DH8DModel,
507 const.AIRCRAFT_B762 : B767Model,
508 const.AIRCRAFT_B763 : B767Model,
509 const.AIRCRAFT_CRJ2 : B767Model,
510 const.AIRCRAFT_F79 : F70Model,
511 const.AIRCRAFT_DC3 : DC3Model,
512 const.AIRCRAFT_T134 : T134Model,
513 const.AIRCRAFT_T154 : T154Model,
514 const.AIRCRAFT_YK40 : YK40Model }
515
516 specialModels = []
517
518 @staticmethod
519 def registerSpecial(clazz):
520 """Register the given class as a special model."""
521 AircraftModel.specialModels.append(clazz)
522
523 @staticmethod
524 def create(aircraft, aircraftName):
525 """Create the model for the given aircraft name, and notify the
526 aircraft about it."""
527 for specialModel in AircraftModel.specialModels:
528 if specialModel.doesHandle(aircraft, aircraftName):
529 return specialModel(aircraft, aircraftName)
530
531 if aircraft.type in AircraftModel.genericModels:
532 return AircraftModel.genericModels[aircraft.type]()
533 else:
534 return GenericModel()
535
536 @staticmethod
537 def convertFrequency(data):
538 """Convert the given frequency data to a string."""
539 frequency = ""
540 for i in range(0, 4):
541 digit = chr(ord('0') + (data&0x0f))
542 data >>= 4
543 frequency = digit + frequency
544 if i==1:
545 frequency = "." + frequency
546 return "1" + frequency
547
548 def __init__(self, flapsNotches):
549 """Construct the aircraft model.
550
551 flapsNotches is a list of degrees of flaps that are available on the aircraft."""
552 self._flapsNotches = flapsNotches
553
554 @property
555 def name(self):
556 """Get the name for this aircraft model."""
557 return "FSUIPC/Generic"
558
559 def doesHandle(self, aircraftName):
560 """Determine if the model handles the given aircraft name.
561
562 This default implementation returns False."""
563 return False
564
565 def _addOffsetWithIndexMember(self, dest, offset, type, attrName = None):
566 """Add the given FSUIPC offset and type to the given array and a member
567 attribute with the given name."""
568 dest.append((offset, type))
569 if attrName is not None:
570 setattr(self, attrName, len(dest))
571
572 def _addDataWithIndexMembers(self, dest, prefix, data):
573 """Add FSUIPC data to the given array and also corresponding index
574 member variables with the given prefix.
575
576 data is a list of triplets of the following items:
577 - the name of the data item. The index member variable will have a name
578 created by prepending the given prefix to this name.
579 - the FSUIPC offset
580 - the FSUIPC type
581
582 The latter two items will be appended to dest."""
583 for (name, offset, type) in data:
584 self._addOffsetWithIndexMember(dest, offset, type, prefix + name)
585
586 def addMonitoringData(self, data):
587 """Add the model-specific monitoring data to the given array."""
588 self.addDataWithIndexMembers(data, "_monidx_",
589 AircraftModel.monitoringData)
590
591 def getAircraftState(self, aircraft, data):
592 """Get an aircraft state object for the given monitoring data."""
593 state = fs.AircraftState()
594
595 timestamp = calendar.timegm(time.struct_time([data[self._monidx_year],
596 1, 1, 0, 0, 0, -1, 1, 0]))
597 timestamp += data[self._monidx_dayOfYear] * 24 * 3600
598 timestamp += data[self._monidx_zuluHour] * 3600
599 timestamp += data[self._monidx_zuluMinute] * 60
600 timestamp += data[self._monidx_seconds]
601 state.timestamp = timestamp
602
603 state.paused = data[self._monidx_paused]!=0 or \
604 data[self._monidx_frozen]!=0 or \
605 data[self._monidx_replay]!=0
606 state.trickMode = data[self._monidx_slew]!=0
607
608 state.overspeed = data[self._monidx_overspeed]!=0
609 state.stalled = data[self._monidx_stalled]!=0
610 state.onTheGround = data[self._monidx_onTheGround]!=0
611
612 state.grossWeight = data[self._monidx_grossWeight] * const.LBSTOKG
613
614 state.heading = data[self._monidx_heading]*360.0/65536.0/65536.0
615 if state.heading<0.0: state.heading += 360.0
616
617 state.pitch = data[self._monidx_pitch]*360.0/65536.0/65536.0
618 state.bank = data[self._monidx_bank]*360.0/65536.0/65536.0
619
620 state.ias = data[self._monidx_ias]/128.0
621 state.groundSpeed = data[self._monidx_groundSpeed]* 3600.0/65536.0/1852.0
622 state.vs = data[self._monidx_vs]*60.0/const.FEETTOMETRES/256.0
623
624 state.altitude = data[self._monidx_altitude]/const.FEETTOMETRES/65536.0/65536.0
625
626 state.gLoad = data[self._monidx_gLoad] / 625.0
627
628 numNotchesM1 = len(self._flapsNotches) - 1
629 flapsIncrement = 16383 / numNotchesM1
630 flapsControl = data[self._monidx_flapsControl]
631 flapsIndex = flapsControl / flapsIncrement
632 if flapsIndex < numNotchesM1:
633 if (flapsControl - (flapsIndex*flapsIncrement) >
634 (flapsIndex+1)*flapsIncrement - flapsControl):
635 flapsIndex += 1
636 state.flapsSet = self._flapsNotches[flapsIndex]
637
638 flapsLeft = data[self._monidx_flapsLeft]
639 flapsIndex = flapsLeft / flapsIncrement
640 state.flaps = self._flapsNotches[flapsIndex]
641 if flapsIndex != numNotchesM1:
642 thisNotch = flapsIndex * flapsIncrement
643 nextNotch = thisNotch + flapsIncrement
644
645 state.flaps += (self._flapsNotches[flapsIndex+1] - state.flaps) * \
646 (flapsLeft - thisNotch) / (nextNotch - thisNotch)
647
648 lights = data[self._monidx_lights]
649
650 state.navLightsOn = (lights&0x01) != 0
651 state.antiCollisionLightsOn = (lights&0x02) != 0
652 state.landingLightsOn = (lights&0x04) != 0
653 state.strobeLightsOn = (lights&0x10) != 0
654
655 state.pitotHeatOn = data[self._monidx_pitot]!=0
656
657 state.gearsDown = data[self._monidx_noseGear]==16383
658
659 state.spoilersArmed = data[self._monidx_spoilersArmed]!=0
660
661 spoilers = data[self._monidx_spoilers]
662 if spoilers<=4800:
663 state.spoilersExtension = 0.0
664 else:
665 state.spoilersExtension = (spoilers - 4800) * 100.0 / (16383 - 4800)
666
667 state.altimeter = data[self._monidx_altimeter] / 16.0
668
669 state.nav1 = AircraftModel.convertFrequency(data[self._monidx_nav1])
670 state.nav2 = AircraftModel.convertFrequency(data[self._monidx_nav2])
671
672 return state
673
674#------------------------------------------------------------------------------
675
676class GenericAircraftModel(AircraftModel):
677 """A generic aircraft model that can handle the fuel levels, the N1 or RPM
678 values and some other common parameters in a generic way."""
679 def __init__(self, flapsNotches, fuelInfo, numEngines, isN1 = True):
680 """Construct the generic aircraft model with the given data.
681
682 flapsNotches is an array of how much degrees the individual flaps
683 notches mean.
684
685 fuelInfo is an array of FSUIPC offsets for the levels of the fuel
686 tanks. It is assumed to be a 4-byte value, followed by another 4-byte
687 value, which is the fuel tank capacity.
688
689 numEngines is the number of engines the aircraft has.
690
691 isN1 determines if the engines have an N1 value or an RPM value
692 (e.g. pistons)."""
693 super(GenericAircraftModel, self).__init__(flapsNotches = flapsNotches)
694
695 self._fuelInfo = fuelInfo
696 self._fuelStartIndex = None
697 self._numEngines = numEngines
698 self._engineStartIndex = None
699 self._isN1 = isN1
700
701 def addMonitoringData(self, data):
702 """Add the model-specific monitoring data to the given array."""
703 super(GenericAircraftModel, self).addMonitoringData(data)
704
705 self._addOffsetWithIndexMember(data, 0x0af4, "H", "_monidx_fuelWeight")
706
707 self._fuelStartIndex = len(data)
708 for offset in self._fuelInfo:
709 self._addOffsetWithIndexMember(data, offset, "u") # tank level
710 self._addOffsetWithIndexMember(data, offset+4, "u") # tank capacity
711
712 if self._isN1:
713 self._engineStartIndex = len(data)
714 for i in range(0, self._numEngines):
715 self._addOffsetWithIndexMember(data, 0x0898 + i * 0x98, "u") # N1
716 self._addOffsetWithIndexMember(data, 0x088c + i * 0x98, "d") # throttle lever
717
718 def getAircraftState(self, aircraft, data):
719 """Get the aircraft state.
720
721 Get it from the parent, and then add the data about the fuel levels and
722 the engine parameters."""
723 state = super(GenericAircraftModel, self).getAircraftState(aircraft,
724 data)
725
726 fuelWeight = data[self._monidx_fuelWeight]/256.0
727 state.fuel = []
728 for i in range(self._fuelStartIndex,
729 self._fuelStartIndex + 2*len(self._fuelInfo), 2):
730 fuel = data[i+1]*data[i]*fuelWeight*const.LBSTOKG/128.0/65536.0
731 state.fuel.append(fuel)
732
733 state.n1 = []
734 state.reverser = []
735 for i in range(self._engineStartIndex,
736 self._engineStartIndex + 2*self._numEngines, 2):
737 state.n1.append(data[i]*100.0/16384.0)
738 state.reverser.append(data[i+1]<0)
739
740 return state
741
742#------------------------------------------------------------------------------
743
744class GenericModel(GenericAircraftModel):
745 """Generic aircraft model for an unknown type."""
746 def __init__(self):
747 """Construct the model."""
748 super(GenericAircraftModel, self).
749 __init__(flapsNotches = [0, 10, 20, 30],
750 fuelInfo = [0x0b74, 0x0b7c, 0xb94],
751 numEngines = 2)
752
753 @property
754 def name(self):
755 """Get the name for this aircraft model."""
756 return "FSUIPC/Generic"
757
758#------------------------------------------------------------------------------
759
760class B737Model(GenericAircraftModel):
761 """Generic model for the Boeing 737 Classing and NG aircraft."""
762 def __init__(self):
763 """Construct the model."""
764 super(GenericAircraftModel, self).
765 __init__(flapsNotches = [0, 1, 2, 5, 10, 15, 25, 30, 40],
766 fuelInfo = [0x0b74, 0x0b7c, 0xb94],
767 numEngines = 2)
768
769 @property
770 def name(self):
771 """Get the name for this aircraft model."""
772 return "FSUIPC/Generic Boeing 737"
773
774#------------------------------------------------------------------------------
775
776class B767Model(GenericAircraftModel):
777 """Generic model for the Boeing 767 aircraft."""
778 def __init__(self):
779 """Construct the model."""
780 super(GenericAircraftModel, self).
781 __init__(flapsNotches = [0, 1, 5, 15, 20, 25, 30],
782 fuelInfo = [0x0b74, 0x0b7c, 0xb94],
783 numEngines = 2)
784
785 @property
786 def name(self):
787 """Get the name for this aircraft model."""
788 return "FSUIPC/Generic Boeing 767"
789
790#------------------------------------------------------------------------------
791
792class DH8DModel(GenericAircraftModel):
793 """Generic model for the Boeing 737 NG aircraft."""
794 def __init__(self):
795 """Construct the model."""
796 super(GenericAircraftModel, self).
797 __init__(flapsNotches = [0, 5, 10, 15, 35],
798 fuelInfo = [0x0b74, 0x0b7c, 0xb94],
799 numEngines = 2)
800
801 @property
802 def name(self):
803 """Get the name for this aircraft model."""
804 return "FSUIPC/Generic Bombardier Dash-8 Q400"
805
806#------------------------------------------------------------------------------
807
808class CRJ2Model(GenericAircraftModel):
809 """Generic model for the Bombardier CRJ-200 aircraft."""
810 def __init__(self):
811 """Construct the model."""
812 super(GenericAircraftModel, self).
813 __init__(flapsNotches = [0, 8, 20, 30, 45],
814 fuelInfo = [0x0b74, 0x0b7c, 0xb94],
815 numEngines = 2)
816
817 @property
818 def name(self):
819 """Get the name for this aircraft model."""
820 return "FSUIPC/Generic Bombardier CRJ-200"
821
822#------------------------------------------------------------------------------
823
824class F70Model(GenericAircraftModel):
825 """Generic model for the Fokker F70 aircraft."""
826 def __init__(self):
827 """Construct the model."""
828 super(GenericAircraftModel, self).
829 __init__(flapsNotches = [0, 8, 15, 25, 42],
830 fuelInfo = [0x0b74, 0x0b7c, 0xb94],
831 numEngines = 2)
832
833 @property
834 def name(self):
835 """Get the name for this aircraft model."""
836 return "FSUIPC/Generic Fokker 70"
837
838#------------------------------------------------------------------------------
839
840class DC3Model(GenericAircraftModel):
841 """Generic model for the Lisunov Li-2 (DC-3) aircraft."""
842 def __init__(self):
843 """Construct the model."""
844 super(GenericAircraftModel, self).
845 __init__(flapsNotches = [0, 15, 30, 45],
846 fuelInfo = [0x0b7c, 0x0b84, 0x0b94, 0x0b9c],
847 numEngines = 2)
848
849 @property
850 def name(self):
851 """Get the name for this aircraft model."""
852 return "FSUIPC/Generic Lisunov Li-2"
853
854#------------------------------------------------------------------------------
855
856class T134Model(GenericAircraftModel):
857 """Generic model for the Tupolev Tu-134 aircraft."""
858 def __init__(self):
859 """Construct the model."""
860 super(GenericAircraftModel, self).
861 __init__(flapsNotches = [0, 10, 20, 30],
862 fuelInfo = [0x0b74,
863 0x0b8c, 0x0b84,
864 0x0ba4, 0x0b9c,
865 0x1254, 0x125c],
866 numEngines = 2)
867
868 @property
869 def name(self):
870 """Get the name for this aircraft model."""
871 return "FSUIPC/Generic Tupolev Tu-134"
872
873#------------------------------------------------------------------------------
874
875class T154Model(GenericAircraftModel):
876 """Generic model for the Tupolev Tu-134 aircraft."""
877 def __init__(self):
878 """Construct the model."""
879 super(GenericAircraftModel, self).
880 __init__(flapsNotches = [0, 15, 28, 45],
881 fuelInfo = [0x0b74, 0x0b7c, 0x0b94,
882 0x1244, 0x0b84, 0x0b9c]
883 numEngines = 3)
884
885 @property
886 def name(self):
887 """Get the name for this aircraft model."""
888 return "FSUIPC/Generic Tupolev Tu-154"
889
890 def getAircraftState(self, aircraft, data):
891 """Get an aircraft state object for the given monitoring data.
892
893 This removes the reverser value for the middle engine."""
894 state = super(T154Model, self).getAircraftState(aircraft, data)
895 del state.reverser[1]
896 return state
897
898#------------------------------------------------------------------------------
899
900class YK40Model(GenericAircraftModel):
901 """Generic model for the Yakovlev Yak-40 aircraft."""
902 def __init__(self):
903 """Construct the model."""
904 super(GenericAircraftModel, self).
905 __init__(flapsNotches = [0, 20, 35],
906 fuelInfo = [0x0b7c, 0x0b94]
907 numEngines = 2)
908
909 @property
910 def name(self):
911 """Get the name for this aircraft model."""
912 return "FSUIPC/Generic Yakovlev Yak-40"
913
914#------------------------------------------------------------------------------
915
Note: See TracBrowser for help on using the repository browser.