source: src/mlx/acft.py@ 86:285443d1c1e2

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

The IntegerEntry widget is used for the VRef as well on the landing page

File size: 24.5 KB
RevLine 
[4]1# Module for the simulator-independent aircraft classes
2
3#---------------------------------------------------------------------------------------
4
[8]5import const
6import checks
[4]7
[8]8import time
[7]9
[4]10#---------------------------------------------------------------------------------------
11
12class Aircraft(object):
13 """Base class for aircraft."""
[8]14 @staticmethod
15 def create(flight):
16 """Create an aircraft instance for the type in the given flight."""
17 return _classes[flight.aircraftType](flight)
18
19 def __init__(self, flight):
[4]20 """Construct the aircraft for the given type."""
[8]21 self._flight = flight
[4]22 self._aircraftState = None
[9]23
24 self._maxVS = -10000.0
25 self._minVS = 10000.0
[4]26
[8]27 self._checkers = []
28
[11]29 # Loggers
30
[8]31 self._checkers.append(checks.StageChecker())
[9]32 self._checkers.append(checks.TakeOffLogger())
[8]33
34 self._checkers.append(checks.AltimeterLogger())
35
36 self._checkers.append(checks.NAV1Logger())
37 self._checkers.append(checks.NAV2Logger())
38 self._checkers.append(checks.SquawkLogger())
39
40 self._checkers.append(checks.AnticollisionLightsLogger())
41 self._checkers.append(checks.LandingLightsLogger())
42 self._checkers.append(checks.StrobeLightsLogger())
43 self._checkers.append(checks.NavLightsLogger())
44
45 self._checkers.append(checks.FlapsLogger())
46
47 self._checkers.append(checks.GearsLogger())
[9]48 self._checkers.append(checks.CruiseSpeedLogger())
[10]49 self._checkers.append(checks.SpoilerLogger())
[9]50
[11]51 # Fault checkers
52
53 self._checkers.append(checks.AntiCollisionLightsChecker())
54 self._checkers.append(checks.LandingLightsChecker())
55 self._checkers.append(checks.NavLightsChecker())
56 self._checkers.append(checks.StrobeLightsChecker())
57
58 self._checkers.append(checks.BankChecker())
59
60 self._checkers.append(checks.FlapsRetractChecker())
61 self._checkers.append(checks.FlapsSpeedLimitChecker())
62
63 self._checkers.append(checks.GearsDownChecker())
64 self._checkers.append(checks.GearSpeedLimitChecker())
65
66 self._checkers.append(checks.GLoadChecker())
67
68 self._checkers.append(checks.MLWChecker())
69 self._checkers.append(checks.MTOWChecker())
70 self._checkers.append(checks.MZFWChecker())
71 self._checkers.append(checks.PayloadChecker())
72
73 self._checkers.append(checks.SpeedChecker())
74 self._checkers.append(checks.VSChecker())
75 self._checkers.append(checks.OverspeedChecker())
76 self._checkers.append(checks.StallChecker())
77
78 self._checkers.append(checks.PitotChecker())
79
80 self._checkers.append(checks.ThrustChecker())
81 self._checkers.append(checks.ReverserChecker())
82
[4]83 @property
84 def type(self):
85 """Get the type of the aircraft."""
[8]86 return self._flight.aircraftType
87
88 @property
89 def flight(self):
90 """Get the flight the aircraft belongs to."""
91 return self._flight
[4]92
[8]93 @property
94 def logger(self):
95 """Get the logger to use for the aircraft."""
96 return self._flight.logger
97
[11]98 def getFlapsSpeedLimit(self, flaps):
99 """Get the speed limit for the given flaps setting."""
100 return self.flapSpeedLimits[flaps] if flaps in self.flapSpeedLimits \
101 else None
102
[8]103 def modelChanged(self, timestamp, aircraftName, modelName):
[4]104 """Called when the simulator's aircraft changes."""
[8]105 self._flight.logger.message(timestamp,
106 "Aircraft: name='%s', model='%s'" % \
107 (aircraftName, modelName))
[4]108
109 def handleState(self, aircraftState):
110 """Called when the state of the aircraft changes."""
[8]111 for checker in self._checkers:
112 checker.check(self._flight, self, self._flight.logger,
113 self._aircraftState, aircraftState)
[7]114
[9]115 self._maxVS = max(self._maxVS, aircraftState.vs)
116 self._minVS = min(self._minVS, aircraftState.vs)
117
[4]118 self._aircraftState = aircraftState
[8]119
120 def setStage(self, aircraftState, newStage):
121 """Set the given stage as the new one and do whatever should be
122 done."""
[9]123 if self._flight.setStage(aircraftState.timestamp, newStage):
124 if newStage==const.STAGE_PUSHANDTAXI:
125 self.logger.message(aircraftState.timestamp, "Block time start")
126 self.logFuel(aircraftState)
127 self.logger.message(aircraftState.timestamp,
128 "Zero-fuel weight: %.0f kg" % (aircraftState.zfw))
129 elif newStage==const.STAGE_TAKEOFF:
130 self.logger.message(aircraftState.timestamp, "Flight time start")
131 self.logger.message(aircraftState.timestamp,
132 "Takeoff weight: %.0f kg, MTOW: %.0f kg" % \
133 (aircraftState.grossWeight, self.mtow))
134 self.logger.message(aircraftState.timestamp,
135 "Wind %03.0f degrees at %.0f knots" % \
136 (aircraftState.windDirection,
137 aircraftState.windSpeed))
[71]138 self.logger.message(aircraftState.timestamp,
139 "Speeds calculated by the pilot: V1: %s, VR: %s, V2: %s" % \
140 ("-" if self._flight.v1 is None
141 else str(self._flight.v1),
142 "-" if self._flight.vr is None
143 else str(self._flight.vr),
144 "-" if self._flight.v2 is None
145 else str(self._flight.v2)))
[9]146 elif newStage==const.STAGE_TAXIAFTERLAND:
147 self.logger.message(aircraftState.timestamp, "Flight time end")
148 self.logFuel(aircraftState)
149 self.logger.message(aircraftState.timestamp,
150 "Landing weight: %.0f kg, MLW: %.0f" % \
151 (aircraftState.grossWeight, self.mlw))
152 self.logger.message(aircraftState.timestamp,
153 "Vertical speed range: %.0f..%.0f feet/min" % \
[12]154 (self._minVS, self._maxVS))
[9]155 elif newStage==const.STAGE_PARKING:
156 self.logger.message(aircraftState.timestamp, "Block time end")
[8]157
[9]158 def prepareFlare(self):
159 """Called when it is detected that we will soon flare.
[8]160
161 On the first call, it should start monitoring some parameters more
162 closely to determine flare time."""
[9]163 self.flight.simulator.startFlare()
164
165 def flareStarted(self, windSpeed, windDirection, visibility,
166 flareStart, flareStartFS):
167 """Called when the flare has started."""
168 self.logger.message(self._aircraftState.timestamp, "The flare has begun")
169 self.logger.message(self._aircraftState.timestamp,
170 "Wind %03.0f degrees at %.0f knots" % \
171 (windDirection, windSpeed))
172 self.logger.message(self._aircraftState.timestamp,
173 "Visibility: %.0f metres" % (visibility,))
174 self.logger.message(self._aircraftState.timestamp,
175 "Altimeter setting: %.0f hPa" % \
176 (self._aircraftState.altimeter,))
[86]177 self.logger.message(self._aircraftState.timestamp,
178 "VRef speed calculated by the pilot: %s" % \
179 ("-" if self._flight.vref is None
180 else str(self._flight.vref)))
[9]181 self.flight.flareStarted(flareStart, flareStartFS)
182
183 def flareFinished(self, flareEnd, flareEndFS, tdRate, tdRateCalculatedByFS,
184 ias, pitch, bank, heading):
185 """Called when the flare has finished."""
186 (flareTimeFromFS, flareTime) = self.flight.flareFinished(flareEnd,
187 flareEndFS)
188 self.logger.message(self._aircraftState.timestamp,
189 "Flare time: %.1f s (from %s)" % \
190 (flareTime,
191 "the simulator" if flareTimeFromFS else "real time",))
192 self.logger.message(self._aircraftState.timestamp,
193 "Touchdown rate: %.0f feet/min" % (tdRate,))
194 self.logger.message(self._aircraftState.timestamp,
195 "Touchdown rate was calculated by the %s" % \
196 ("simulator" if tdRateCalculatedByFS else "logger",))
197 self.logger.message(self._aircraftState.timestamp,
198 "Touchdown speed: %.0f knots" % (ias,))
199 self.logger.message(self._aircraftState.timestamp,
200 "Touchdown pitch: %.1f degrees" % (pitch,))
201 self.logger.message(self._aircraftState.timestamp,
202 "Touchdown bank: %.1f degrees" % (bank,))
203 self.logger.message(self._aircraftState.timestamp,
204 "Touchdown heading: %03.0f degrees" % (heading,))
[8]205
206 def cancelFlare(self):
207 """Cancel flare, if it has started."""
[9]208 self.flight.simulator.cancelFlare()
[8]209
210 def checkFlightEnd(self, aircraftState):
211 """Check if the end of the flight has arrived.
212
213 This default implementation checks the N1 values, but for
214 piston-powered aircraft you need to check the RPMs."""
215 for n1 in aircraftState.n1:
216 if n1>=0.5: return False
217 return True
218
[4]219#---------------------------------------------------------------------------------------
[7]220
221class Boeing737(Aircraft):
222 """Base class for the various aircraft in the Boeing 737 family.
223
224 The aircraft type-specific values in the aircraft state have the following
225 structure:
226 - fuel: centre, left, right
227 - n1: left, right
228 - reverser: left, right"""
[9]229 def __init__(self, flight):
230 super(Boeing737, self).__init__(flight)
231 self.gearSpeedLimit = 270
232 self.flapSpeedLimits = { 1 : 260,
233 2 : 260,
234 5 : 250,
235 10 : 210,
236 15 : 200,
237 25 : 190,
238 30 : 175,
239 40 : 162 }
[7]240
[9]241 def logFuel(self, aircraftState):
242 """Log the amount of fuel"""
243 self.logger.message(aircraftState.timestamp,
244 "Fuel: left=%.0f kg - centre=%.0f kg - right=%.0f kg" % \
245 (aircraftState.fuel[1], aircraftState.fuel[0],
246 aircraftState.fuel[2]))
247 self.logger.message(aircraftState.timestamp,
248 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
249
[7]250#---------------------------------------------------------------------------------------
251
252class B736(Boeing737):
253 """Boeing 737-600 aircraft."""
[8]254 def __init__(self, flight):
255 super(B736, self).__init__(flight)
[9]256 self.dow = 38307
257 self.mtow = 58328
258 self.mlw = 54657
259 self.mzfw = 51482
[7]260
261#---------------------------------------------------------------------------------------
262
263class B737(Boeing737):
264 """Boeing 737-700 aircraft."""
[8]265 def __init__(self, flight):
266 super(B737, self).__init__(flight)
[9]267 self.dow = 39250
268 self.mtow = 61410
269 self.mlw = 58059
270 self.mzfw = 54657
[7]271
272#---------------------------------------------------------------------------------------
273
274class B738(Boeing737):
275 """Boeing 737-800 aircraft."""
[8]276 def __init__(self, flight):
277 super(B738, self).__init__(flight)
[9]278 self.dow = 42690
279 self.mtow = 71709
280 self.mlw = 65317
281 self.mzfw = 61688
282
283#---------------------------------------------------------------------------------------
284
285class B738Charter(B738):
286 """Boeing 737-800 aircraft used for charters."""
287 def __init__(self, flight):
288 super(B738Charter, self).__init__(flight)
289 self.mtow = 77791
[7]290
291#---------------------------------------------------------------------------------------
292
293class B733(Boeing737):
294 """Boeing 737-300 aircraft."""
[8]295 def __init__(self, flight):
296 super(B733, self).__init__(flight)
[9]297 self.dow = 32700
298 self.mtow = 62820
299 self.mlw = 51700
300 self.mzfw = 48410
[7]301
302#---------------------------------------------------------------------------------------
303
304class B734(Boeing737):
305 """Boeing 737-400 aircraft."""
[8]306 def __init__(self, flight):
307 super(B734, self).__init__(flight)
[9]308 self.dow = 33200
309 self.mtow = 68050
310 self.mlw = 56200
311 self.mzfw = 53100
[7]312
313#---------------------------------------------------------------------------------------
314
315class B735(Boeing737):
316 """Boeing 737-500 aircraft."""
[8]317 def __init__(self, flight):
318 super(B735, self).__init__(flight)
[9]319 self.dow = 31300
320 self.mtow = 60550
321 self.mlw = 50000
322 self.mzfw = 46700
[7]323
324#---------------------------------------------------------------------------------------
325
326class DH8D(Aircraft):
327 """Bombardier Dash-8 Q400 aircraft.
328
329 The aircraft type-specific values in the aircraft state have the following
330 structure:
331 - fuel: centre, left, right
332 - n1: left, right
333 - reverser: left, right."""
[8]334 def __init__(self, flight):
335 super(DH8D, self).__init__(flight)
[9]336 self.dow = 17185
337 self.mtow = 29257
338 self.mlw = 28009
339 self.mzfw = 25855
340 self.gearSpeedLimit = 215
341 self.flapSpeedLimits = { 5 : 200,
342 10 : 181,
343 15 : 172,
344 35 : 158 }
345
346 def logFuel(self, aircraftState):
347 """Log the amount of fuel"""
348 self.logger.message(aircraftState.timestamp,
349 "Fuel: left=%.0f kg - centre=%.0f kg - right=%.0f kg" % \
350 (aircraftState.fuel[1], aircraftState.fuel[0],
351 aircraftState.fuel[2]))
352 self.logger.message(aircraftState.timestamp,
353 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
[7]354
355#---------------------------------------------------------------------------------------
356
357class Boeing767(Aircraft):
358 """Base class for the various aircraft in the Boeing 767 family.
359
360 The aircraft type-specific values in the aircraft state have the following
361 structure:
362 - fuel: centre, left, right
363 - n1: left, right
364 - reverser: left, right"""
[9]365 def __init__(self, flight):
366 super(Boeing767, self).__init__(flight)
367 self.gearSpeedLimit = 270
368 self.flapSpeedLimits = { 1 : 255,
369 5 : 235,
370 10 : 215,
371 20 : 215,
372 25 : 185,
373 30 : 175 }
[7]374
[9]375 def logFuel(self, aircraftState):
376 """Log the amount of fuel"""
377 self.logger.message(aircraftState.timestamp,
378 "Fuel: left=%.0f kg - centre=%.0f kg - right=%.0f kg" % \
379 (aircraftState.fuel[1], aircraftState.fuel[0],
380 aircraftState.fuel[2]))
381 self.logger.message(aircraftState.timestamp,
382 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
383
[7]384#---------------------------------------------------------------------------------------
385
386class B762(Boeing767):
387 """Boeing 767-200 aircraft."""
[8]388 def __init__(self, flight):
389 super(B762, self).__init__(flight)
[9]390 self.dow = 84507
391 self.mtow = 175540
392 self.mlw = 126098
393 self.mzfw = 114758
[7]394
395#---------------------------------------------------------------------------------------
396
397class B763(Boeing767):
398 """Boeing 767-300 aircraft."""
[8]399 def __init__(self, flight):
400 super(B763, self).__init__(cflight)
[9]401 self.dow = 91311
402 self.mtow = 181436
403 self.mlw = 137892
404 self.mzfw = 130635
[7]405
406#---------------------------------------------------------------------------------------
407
408class CRJ2(Aircraft):
409 """Bombardier CRJ-200 aircraft.
410
411 The aircraft type-specific values in the aircraft state have the following
412 structure:
413 - fuel: centre, left, right
414 - n1: left, right
415 - reverser: left, right."""
[8]416 def __init__(self, flight):
417 super(CRJ2, self).__init__(flight)
[9]418 self.dow = 14549
419 self.mtow = 22995
420 self.mlw = 21319
421 self.mzfw = 19958
422 self.gearSpeedLimit = 240
423 self.flapSpeedLimits = { 8 : 260,
424 20 : 220,
425 30 : 190,
426 45 : 175 }
[7]427
[9]428 def logFuel(self, aircraftState):
429 """Log the amount of fuel"""
430 self.logger.message(aircraftState.timestamp,
431 "Fuel: left=%.0f kg - centre=%.0f kg - right=%.0f kg" % \
432 (aircraftState.fuel[1], aircraftState.fuel[0],
433 aircraftState.fuel[2]))
434 self.logger.message(aircraftState.timestamp,
435 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
436
[7]437#---------------------------------------------------------------------------------------
438
439class F70(Aircraft):
440 """Fokker 70 aircraft.
441
442 The aircraft type-specific values in the aircraft state have the following
443 structure:
444 - fuel: centre, left, right
445 - n1: left, right
446 - reverser: left, right."""
[8]447 def __init__(self, flight):
448 super(F70, self).__init__(flight)
[9]449 self.dow = 24283
450 self.mtow = 38100 # FIXME: differentiate by registration number,
451 # MTOW of HA-LMF: 41955
452 self.mlw = 36740
453 self.mzfw = 32655
454 self.gearSpeedLimit = 200
455 self.flapSpeedLimits = { 8 : 250,
456 15 : 220,
457 25 : 220,
458 42 : 180 }
[7]459
[9]460 def logFuel(self, aircraftState):
461 """Log the amount of fuel"""
462 self.logger.message(aircraftState.timestamp,
463 "Fuel: left=%.0f kg - centre=%.0f kg - right=%.0f kg" % \
464 (aircraftState.fuel[1], aircraftState.fuel[0],
465 aircraftState.fuel[2]))
466 self.logger.message(aircraftState.timestamp,
467 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
468
[7]469#---------------------------------------------------------------------------------------
470
471class DC3(Aircraft):
472 """Lisunov Li-2 (DC-3) aircraft.
473
474 The aircraft type-specific values in the aircraft state have the following
475 structure:
[52]476 - fuel: left, right, left aux, right aux
[7]477 - rpm: left, right
478 - reverser: left, right."""
[8]479 def __init__(self, flight):
480 super(DC3, self).__init__(flight)
[9]481 self.dow = 8627
482 self.mtow = 11884
483 self.mlw = 11793
484 self.mzfw = 11780
485 self.gearSpeedLimit = 148
486 self.flapSpeedLimits = { 15 : 135,
487 30 : 99,
488 45 : 97 }
[8]489
490 def _checkFlightEnd(self, aircraftState):
491 """Check if the end of the flight has arrived.
492
493 This implementation checks the RPM values to be 0."""
494 for rpm in aircraftState.rpm:
[29]495 if rpm>0: return False
496 return True
[7]497
[9]498 def logFuel(self, aircraftState):
499 """Log the amount of fuel"""
500 self.logger.message(aircraftState.timestamp,
501 "Fuel: left aux=%.0f kg - left=%.0f kg - right=%.0f kg - right aux=%.0f kg" % \
502 (aircraftState.fuel[2], aircraftState.fuel[0],
503 aircraftState.fuel[1], aircraftState.fuel[3]))
504 self.logger.message(aircraftState.timestamp,
505 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
506
[7]507#---------------------------------------------------------------------------------------
508
509class T134(Aircraft):
510 """Tupolev Tu-134 aircraft.
511
512 The aircraft type-specific values in the aircraft state have the following
513 structure:
514 - fuel: centre, left tip, left aux, right tip, right aux, external 1,
515 external 2
516 - n1: left, right
517 - reverser: left, right."""
[8]518 def __init__(self, flight):
519 super(T134, self).__init__(flight)
[9]520 self.dow = 29927
521 self.mtow = 47600
522 self.mlw = 43000
523 self.mzfw = 38500
524 self.gearSpeedLimit = 216
525 self.flapSpeedLimits = { 10 : 240,
526 20 : 216,
527 30 : 161 }
[7]528
[9]529 def logFuel(self, aircraftState):
530 """Log the amount of fuel"""
531 self.logger.message(aircraftState.timestamp,
532 "Fuel: left aux=%.0f kg - left tip=%.0f kg - centre= %.0f kg - right tip=%.0f kg - right aux=%.0f kg - external 1=%.0f kg - external 2=%.0f kg" % \
533 (aircraftState.fuel[2], aircraftState.fuel[1],
534 aircraftState.fuel[0],
535 aircraftState.fuel[3], aircraftState.fuel[4],
536 aircraftState.fuel[5], aircraftState.fuel[6]))
537 self.logger.message(aircraftState.timestamp,
538 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
539
[7]540#---------------------------------------------------------------------------------------
541
542class T154(Aircraft):
543 """Tupolev Tu-154 aircraft.
544
545 The aircraft type-specific values in the aircraft state have the following
546 structure:
547 - fuel: centre, left, right, centre 2, left aux, right aux
548 - n1: left, centre, right
549 - reverser: left, right"""
[8]550 def __init__(self, flight):
551 super(T154, self).__init__(flight)
[9]552 self.dow = 53259
553 self.mtow = 98000
554 self.mlw = 78000
555 self.mzfw = 72000
556 self.gearSpeedLimit = 216
557 self.flapSpeedLimits = { 15 : 227,
558 28 : 194,
559 45 : 162 }
560
561 def logFuel(self, aircraftState):
562 """Log the amount of fuel"""
563 self.logger.message(aircraftState.timestamp,
564 "Fuel: left aux=%.0f kg - left=%.0f kg - centre=%.0f kg - centre 2=%.0f kg - right=%.0f kg - right aux=%.0f kg" % \
565 (aircraftState.fuel[4], aircraftState.fuel[1],
566 aircraftState.fuel[0], aircraftState.fuel[3],
567 aircraftState.fuel[2], aircraftState.fuel[5]))
568 self.logger.message(aircraftState.timestamp,
569 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
[7]570
571#---------------------------------------------------------------------------------------
572
[9]573
[7]574class YK40(Aircraft):
575 """Yakovlev Yak-40 aircraft.
576
577 The aircraft type-specific values in the aircraft state have the following
578 structure:
579 - fuel: left, right
580 - n1: left, right
581 - reverser: left, right"""
[8]582 def __init__(self, flight):
583 super(YK40, self).__init__(flight)
[9]584 self.dow = 9400
585 self.mtow = 17200
586 self.mlw = 16800
587 self.mzfw = 12100
588 self.gearSpeedLimit = 165
589 self.flapSpeedLimits = { 20 : 165,
590 35 : 135 }
591
592 def logFuel(self, aircraftState):
593 """Log the amount of fuel"""
594 self.logger.message(aircraftState.timestamp,
595 "Fuel: left=%.0f kg - right=%.0f kg" % \
596 (aircraftState.fuel[0], aircraftState.fuel[1]))
597 self.logger.message(aircraftState.timestamp,
598 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
[7]599
600#---------------------------------------------------------------------------------------
601
[8]602_classes = { const.AIRCRAFT_B736 : B736,
603 const.AIRCRAFT_B737 : B737,
604 const.AIRCRAFT_B738 : B738,
605 const.AIRCRAFT_B733 : B733,
606 const.AIRCRAFT_B734 : B734,
607 const.AIRCRAFT_B735 : B735,
608 const.AIRCRAFT_DH8D : DH8D,
609 const.AIRCRAFT_B762 : B762,
610 const.AIRCRAFT_B763 : B763,
611 const.AIRCRAFT_CRJ2 : CRJ2,
612 const.AIRCRAFT_F70 : F70,
613 const.AIRCRAFT_DC3 : DC3,
614 const.AIRCRAFT_T134 : T134,
615 const.AIRCRAFT_T154 : T154,
616 const.AIRCRAFT_YK40 : YK40 }
617
618#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.