source: src/mlx/acft.py@ 89:ef4711a984fe

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

Added the collection of some further statistics and the finish page

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