source: src/mlx/acft.py@ 74:1aba83981490

Last change on this file since 74:1aba83981490 was 71:2dba0ba6cd1b, checked in by István Váradi <ivaradi@…>, 13 years ago

Added the takeoff page.

File size: 24.2 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.flight.flareStarted(flareStart, flareStartFS)
178
179 def flareFinished(self, flareEnd, flareEndFS, tdRate, tdRateCalculatedByFS,
180 ias, pitch, bank, heading):
181 """Called when the flare has finished."""
182 (flareTimeFromFS, flareTime) = self.flight.flareFinished(flareEnd,
183 flareEndFS)
184 self.logger.message(self._aircraftState.timestamp,
185 "Flare time: %.1f s (from %s)" % \
186 (flareTime,
187 "the simulator" if flareTimeFromFS else "real time",))
188 self.logger.message(self._aircraftState.timestamp,
189 "Touchdown rate: %.0f feet/min" % (tdRate,))
190 self.logger.message(self._aircraftState.timestamp,
191 "Touchdown rate was calculated by the %s" % \
192 ("simulator" if tdRateCalculatedByFS else "logger",))
193 self.logger.message(self._aircraftState.timestamp,
194 "Touchdown speed: %.0f knots" % (ias,))
195 self.logger.message(self._aircraftState.timestamp,
196 "Touchdown pitch: %.1f degrees" % (pitch,))
197 self.logger.message(self._aircraftState.timestamp,
198 "Touchdown bank: %.1f degrees" % (bank,))
199 self.logger.message(self._aircraftState.timestamp,
200 "Touchdown heading: %03.0f degrees" % (heading,))
201
202 def cancelFlare(self):
203 """Cancel flare, if it has started."""
204 self.flight.simulator.cancelFlare()
205
206 def checkFlightEnd(self, aircraftState):
207 """Check if the end of the flight has arrived.
208
209 This default implementation checks the N1 values, but for
210 piston-powered aircraft you need to check the RPMs."""
211 for n1 in aircraftState.n1:
212 if n1>=0.5: return False
213 return True
214
215#---------------------------------------------------------------------------------------
216
217class Boeing737(Aircraft):
218 """Base class for the various aircraft in the Boeing 737 family.
219
220 The aircraft type-specific values in the aircraft state have the following
221 structure:
222 - fuel: centre, left, right
223 - n1: left, right
224 - reverser: left, right"""
225 def __init__(self, flight):
226 super(Boeing737, self).__init__(flight)
227 self.gearSpeedLimit = 270
228 self.flapSpeedLimits = { 1 : 260,
229 2 : 260,
230 5 : 250,
231 10 : 210,
232 15 : 200,
233 25 : 190,
234 30 : 175,
235 40 : 162 }
236
237 def logFuel(self, aircraftState):
238 """Log the amount of fuel"""
239 self.logger.message(aircraftState.timestamp,
240 "Fuel: left=%.0f kg - centre=%.0f kg - right=%.0f kg" % \
241 (aircraftState.fuel[1], aircraftState.fuel[0],
242 aircraftState.fuel[2]))
243 self.logger.message(aircraftState.timestamp,
244 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
245
246#---------------------------------------------------------------------------------------
247
248class B736(Boeing737):
249 """Boeing 737-600 aircraft."""
250 def __init__(self, flight):
251 super(B736, self).__init__(flight)
252 self.dow = 38307
253 self.mtow = 58328
254 self.mlw = 54657
255 self.mzfw = 51482
256
257#---------------------------------------------------------------------------------------
258
259class B737(Boeing737):
260 """Boeing 737-700 aircraft."""
261 def __init__(self, flight):
262 super(B737, self).__init__(flight)
263 self.dow = 39250
264 self.mtow = 61410
265 self.mlw = 58059
266 self.mzfw = 54657
267
268#---------------------------------------------------------------------------------------
269
270class B738(Boeing737):
271 """Boeing 737-800 aircraft."""
272 def __init__(self, flight):
273 super(B738, self).__init__(flight)
274 self.dow = 42690
275 self.mtow = 71709
276 self.mlw = 65317
277 self.mzfw = 61688
278
279#---------------------------------------------------------------------------------------
280
281class B738Charter(B738):
282 """Boeing 737-800 aircraft used for charters."""
283 def __init__(self, flight):
284 super(B738Charter, self).__init__(flight)
285 self.mtow = 77791
286
287#---------------------------------------------------------------------------------------
288
289class B733(Boeing737):
290 """Boeing 737-300 aircraft."""
291 def __init__(self, flight):
292 super(B733, self).__init__(flight)
293 self.dow = 32700
294 self.mtow = 62820
295 self.mlw = 51700
296 self.mzfw = 48410
297
298#---------------------------------------------------------------------------------------
299
300class B734(Boeing737):
301 """Boeing 737-400 aircraft."""
302 def __init__(self, flight):
303 super(B734, self).__init__(flight)
304 self.dow = 33200
305 self.mtow = 68050
306 self.mlw = 56200
307 self.mzfw = 53100
308
309#---------------------------------------------------------------------------------------
310
311class B735(Boeing737):
312 """Boeing 737-500 aircraft."""
313 def __init__(self, flight):
314 super(B735, self).__init__(flight)
315 self.dow = 31300
316 self.mtow = 60550
317 self.mlw = 50000
318 self.mzfw = 46700
319
320#---------------------------------------------------------------------------------------
321
322class DH8D(Aircraft):
323 """Bombardier Dash-8 Q400 aircraft.
324
325 The aircraft type-specific values in the aircraft state have the following
326 structure:
327 - fuel: centre, left, right
328 - n1: left, right
329 - reverser: left, right."""
330 def __init__(self, flight):
331 super(DH8D, self).__init__(flight)
332 self.dow = 17185
333 self.mtow = 29257
334 self.mlw = 28009
335 self.mzfw = 25855
336 self.gearSpeedLimit = 215
337 self.flapSpeedLimits = { 5 : 200,
338 10 : 181,
339 15 : 172,
340 35 : 158 }
341
342 def logFuel(self, aircraftState):
343 """Log the amount of fuel"""
344 self.logger.message(aircraftState.timestamp,
345 "Fuel: left=%.0f kg - centre=%.0f kg - right=%.0f kg" % \
346 (aircraftState.fuel[1], aircraftState.fuel[0],
347 aircraftState.fuel[2]))
348 self.logger.message(aircraftState.timestamp,
349 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
350
351#---------------------------------------------------------------------------------------
352
353class Boeing767(Aircraft):
354 """Base class for the various aircraft in the Boeing 767 family.
355
356 The aircraft type-specific values in the aircraft state have the following
357 structure:
358 - fuel: centre, left, right
359 - n1: left, right
360 - reverser: left, right"""
361 def __init__(self, flight):
362 super(Boeing767, self).__init__(flight)
363 self.gearSpeedLimit = 270
364 self.flapSpeedLimits = { 1 : 255,
365 5 : 235,
366 10 : 215,
367 20 : 215,
368 25 : 185,
369 30 : 175 }
370
371 def logFuel(self, aircraftState):
372 """Log the amount of fuel"""
373 self.logger.message(aircraftState.timestamp,
374 "Fuel: left=%.0f kg - centre=%.0f kg - right=%.0f kg" % \
375 (aircraftState.fuel[1], aircraftState.fuel[0],
376 aircraftState.fuel[2]))
377 self.logger.message(aircraftState.timestamp,
378 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
379
380#---------------------------------------------------------------------------------------
381
382class B762(Boeing767):
383 """Boeing 767-200 aircraft."""
384 def __init__(self, flight):
385 super(B762, self).__init__(flight)
386 self.dow = 84507
387 self.mtow = 175540
388 self.mlw = 126098
389 self.mzfw = 114758
390
391#---------------------------------------------------------------------------------------
392
393class B763(Boeing767):
394 """Boeing 767-300 aircraft."""
395 def __init__(self, flight):
396 super(B763, self).__init__(cflight)
397 self.dow = 91311
398 self.mtow = 181436
399 self.mlw = 137892
400 self.mzfw = 130635
401
402#---------------------------------------------------------------------------------------
403
404class CRJ2(Aircraft):
405 """Bombardier CRJ-200 aircraft.
406
407 The aircraft type-specific values in the aircraft state have the following
408 structure:
409 - fuel: centre, left, right
410 - n1: left, right
411 - reverser: left, right."""
412 def __init__(self, flight):
413 super(CRJ2, self).__init__(flight)
414 self.dow = 14549
415 self.mtow = 22995
416 self.mlw = 21319
417 self.mzfw = 19958
418 self.gearSpeedLimit = 240
419 self.flapSpeedLimits = { 8 : 260,
420 20 : 220,
421 30 : 190,
422 45 : 175 }
423
424 def logFuel(self, aircraftState):
425 """Log the amount of fuel"""
426 self.logger.message(aircraftState.timestamp,
427 "Fuel: left=%.0f kg - centre=%.0f kg - right=%.0f kg" % \
428 (aircraftState.fuel[1], aircraftState.fuel[0],
429 aircraftState.fuel[2]))
430 self.logger.message(aircraftState.timestamp,
431 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
432
433#---------------------------------------------------------------------------------------
434
435class F70(Aircraft):
436 """Fokker 70 aircraft.
437
438 The aircraft type-specific values in the aircraft state have the following
439 structure:
440 - fuel: centre, left, right
441 - n1: left, right
442 - reverser: left, right."""
443 def __init__(self, flight):
444 super(F70, self).__init__(flight)
445 self.dow = 24283
446 self.mtow = 38100 # FIXME: differentiate by registration number,
447 # MTOW of HA-LMF: 41955
448 self.mlw = 36740
449 self.mzfw = 32655
450 self.gearSpeedLimit = 200
451 self.flapSpeedLimits = { 8 : 250,
452 15 : 220,
453 25 : 220,
454 42 : 180 }
455
456 def logFuel(self, aircraftState):
457 """Log the amount of fuel"""
458 self.logger.message(aircraftState.timestamp,
459 "Fuel: left=%.0f kg - centre=%.0f kg - right=%.0f kg" % \
460 (aircraftState.fuel[1], aircraftState.fuel[0],
461 aircraftState.fuel[2]))
462 self.logger.message(aircraftState.timestamp,
463 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
464
465#---------------------------------------------------------------------------------------
466
467class DC3(Aircraft):
468 """Lisunov Li-2 (DC-3) aircraft.
469
470 The aircraft type-specific values in the aircraft state have the following
471 structure:
472 - fuel: left, right, left aux, right aux
473 - rpm: left, right
474 - reverser: left, right."""
475 def __init__(self, flight):
476 super(DC3, self).__init__(flight)
477 self.dow = 8627
478 self.mtow = 11884
479 self.mlw = 11793
480 self.mzfw = 11780
481 self.gearSpeedLimit = 148
482 self.flapSpeedLimits = { 15 : 135,
483 30 : 99,
484 45 : 97 }
485
486 def _checkFlightEnd(self, aircraftState):
487 """Check if the end of the flight has arrived.
488
489 This implementation checks the RPM values to be 0."""
490 for rpm in aircraftState.rpm:
491 if rpm>0: return False
492 return True
493
494 def logFuel(self, aircraftState):
495 """Log the amount of fuel"""
496 self.logger.message(aircraftState.timestamp,
497 "Fuel: left aux=%.0f kg - left=%.0f kg - right=%.0f kg - right aux=%.0f kg" % \
498 (aircraftState.fuel[2], aircraftState.fuel[0],
499 aircraftState.fuel[1], aircraftState.fuel[3]))
500 self.logger.message(aircraftState.timestamp,
501 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
502
503#---------------------------------------------------------------------------------------
504
505class T134(Aircraft):
506 """Tupolev Tu-134 aircraft.
507
508 The aircraft type-specific values in the aircraft state have the following
509 structure:
510 - fuel: centre, left tip, left aux, right tip, right aux, external 1,
511 external 2
512 - n1: left, right
513 - reverser: left, right."""
514 def __init__(self, flight):
515 super(T134, self).__init__(flight)
516 self.dow = 29927
517 self.mtow = 47600
518 self.mlw = 43000
519 self.mzfw = 38500
520 self.gearSpeedLimit = 216
521 self.flapSpeedLimits = { 10 : 240,
522 20 : 216,
523 30 : 161 }
524
525 def logFuel(self, aircraftState):
526 """Log the amount of fuel"""
527 self.logger.message(aircraftState.timestamp,
528 "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" % \
529 (aircraftState.fuel[2], aircraftState.fuel[1],
530 aircraftState.fuel[0],
531 aircraftState.fuel[3], aircraftState.fuel[4],
532 aircraftState.fuel[5], aircraftState.fuel[6]))
533 self.logger.message(aircraftState.timestamp,
534 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
535
536#---------------------------------------------------------------------------------------
537
538class T154(Aircraft):
539 """Tupolev Tu-154 aircraft.
540
541 The aircraft type-specific values in the aircraft state have the following
542 structure:
543 - fuel: centre, left, right, centre 2, left aux, right aux
544 - n1: left, centre, right
545 - reverser: left, right"""
546 def __init__(self, flight):
547 super(T154, self).__init__(flight)
548 self.dow = 53259
549 self.mtow = 98000
550 self.mlw = 78000
551 self.mzfw = 72000
552 self.gearSpeedLimit = 216
553 self.flapSpeedLimits = { 15 : 227,
554 28 : 194,
555 45 : 162 }
556
557 def logFuel(self, aircraftState):
558 """Log the amount of fuel"""
559 self.logger.message(aircraftState.timestamp,
560 "Fuel: left aux=%.0f kg - left=%.0f kg - centre=%.0f kg - centre 2=%.0f kg - right=%.0f kg - right aux=%.0f kg" % \
561 (aircraftState.fuel[4], aircraftState.fuel[1],
562 aircraftState.fuel[0], aircraftState.fuel[3],
563 aircraftState.fuel[2], aircraftState.fuel[5]))
564 self.logger.message(aircraftState.timestamp,
565 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
566
567#---------------------------------------------------------------------------------------
568
569
570class YK40(Aircraft):
571 """Yakovlev Yak-40 aircraft.
572
573 The aircraft type-specific values in the aircraft state have the following
574 structure:
575 - fuel: left, right
576 - n1: left, right
577 - reverser: left, right"""
578 def __init__(self, flight):
579 super(YK40, self).__init__(flight)
580 self.dow = 9400
581 self.mtow = 17200
582 self.mlw = 16800
583 self.mzfw = 12100
584 self.gearSpeedLimit = 165
585 self.flapSpeedLimits = { 20 : 165,
586 35 : 135 }
587
588 def logFuel(self, aircraftState):
589 """Log the amount of fuel"""
590 self.logger.message(aircraftState.timestamp,
591 "Fuel: left=%.0f kg - right=%.0f kg" % \
592 (aircraftState.fuel[0], aircraftState.fuel[1]))
593 self.logger.message(aircraftState.timestamp,
594 "Total fuel: %.0f kg" % (sum(aircraftState.fuel),))
595
596#---------------------------------------------------------------------------------------
597
598_classes = { const.AIRCRAFT_B736 : B736,
599 const.AIRCRAFT_B737 : B737,
600 const.AIRCRAFT_B738 : B738,
601 const.AIRCRAFT_B733 : B733,
602 const.AIRCRAFT_B734 : B734,
603 const.AIRCRAFT_B735 : B735,
604 const.AIRCRAFT_DH8D : DH8D,
605 const.AIRCRAFT_B762 : B762,
606 const.AIRCRAFT_B763 : B763,
607 const.AIRCRAFT_CRJ2 : CRJ2,
608 const.AIRCRAFT_F70 : F70,
609 const.AIRCRAFT_DC3 : DC3,
610 const.AIRCRAFT_T134 : T134,
611 const.AIRCRAFT_T154 : T154,
612 const.AIRCRAFT_YK40 : YK40 }
613
614#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.