source: src/mlx/acft.py@ 55:f6a3ee5f8855

Last change on this file since 55:f6a3ee5f8855 was 52:09c8dec95072, checked in by István Váradi <ivaradi@…>, 13 years ago

Basic FSUIP simulation works

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