source: src/mlx/acft.py@ 95:d941f0f18648

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

The thrust checker is not present for the Dash-8 Q400

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