source: src/mlx/acft.py@ 52:09c8dec95072

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

Basic FSUIP simulation works

File size: 23.7 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))
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" % \
[12]146 (self._minVS, self._maxVS))
[9]147 elif newStage==const.STAGE_PARKING:
148 self.logger.message(aircraftState.timestamp, "Block time end")
[8]149
[9]150 def prepareFlare(self):
151 """Called when it is detected that we will soon flare.
[8]152
153 On the first call, it should start monitoring some parameters more
154 closely to determine flare time."""
[9]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,))
[8]193
194 def cancelFlare(self):
195 """Cancel flare, if it has started."""
[9]196 self.flight.simulator.cancelFlare()
[8]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
[4]207#---------------------------------------------------------------------------------------
[7]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"""
[9]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 }
[7]228
[9]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
[7]238#---------------------------------------------------------------------------------------
239
240class B736(Boeing737):
241 """Boeing 737-600 aircraft."""
[8]242 def __init__(self, flight):
243 super(B736, self).__init__(flight)
[9]244 self.dow = 38307
245 self.mtow = 58328
246 self.mlw = 54657
247 self.mzfw = 51482
[7]248
249#---------------------------------------------------------------------------------------
250
251class B737(Boeing737):
252 """Boeing 737-700 aircraft."""
[8]253 def __init__(self, flight):
254 super(B737, self).__init__(flight)
[9]255 self.dow = 39250
256 self.mtow = 61410
257 self.mlw = 58059
258 self.mzfw = 54657
[7]259
260#---------------------------------------------------------------------------------------
261
262class B738(Boeing737):
263 """Boeing 737-800 aircraft."""
[8]264 def __init__(self, flight):
265 super(B738, self).__init__(flight)
[9]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
[7]278
279#---------------------------------------------------------------------------------------
280
281class B733(Boeing737):
282 """Boeing 737-300 aircraft."""
[8]283 def __init__(self, flight):
284 super(B733, self).__init__(flight)
[9]285 self.dow = 32700
286 self.mtow = 62820
287 self.mlw = 51700
288 self.mzfw = 48410
[7]289
290#---------------------------------------------------------------------------------------
291
292class B734(Boeing737):
293 """Boeing 737-400 aircraft."""
[8]294 def __init__(self, flight):
295 super(B734, self).__init__(flight)
[9]296 self.dow = 33200
297 self.mtow = 68050
298 self.mlw = 56200
299 self.mzfw = 53100
[7]300
301#---------------------------------------------------------------------------------------
302
303class B735(Boeing737):
304 """Boeing 737-500 aircraft."""
[8]305 def __init__(self, flight):
306 super(B735, self).__init__(flight)
[9]307 self.dow = 31300
308 self.mtow = 60550
309 self.mlw = 50000
310 self.mzfw = 46700
[7]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."""
[8]322 def __init__(self, flight):
323 super(DH8D, self).__init__(flight)
[9]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),))
[7]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"""
[9]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 }
[7]362
[9]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
[7]372#---------------------------------------------------------------------------------------
373
374class B762(Boeing767):
375 """Boeing 767-200 aircraft."""
[8]376 def __init__(self, flight):
377 super(B762, self).__init__(flight)
[9]378 self.dow = 84507
379 self.mtow = 175540
380 self.mlw = 126098
381 self.mzfw = 114758
[7]382
383#---------------------------------------------------------------------------------------
384
385class B763(Boeing767):
386 """Boeing 767-300 aircraft."""
[8]387 def __init__(self, flight):
388 super(B763, self).__init__(cflight)
[9]389 self.dow = 91311
390 self.mtow = 181436
391 self.mlw = 137892
392 self.mzfw = 130635
[7]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."""
[8]404 def __init__(self, flight):
405 super(CRJ2, self).__init__(flight)
[9]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 }
[7]415
[9]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
[7]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."""
[8]435 def __init__(self, flight):
436 super(F70, self).__init__(flight)
[9]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 }
[7]447
[9]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
[7]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:
[52]464 - fuel: left, right, left aux, right aux
[7]465 - rpm: left, right
466 - reverser: left, right."""
[8]467 def __init__(self, flight):
468 super(DC3, self).__init__(flight)
[9]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 }
[8]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:
[29]483 if rpm>0: return False
484 return True
[7]485
[9]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
[7]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."""
[8]506 def __init__(self, flight):
507 super(T134, self).__init__(flight)
[9]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 }
[7]516
[9]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
[7]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"""
[8]538 def __init__(self, flight):
539 super(T154, self).__init__(flight)
[9]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),))
[7]558
559#---------------------------------------------------------------------------------------
560
[9]561
[7]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"""
[8]570 def __init__(self, flight):
571 super(YK40, self).__init__(flight)
[9]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),))
[7]587
588#---------------------------------------------------------------------------------------
589
[8]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.