source: src/mlx/acft.py@ 71:2dba0ba6cd1b

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

Added the takeoff page.

File size: 24.2 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,))
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,))
[8]201
202 def cancelFlare(self):
203 """Cancel flare, if it has started."""
[9]204 self.flight.simulator.cancelFlare()
[8]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
[4]215#---------------------------------------------------------------------------------------
[7]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"""
[9]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 }
[7]236
[9]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
[7]246#---------------------------------------------------------------------------------------
247
248class B736(Boeing737):
249 """Boeing 737-600 aircraft."""
[8]250 def __init__(self, flight):
251 super(B736, self).__init__(flight)
[9]252 self.dow = 38307
253 self.mtow = 58328
254 self.mlw = 54657
255 self.mzfw = 51482
[7]256
257#---------------------------------------------------------------------------------------
258
259class B737(Boeing737):
260 """Boeing 737-700 aircraft."""
[8]261 def __init__(self, flight):
262 super(B737, self).__init__(flight)
[9]263 self.dow = 39250
264 self.mtow = 61410
265 self.mlw = 58059
266 self.mzfw = 54657
[7]267
268#---------------------------------------------------------------------------------------
269
270class B738(Boeing737):
271 """Boeing 737-800 aircraft."""
[8]272 def __init__(self, flight):
273 super(B738, self).__init__(flight)
[9]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
[7]286
287#---------------------------------------------------------------------------------------
288
289class B733(Boeing737):
290 """Boeing 737-300 aircraft."""
[8]291 def __init__(self, flight):
292 super(B733, self).__init__(flight)
[9]293 self.dow = 32700
294 self.mtow = 62820
295 self.mlw = 51700
296 self.mzfw = 48410
[7]297
298#---------------------------------------------------------------------------------------
299
300class B734(Boeing737):
301 """Boeing 737-400 aircraft."""
[8]302 def __init__(self, flight):
303 super(B734, self).__init__(flight)
[9]304 self.dow = 33200
305 self.mtow = 68050
306 self.mlw = 56200
307 self.mzfw = 53100
[7]308
309#---------------------------------------------------------------------------------------
310
311class B735(Boeing737):
312 """Boeing 737-500 aircraft."""
[8]313 def __init__(self, flight):
314 super(B735, self).__init__(flight)
[9]315 self.dow = 31300
316 self.mtow = 60550
317 self.mlw = 50000
318 self.mzfw = 46700
[7]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."""
[8]330 def __init__(self, flight):
331 super(DH8D, self).__init__(flight)
[9]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),))
[7]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"""
[9]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 }
[7]370
[9]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
[7]380#---------------------------------------------------------------------------------------
381
382class B762(Boeing767):
383 """Boeing 767-200 aircraft."""
[8]384 def __init__(self, flight):
385 super(B762, self).__init__(flight)
[9]386 self.dow = 84507
387 self.mtow = 175540
388 self.mlw = 126098
389 self.mzfw = 114758
[7]390
391#---------------------------------------------------------------------------------------
392
393class B763(Boeing767):
394 """Boeing 767-300 aircraft."""
[8]395 def __init__(self, flight):
396 super(B763, self).__init__(cflight)
[9]397 self.dow = 91311
398 self.mtow = 181436
399 self.mlw = 137892
400 self.mzfw = 130635
[7]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."""
[8]412 def __init__(self, flight):
413 super(CRJ2, self).__init__(flight)
[9]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 }
[7]423
[9]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
[7]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."""
[8]443 def __init__(self, flight):
444 super(F70, self).__init__(flight)
[9]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 }
[7]455
[9]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
[7]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:
[52]472 - fuel: left, right, left aux, right aux
[7]473 - rpm: left, right
474 - reverser: left, right."""
[8]475 def __init__(self, flight):
476 super(DC3, self).__init__(flight)
[9]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 }
[8]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:
[29]491 if rpm>0: return False
492 return True
[7]493
[9]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
[7]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."""
[8]514 def __init__(self, flight):
515 super(T134, self).__init__(flight)
[9]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 }
[7]524
[9]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
[7]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"""
[8]546 def __init__(self, flight):
547 super(T154, self).__init__(flight)
[9]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),))
[7]566
567#---------------------------------------------------------------------------------------
568
[9]569
[7]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"""
[8]578 def __init__(self, flight):
579 super(YK40, self).__init__(flight)
[9]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),))
[7]595
596#---------------------------------------------------------------------------------------
597
[8]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.