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
Line 
1# Module for the simulator-independent aircraft classes
2
3#---------------------------------------------------------------------------------------
4
5import const
6import checks
7
8import time
9
10#---------------------------------------------------------------------------------------
11
12class Aircraft(object):
13 """Base class for aircraft."""
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):
20 """Construct the aircraft for the given type."""
21 self._flight = flight
22 self._aircraftState = None
23
24 self._maxVS = -10000.0
25 self._minVS = 10000.0
26
27 self._checkers = []
28
29 # Loggers
30
31 self._checkers.append(checks.StageChecker())
32 self._checkers.append(checks.TakeOffLogger())
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())
48 self._checkers.append(checks.CruiseSpeedLogger())
49 self._checkers.append(checks.SpoilerLogger())
50
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
83 @property
84 def type(self):
85 """Get the type of the aircraft."""
86 return self._flight.aircraftType
87
88 @property
89 def flight(self):
90 """Get the flight the aircraft belongs to."""
91 return self._flight
92
93 @property
94 def logger(self):
95 """Get the logger to use for the aircraft."""
96 return self._flight.logger
97
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
103 def modelChanged(self, timestamp, aircraftName, modelName):
104 """Called when the simulator's aircraft changes."""
105 self._flight.logger.message(timestamp,
106 "Aircraft: name='%s', model='%s'" % \
107 (aircraftName, modelName))
108
109 def handleState(self, aircraftState):
110 """Called when the state of the aircraft changes."""
111 for checker in self._checkers:
112 checker.check(self._flight, self, self._flight.logger,
113 self._aircraftState, aircraftState)
114
115 self._maxVS = max(self._maxVS, aircraftState.vs)
116 self._minVS = min(self._minVS, aircraftState.vs)
117
118 self._aircraftState = aircraftState
119
120 def setStage(self, aircraftState, newStage):
121 """Set the given stage as the new one and do whatever should be
122 done."""
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))
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)))
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" % \
154 (self._minVS, self._maxVS))
155 elif newStage==const.STAGE_PARKING:
156 self.logger.message(aircraftState.timestamp, "Block time end")
157
158 def prepareFlare(self):
159 """Called when it is detected that we will soon flare.
160
161 On the first call, it should start monitoring some parameters more
162 closely to determine flare time."""
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,))
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)))
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,))
205
206 def cancelFlare(self):
207 """Cancel flare, if it has started."""
208 self.flight.simulator.cancelFlare()
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
219#---------------------------------------------------------------------------------------
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"""
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 }
240
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
250#---------------------------------------------------------------------------------------
251
252class B736(Boeing737):
253 """Boeing 737-600 aircraft."""
254 def __init__(self, flight):
255 super(B736, self).__init__(flight)
256 self.dow = 38307
257 self.mtow = 58328
258 self.mlw = 54657
259 self.mzfw = 51482
260
261#---------------------------------------------------------------------------------------
262
263class B737(Boeing737):
264 """Boeing 737-700 aircraft."""
265 def __init__(self, flight):
266 super(B737, self).__init__(flight)
267 self.dow = 39250
268 self.mtow = 61410
269 self.mlw = 58059
270 self.mzfw = 54657
271
272#---------------------------------------------------------------------------------------
273
274class B738(Boeing737):
275 """Boeing 737-800 aircraft."""
276 def __init__(self, flight):
277 super(B738, self).__init__(flight)
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
290
291#---------------------------------------------------------------------------------------
292
293class B733(Boeing737):
294 """Boeing 737-300 aircraft."""
295 def __init__(self, flight):
296 super(B733, self).__init__(flight)
297 self.dow = 32700
298 self.mtow = 62820
299 self.mlw = 51700
300 self.mzfw = 48410
301
302#---------------------------------------------------------------------------------------
303
304class B734(Boeing737):
305 """Boeing 737-400 aircraft."""
306 def __init__(self, flight):
307 super(B734, self).__init__(flight)
308 self.dow = 33200
309 self.mtow = 68050
310 self.mlw = 56200
311 self.mzfw = 53100
312
313#---------------------------------------------------------------------------------------
314
315class B735(Boeing737):
316 """Boeing 737-500 aircraft."""
317 def __init__(self, flight):
318 super(B735, self).__init__(flight)
319 self.dow = 31300
320 self.mtow = 60550
321 self.mlw = 50000
322 self.mzfw = 46700
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."""
334 def __init__(self, flight):
335 super(DH8D, self).__init__(flight)
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),))
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"""
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 }
374
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
384#---------------------------------------------------------------------------------------
385
386class B762(Boeing767):
387 """Boeing 767-200 aircraft."""
388 def __init__(self, flight):
389 super(B762, self).__init__(flight)
390 self.dow = 84507
391 self.mtow = 175540
392 self.mlw = 126098
393 self.mzfw = 114758
394
395#---------------------------------------------------------------------------------------
396
397class B763(Boeing767):
398 """Boeing 767-300 aircraft."""
399 def __init__(self, flight):
400 super(B763, self).__init__(cflight)
401 self.dow = 91311
402 self.mtow = 181436
403 self.mlw = 137892
404 self.mzfw = 130635
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."""
416 def __init__(self, flight):
417 super(CRJ2, self).__init__(flight)
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 }
427
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
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."""
447 def __init__(self, flight):
448 super(F70, self).__init__(flight)
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 }
459
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
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:
476 - fuel: left, right, left aux, right aux
477 - rpm: left, right
478 - reverser: left, right."""
479 def __init__(self, flight):
480 super(DC3, self).__init__(flight)
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 }
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:
495 if rpm>0: return False
496 return True
497
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
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."""
518 def __init__(self, flight):
519 super(T134, self).__init__(flight)
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 }
528
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
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"""
550 def __init__(self, flight):
551 super(T154, self).__init__(flight)
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),))
570
571#---------------------------------------------------------------------------------------
572
573
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"""
582 def __init__(self, flight):
583 super(YK40, self).__init__(flight)
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),))
599
600#---------------------------------------------------------------------------------------
601
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.