source: src/mlx/acft.py@ 611:6e8540c1970b

Last change on this file since 611:6e8540c1970b was 611:6e8540c1970b, checked in by István Váradi <ivaradi@…>, 10 years ago

Added checks for tailstrike during takeoff and touchdown (re #250)

File size: 43.2 KB
Line 
1
2import const
3import checks
4import fs
5from i18n import xstr
6import util
7
8import sys
9import time
10import traceback
11
12from collections import deque
13
14#---------------------------------------------------------------------------------------
15
16## @package mlx.acft
17#
18# The simulator-independent aircraft classes.
19#
20# This module contains the aircraft classes that contain some general data
21# about each aircraft type in the MAVA Fleet. The base class, \ref Aircraft
22# also implements some parts of the logging and traces some data. The classes
23# are also responsible for creating the \ref mlx.checks "checkers". Most of
24# them are created by the \ref Aircraft class' constructor, but there can be
25# type-specific differences. For example the lights are different for different
26# types, so there is a \ref Aircraft._appendLightsLoggers "function" which can
27# be reimplemented in child classes, if needed. This class maintains also the
28# \ref SmoothedValue "smoothed values" of the IAS and the VS and set these
29# values in the \ref mlx.fs.AircraftState "aircraft state" when it is received
30# from the simulator.
31
32#---------------------------------------------------------------------------------------
33
34# Derate type: no derate possible
35DERATE_NONE = 0
36
37# Derate type: Boeing, i.e. a percentage value.
38# For logging, the percentage value is expected as a string (i.e. whatever the
39# pilot enters into the text field).
40DERATE_BOEING = 1
41
42# Derate type: EPR, i.e. an EPR value.
43# For logging, the EPR value is expected as a string (i.e. whatever the pilot
44# enters into the text field).
45DERATE_EPR = 2
46
47# Derate type: Tupolev, i.e. nominal or takeoff
48# For logging, one of the DERATE_TUPOLEV_xxx values are expected.
49DERATE_TUPOLEV = 3
50
51# Tupolev derate value: nominal
52DERATE_TUPOLEV_NOMINAL = 1
53
54# Tupolev derate value: takeoff
55DERATE_TUPOLEV_TAKEOFF = 2
56
57# Derate type: BAe-146, i.e. enabled or not
58# For logging, a boolean is expected.
59DERATE_B462 = 4
60
61#---------------------------------------------------------------------------------------
62
63class SmoothedValue(object):
64 """A smoothed value."""
65 def __init__(self):
66 """Construct the value."""
67 self._deque = deque()
68 self._sum = 0
69
70 def add(self, length, value):
71 """Add the given value and smooth with the given length."""
72 dequeLength = len(self._deque)
73 while dequeLength>=length:
74 self._sum -= self._deque.popleft()
75 dequeLength -= 1
76
77 self._sum += value
78 self._deque.append(value)
79
80 def get(self):
81 """Get the average."""
82 return self._sum / len(self._deque)
83
84#---------------------------------------------------------------------------------------
85
86class Aircraft(object):
87 """Base class for aircraft."""
88 @staticmethod
89 def create(flight):
90 """Create an aircraft instance for the type in the given flight."""
91 return _classes[flight.aircraftType](flight)
92
93 def __init__(self, flight, minLandingFuel = None,
94 recommendedLandingFuel = None):
95 """Construct the aircraft for the given type."""
96 self._flight = flight
97 self._minLandingFuel = minLandingFuel
98 self._recommendedLandingFuel = recommendedLandingFuel
99
100 self._name = None
101 self._modelName = None
102
103 self._aircraftState = None
104
105 self._maxVS = -10000.0
106 self._minVS = 10000.0
107
108 self._v1r2LineIndex = None
109 self._derateLineID = None
110 self._takeoffAntiIceLineID = None
111 self._vrefLineIndex = None
112 self._landingAntiIceLineID = None
113
114 self.humanWeight = 82.0
115
116 self.initialClimbSpeedAltitude = 1500
117 self.reverseMinSpeed = 50
118
119 self.needNoStrobeSpeedCheck = False
120
121 self.maxTakeOffPitch = 15.0
122 self.maxTouchDownPitch = 15.0
123
124 self._checkers = []
125
126 config = flight.config
127 # Loggers
128
129 self._checkers.append(checks.StageChecker())
130 self._checkers.append(checks.TakeOffLogger())
131
132 self._checkers.append(checks.AltimeterLogger())
133
134 self._ilsLogger = checks.ILSLogger()
135 self._checkers.append(self._ilsLogger)
136 self._nav1Logger = checks.NAV1Logger()
137 self._checkers.append(self._nav1Logger)
138 self._nav2Logger = checks.NAV2Logger()
139 self._checkers.append(self._nav2Logger)
140
141 self._adf1Logger = checks.ADF1Logger()
142 self._checkers.append(self._adf1Logger)
143 self._adf2Logger = checks.ADF2Logger()
144 self._checkers.append(self._adf2Logger)
145
146 self._checkers.append(checks.SquawkLogger())
147
148 self._appendLightsLoggers()
149
150 self._checkers.append(checks.FlapsLogger())
151
152 self._checkers.append(checks.GearsLogger())
153 self._checkers.append(checks.InitialClimbSpeedLogger())
154 self._checkers.append(checks.CruiseSpeedLogger())
155 self._checkers.append(checks.SpoilerLogger())
156 self._checkers.append(checks.APLogger())
157
158 if config.isMessageTypeFS(const.MESSAGETYPE_VISIBILITY):
159 self._checkers.append(checks.VisibilityChecker())
160
161 # FIXME: we should have a central data model object, and not collect
162 # the data from the GUI. However, some pieces of data (e.g. V-speeds,
163 # etc. that is entered into the GUI) *should* be a part of the GUI and
164 # queried from it, so the model should have a reference to the GUI as
165 # well and access such data via the GUI!
166 if config.onlineACARS and flight.loggedIn and not flight.entranceExam:
167 self._checkers.append(checks.ACARSSender(flight._gui))
168
169 # Fault checkers
170
171 self._appendLightsCheckers()
172
173 self._checkers.append(checks.TransponderChecker())
174
175 self._checkers.append(checks.BankChecker())
176
177 self._checkers.append(checks.FlapsRetractChecker())
178 self._checkers.append(checks.FlapsSpeedLimitChecker())
179
180 self._checkers.append(checks.GearsDownChecker())
181 self._checkers.append(checks.GearSpeedLimitChecker())
182
183 self._checkers.append(checks.GLoadChecker())
184
185 self._checkers.append(checks.MLWChecker())
186 self._checkers.append(checks.MTOWChecker())
187 self._checkers.append(checks.MZFWChecker())
188 self._checkers.append(checks.PayloadChecker())
189
190 self._checkers.append(checks.NoStrobeSpeedChecker())
191 self._checkers.append(checks.SpeedChecker())
192 self._checkers.append(checks.VSChecker())
193
194 timeout = 30.0 + config.realIASSmoothingLength - 1
195 self._checkers.append(checks.OverspeedChecker(timeout = timeout))
196
197 self._checkers.append(checks.StallChecker())
198
199 self._checkers.append(checks.PitotChecker())
200
201 self._checkers.append(checks.ReverserLogger())
202 self._checkers.append(checks.ReverserChecker())
203
204 if flight.aircraftType is not None and config.enableApproachCallouts:
205 approachCallouts = flight.config.getApproachCallouts(flight.aircraftType)
206 if approachCallouts:
207 self._checkers.append(checks.ApproachCalloutsPlayer(approachCallouts))
208
209 self._smoothedIAS = SmoothedValue()
210 self._smoothedVS = SmoothedValue()
211
212 @property
213 def type(self):
214 """Get the type of the aircraft."""
215 return self._flight.aircraftType
216
217 @property
218 def flight(self):
219 """Get the flight the aircraft belongs to."""
220 return self._flight
221
222 @property
223 def minLandingFuel(self):
224 """Get the minimum acceptable amount of the landing fuel."""
225 return self._minLandingFuel
226
227 @property
228 def recommendedLandingFuel(self):
229 """Get the recommended amount of the landing fuel."""
230 return self._recommendedLandingFuel
231
232 @property
233 def logger(self):
234 """Get the logger to use for the aircraft."""
235 return self._flight.logger
236
237 @property
238 def state(self):
239 """Get the current aircraft state."""
240 return self._aircraftState
241
242 @property
243 def speedInKnots(self):
244 """Indicate if the speed is in knots.
245
246 This default implementation returns True."""
247 return True
248
249 @property
250 def timestamp(self):
251 """Get the timestamp of the current state."""
252 return None if self._aircraftState is None \
253 else self._aircraftState.timestamp
254
255 @property
256 def derateType(self):
257 """Get the derate type for this aircraft.
258
259 This default implementation returns DERATE_NONE."""
260 return DERATE_NONE
261
262 def getDerateLine(self, value):
263 """Get the log line for the given derate value.
264
265 It uses the the derate type and produces the standard message for
266 each. This children need not override it, although they can."""
267 dt = self.derateType
268
269 if dt==DERATE_BOEING:
270 return "Derate calculated by the pilot: %s %%" % \
271 ("-" if value is None else value,)
272 elif dt==DERATE_EPR:
273 return "EPR calculated by the pilot: %s" % \
274 ("-" if value is None else value,)
275 elif dt==DERATE_TUPOLEV:
276 return "Thrust setting calculated by the pilot: %s" % \
277 ("-" if value is None else
278 "nominal" if value==DERATE_TUPOLEV_NOMINAL else "takeoff",)
279 elif dt==DERATE_B462:
280 return "Derate setting: %s" % \
281 ("-" if value is None else "enabled" if value else "disabled",)
282 elif dt!=DERATE_NONE:
283 print "mlx.acft.getDerateLine: invalid derate type: " + dt
284
285 return None
286
287 def getFlapsSpeedLimit(self, flaps):
288 """Get the speed limit for the given flaps setting."""
289 return self.flapSpeedLimits[flaps] if flaps in self.flapSpeedLimits \
290 else None
291
292 def modelChanged(self, timestamp, aircraftName, modelName):
293 """Called when the simulator's aircraft changes."""
294 self._name = aircraftName
295 self._modelName = modelName
296 if self._flight.stage is not None:
297 self._logNameAndModel(timestamp)
298
299 def handleState(self, aircraftState):
300 """Called when the state of the aircraft changes.
301
302 This is the function that the simulator calls directly with the new
303 state."""
304 try:
305 config = self._flight.config
306
307 self._smoothedIAS.add(config.realIASSmoothingLength, aircraftState.ias)
308 aircraftState.smoothedIAS = self._smoothedIAS.get()
309
310 self._smoothedVS.add(config.realVSSmoothingLength, aircraftState.vs)
311 aircraftState.smoothedVS = self._smoothedVS.get()
312
313 for checker in self._checkers:
314 try:
315 checker.check(self._flight, self, self._flight.logger,
316 self._aircraftState, aircraftState)
317 except:
318 print >> sys.stderr, "Checker", checker, "failed"
319 traceback.print_exc()
320
321 self._flight.handleState(self._aircraftState, aircraftState)
322
323 self._maxVS = max(self._maxVS, aircraftState.vs)
324 self._minVS = min(self._minVS, aircraftState.vs)
325 except:
326 print >> sys.stderr, "Failed to handle the state"
327 traceback.print_exc()
328 finally:
329 self._aircraftState = aircraftState
330
331 def setStage(self, aircraftState, newStage):
332 """Set the given stage as the new one and do whatever should be
333 done."""
334 if newStage==const.STAGE_BOARDING:
335 self._logNameAndModel(aircraftState.timestamp)
336
337 if self._flight.setStage(aircraftState.timestamp, newStage):
338 if newStage==const.STAGE_PUSHANDTAXI:
339 self.logger.message(aircraftState.timestamp, "Block time start")
340 self._flight.logFuel(aircraftState)
341 self.logger.message(aircraftState.timestamp,
342 "ZFW: %.2f kg" % (aircraftState.zfw))
343 flight = self._flight
344 if flight.v1 is None or flight.vr is None or flight.v2 is None:
345 fs.sendMessage(const.MESSAGETYPE_HELP,
346 "Don't forget to set the takeoff V-speeds!",
347 5)
348 elif newStage==const.STAGE_TAKEOFF:
349 self.logger.message(aircraftState.timestamp,
350 "Flight time start")
351 self.logger.message(aircraftState.timestamp,
352 "Takeoff weight: %.0f kg, MTOW: %.0f kg" % \
353 (aircraftState.grossWeight, self.mtow))
354 self._logQNH(aircraftState)
355 self.logger.message(aircraftState.timestamp,
356 "Wind %03.0f/%.0f" % \
357 (aircraftState.windDirection,
358 aircraftState.windSpeed))
359 self._logRadios(aircraftState)
360 self._logV1R2(aircraftState)
361 self._logDerate(aircraftState)
362 self._logTakeoffAntiIce(aircraftState)
363 elif newStage==const.STAGE_DESCENT or newStage==const.STAGE_LANDING:
364 self._logRadios(aircraftState)
365 if newStage==const.STAGE_LANDING:
366 self._logQNH(aircraftState)
367 elif newStage==const.STAGE_TAXIAFTERLAND:
368 flight = self._flight
369 bookedFlight = flight.bookedFlight
370 config = flight.config
371 if config.onlineGateSystem and \
372 flight.loggedIn and \
373 not flight.entranceExam and \
374 bookedFlight.arrivalICAO=="LHBP" and \
375 config.isMessageTypeFS(const.MESSAGETYPE_GATE_SYSTEM):
376 self._flight.getFleet(callback = self._fleetRetrieved,
377 force = True)
378 self.logger.message(aircraftState.timestamp, "Flight time end")
379 self._flight.logFuel(aircraftState)
380 if self._minLandingFuel is not None and \
381 aircraftState.totalFuel<self._minLandingFuel:
382 self._flight.handleFault(self.__class__,
383 aircraftState.timestamp,
384 "The amount of the landing fuel is less than the minimum for this type: %ukgs (possible NO GO!)" %
385 (self._minLandingFuel,), 0)
386 self.logger.message(aircraftState.timestamp,
387 "Landing weight: %.0f kg, MLW: %.0f" % \
388 (aircraftState.grossWeight, self.mlw))
389 self.logger.message(aircraftState.timestamp,
390 "Vertical speed range: %.0f..%.0f feet/min" % \
391 (self._minVS, self._maxVS))
392 # elif newStage==const.STAGE_PARKING:
393 # self.logger.message(aircraftState.timestamp, "Block time end")
394 elif newStage==const.STAGE_END:
395 flightLength = self._flight.flightTimeEnd - self._flight.flightTimeStart
396 self.logger.message(aircraftState.timestamp, "Block time end")
397 self.logger.message(aircraftState.timestamp,
398 "Flight time: " +
399 util.getTimeIntervalString(flightLength))
400 self.logger.message(aircraftState.timestamp,
401 "Flown distance: %.2f NM" % \
402 (self._flight.flownDistance,))
403 blockLength = self._flight.blockTimeEnd - self._flight.blockTimeStart
404 self.logger.message(aircraftState.timestamp,
405 "Block time: " +
406 util.getTimeIntervalString(blockLength))
407
408 def prepareFlare(self):
409 """Called when it is detected that we will soon flare.
410
411 On the first call, it should start monitoring some parameters more
412 closely to determine flare time."""
413 self.flight.simulator.startFlare()
414
415 def flareStarted(self, windSpeed, windDirection, visibility,
416 flareStart, flareStartFS):
417 """Called when the flare has started."""
418 self.logger.message(self._aircraftState.timestamp, "The flare has begun")
419 self.logger.message(self._aircraftState.timestamp,
420 "Wind %03.0f/%.0f" % \
421 (windDirection, windSpeed))
422 self.logger.message(self._aircraftState.timestamp,
423 "Visibility: %.0f metres" % (visibility,))
424 self._logQNH(self._aircraftState)
425 self._logVRef()
426 self._logLandingAntiIce(self._aircraftState)
427 self.flight.flareStarted(flareStart, flareStartFS)
428 fs.sendMessage(const.MESSAGETYPE_INFORMATION, "Flare-time", 3)
429
430 def flareFinished(self, flareEnd, flareEndFS, tdRate, tdRateCalculatedByFS,
431 ias, pitch, bank, heading):
432 """Called when the flare has finished."""
433 (flareTimeFromFS, flareTime) = self.flight.flareFinished(flareEnd,
434 flareEndFS,
435 tdRate)
436 self.logger.message(self._aircraftState.timestamp,
437 "Flaretime: %.3f (from %s)" % \
438 (flareTime,
439 "the simulator" if flareTimeFromFS else "real time",))
440 self.logger.message(self._aircraftState.timestamp,
441 "Touchdown rate: %.0f feet/min" % (tdRate,))
442 self.logger.message(self._aircraftState.timestamp,
443 "Touchdown rate was calculated by the %s" % \
444 ("simulator" if tdRateCalculatedByFS else "logger",))
445 flight = self._flight
446 self.logger.message(self._aircraftState.timestamp,
447 "Touchdown speed: %.0f %s" % \
448 (flight.speedFromKnots(ias),
449 flight.getEnglishSpeedUnit()))
450 self.logger.message(self._aircraftState.timestamp,
451 "Touchdown pitch: %.1f degrees" % (pitch,))
452 self.logger.message(self._aircraftState.timestamp,
453 "Touchdown bank: %.1f degrees" % (bank,))
454 self.logger.message(self._aircraftState.timestamp,
455 "Touchdown heading: %03.0f degrees" % (heading,))
456 self.logger.message(self._aircraftState.timestamp,
457 "CG: %.1f%%" % \
458 (self._aircraftState.cog*100.0,))
459
460 if abs(pitch)>self.maxTouchDownPitch:
461 self._flight.handleNoGo("TDPitch", self._aircraftState.timestamp,
462 "Touchdown pitch higher than aircraft maximum (%.2f)" % \
463 (self.maxTouchDownPitch,),
464 "TD TAILSTRIKE NO GO")
465
466 def cancelFlare(self):
467 """Cancel flare, if it has started."""
468 self.flight.simulator.cancelFlare()
469
470 def checkFlightEnd(self, aircraftState):
471 """Check if the end of the flight has arrived.
472
473 This default implementation checks the N1 values, but for
474 piston-powered aircraft you need to check the RPMs."""
475 for n1 in aircraftState.n1:
476 if n1>=0.5: return False
477 return True
478
479 def updateV1R2(self):
480 """Update the V1, Vr and V2 values from the flight, if the these values
481 have already been logged."""
482 if self._v1r2LineIndex is not None:
483 self._logV1R2()
484
485 def updateDerate(self):
486 """Update the derate value from the flight, if the these values
487 have already been logged."""
488 if self._derateLineID is not None:
489 self._logDerate()
490
491 def updateTakeoffAntiIce(self):
492 """Update the take-off anti-ice setting."""
493 if self._takeoffAntiIceLineID is not None:
494 self._logTakeoffAntiIce()
495
496 def _appendLightsLoggers(self):
497 """Append the loggers needed for the lights.
498
499 This default implementation adds the loggers for the anti-collision
500 lights, the landing lights, the strobe lights and the NAV lights."""
501 self._checkers.append(checks.AnticollisionLightsLogger())
502 self._checkers.append(checks.LandingLightsLogger())
503 self._checkers.append(checks.StrobeLightsLogger())
504 self._checkers.append(checks.NavLightsLogger())
505
506 def _appendLightsCheckers(self):
507 """Append the checkers needed for the lights.
508
509 This default implementation adds the checkers for the anti-collision
510 lights, the landing lights, the strobe lights and the NAV lights."""
511 self._checkers.append(checks.AntiCollisionLightsChecker())
512 self._checkers.append(checks.LandingLightsChecker())
513 self._checkers.append(checks.NavLightsChecker())
514 self._checkers.append(checks.StrobeLightsChecker())
515
516 def _speedToLog(self, speed):
517 """Convert the given speed (being either None or expressed in the
518 flight's speed unit into a string."""
519 if speed is None:
520 return "-"
521 else:
522 return str(speed) + " " + self._flight.getEnglishSpeedUnit()
523
524 def _logV1R2(self, state = None):
525 """Log the V1, Vr and V2 value either newly, or by updating the
526 corresponding line."""
527 message = "Calc. TO speeds: V1: %s, VR: %s, V2: %s" % \
528 (self._speedToLog(self._flight.v1),
529 self._speedToLog(self._flight.vr),
530 self._speedToLog(self._flight.v2))
531
532 if self._v1r2LineIndex is None:
533 if state is None:
534 state = self._aircraftState
535 self._v1r2LineIndex = \
536 self.logger.message(state.timestamp, message)
537 else:
538 self.logger.updateLine(self._v1r2LineIndex, message)
539
540 def _logDerate(self, state = None):
541 """Log the derate values either newly or by updating the corresponding
542 line."""
543 dt = self.derateType
544 if dt==DERATE_NONE:
545 return
546
547 message = self.getDerateLine(self._flight.derate)
548 if message is not None:
549 if self._derateLineID is None:
550 if state is None:
551 state = self._aircraftState
552 self._derateLineID = \
553 self.logger.message(state.timestamp, message)
554 else:
555 self.logger.updateLine(self._derateLineID, message)
556
557 def _logTakeoffAntiIce(self, state = None):
558 """Log the take-off anti-ice setting either newly or by updating the
559 corresponding line."""
560 antiIceOn = self._flight.takeoffAntiIceOn
561 if state is not None:
562 antiIceOn = antiIceOn or state.antiIceOn is True
563 self._flight.takeoffAntiIceOn = antiIceOn
564
565 message = "Anti-ice was turned %s" % \
566 ("ON" if antiIceOn else "OFF")
567
568 if self._takeoffAntiIceLineID is None:
569 if state is None:
570 state = self._aircraftState
571 self._takeoffAntiIceLineID = \
572 self.logger.message(state.timestamp, message)
573 else:
574 self.logger.updateLine(self._takeoffAntiIceLineID, message)
575
576 def updateVRef(self):
577 """Update the Vref value from the flight, if the Vref value has already
578 been logged."""
579 if self._vrefLineIndex is not None:
580 self._logVRef()
581
582 def _logVRef(self):
583 """Log the Vref value either newly, or by updating the corresponding
584 line."""
585 message = "VRef speed calculated by the pilot: %s" % \
586 (self._speedToLog(self._flight.vref),)
587 if self._vrefLineIndex is None:
588 self._vrefLineIndex = \
589 self.logger.message(self._aircraftState.timestamp, message)
590 else:
591 self.logger.updateLine(self._vrefLineIndex, message)
592
593 def updateLandingAntiIce(self):
594 """Update the landing anti-ice setting."""
595 if self._landingAntiIceLineID is not None:
596 self._logLandingAntiIce()
597
598 def _logLandingAntiIce(self, state = None):
599 """Log the landing anti-ice setting either newly or by updating the
600 corresponding line."""
601 antiIceOn = self._flight.landingAntiIceOn
602 if state is not None:
603 antiIceOn = antiIceOn or state.antiIceOn is True
604 self._flight.landingAntiIceOn = antiIceOn
605
606 message = "Anti-ice was turned %s" % \
607 ("ON" if antiIceOn else "OFF")
608
609 if self._landingAntiIceLineID is None:
610 if state is None:
611 state = self._aircraftState
612 self._landingAntiIceLineID = \
613 self.logger.message(state.timestamp, message)
614 else:
615 self.logger.updateLine(self._landingAntiIceLineID, message)
616
617 def _fleetRetrieved(self, fleet):
618 """Callback for the fleet retrieval result."""
619 if fleet is not None:
620 gateList = ""
621 occupiedGateNumbers = fleet.getOccupiedGateNumbers()
622 for gateNumber in const.lhbpGateNumbers:
623 if gateNumber not in occupiedGateNumbers:
624 if gateList: gateList += ", "
625 gateList += gateNumber
626 fs.sendMessage(const.MESSAGETYPE_GATE_SYSTEM,
627 "Free gates: " + gateList, 20)
628
629
630 def _logRadios(self, aircraftState):
631 """Log the radios from the given aircraft state."""
632 flight = self._flight
633 logger = flight.logger
634
635 self._ilsLogger.forceLog(flight, logger, aircraftState)
636 self._nav1Logger.forceLog(flight, logger, aircraftState)
637 self._nav2Logger.forceLog(flight, logger, aircraftState)
638 self._adf1Logger.forceLog(flight, logger, aircraftState)
639 self._adf2Logger.forceLog(flight, logger, aircraftState)
640
641 def _logQNH(self, aircraftState):
642 """Log the current QNH along with the altimeter setting."""
643 self.logger.message(aircraftState.timestamp,
644 "QNH: %.2f hPa, altimeter: %.2f hPa" % \
645 (aircraftState.qnh, aircraftState.altimeter))
646
647 def _logNameAndModel(self, timestamp):
648 """Log the aircraft's name and model with taking the timestamp from the
649 given state."""
650 self._flight.logger.message(timestamp,
651 "Aircraft: name='%s', model='%s'" % \
652 (self._name, self._modelName))
653
654#---------------------------------------------------------------------------------------
655
656class Boeing737(Aircraft):
657 """Base class for the various aircraft in the Boeing 737 family.
658
659 The aircraft type-specific values in the aircraft state have the following
660 structure:
661 - fuel: left, centre, right
662 - n1: left, right
663 - reverser: left, right"""
664 def __init__(self, flight, minLandingFuel = 2500,
665 recommendedLandingFuel = 3500):
666 super(Boeing737, self).__init__(flight,
667 minLandingFuel = minLandingFuel,
668 recommendedLandingFuel =
669 recommendedLandingFuel)
670
671 self.gearSpeedLimit = 270
672 self.flapSpeedLimits = { 1 : 260,
673 2 : 260,
674 5 : 250,
675 10 : 210,
676 15 : 200,
677 25 : 190,
678 30 : 175,
679 40 : 162 }
680
681 @property
682 def derateType(self):
683 """Get the derate type for this type."""
684 return DERATE_BOEING
685
686#---------------------------------------------------------------------------------------
687
688class B736(Boeing737):
689 """Boeing 737-600 aircraft."""
690 def __init__(self, flight):
691 super(B736, self).__init__(flight)
692 self.dow = 38307
693 self.mtow = 58328
694 self.mlw = 54657
695 self.mzfw = 51482
696 self.maxTakeOffPitch = 16.2
697 self.maxTouchDownPitch = 14.7
698
699#---------------------------------------------------------------------------------------
700
701class B737(Boeing737):
702 """Boeing 737-700 aircraft."""
703 def __init__(self, flight):
704 super(B737, self).__init__(flight)
705 self.dow = 39250
706 self.mtow = 61410
707 self.mlw = 58059
708 self.mzfw = 54657
709 self.maxTakeOffPitch = 14.7
710 self.maxTouchDownPitch = 13.2
711
712#---------------------------------------------------------------------------------------
713
714class B738(Boeing737):
715 """Boeing 737-800 aircraft."""
716 def __init__(self, flight):
717 super(B738, self).__init__(flight)
718 self.dow = 42690
719 self.mtow = 71709
720 self.mlw = 65317
721 self.mzfw = 61688
722 self.maxTakeOffPitch = 11
723 self.maxTouchDownPitch = 9.5
724
725#---------------------------------------------------------------------------------------
726
727class B738Charter(B738):
728 """Boeing 737-800 aircraft used for charters."""
729 def __init__(self, flight):
730 super(B738Charter, self).__init__(flight)
731 self.mtow = 77791
732
733#---------------------------------------------------------------------------------------
734
735class Boeing737CL(Boeing737):
736 """Base class for the various aircraft in the Boeing 737 Classic family."""
737 def __init__(self, flight):
738 super(Boeing737CL, self).__init__(flight, minLandingFuel = 3500,
739 recommendedLandingFuel = None)
740
741#---------------------------------------------------------------------------------------
742
743class B733(Boeing737CL):
744 """Boeing 737-300 aircraft."""
745 def __init__(self, flight):
746 super(B733, self).__init__(flight)
747 self.dow = 32700
748 self.mtow = 62820
749 self.mlw = 51700
750 self.mzfw = 48410
751 self.maxTakeOffPitch = 13.4
752 self.maxTouchDownPitch = 12.0
753
754#---------------------------------------------------------------------------------------
755
756class B734(Boeing737CL):
757 """Boeing 737-400 aircraft."""
758 def __init__(self, flight):
759 super(B734, self).__init__(flight)
760 self.dow = 33200
761 self.mtow = 68050
762 self.mlw = 56200
763 self.mzfw = 53100
764 self.maxTakeOffPitch = 11.4
765 self.maxTouchDownPitch = 10
766
767#---------------------------------------------------------------------------------------
768
769class B735(Boeing737CL):
770 """Boeing 737-500 aircraft."""
771 def __init__(self, flight):
772 super(B735, self).__init__(flight)
773 self.dow = 31300
774 self.mtow = 60550
775 self.mlw = 50000
776 self.mzfw = 46700
777 self.maxTakeOffPitch = 14.7
778 self.maxTouchDownPitch = 13.2
779
780#---------------------------------------------------------------------------------------
781
782class DH8D(Aircraft):
783 """Bombardier Dash-8 Q400 aircraft.
784
785 The aircraft type-specific values in the aircraft state have the following
786 structure:
787 - fuel: left, right
788 - n1: left, right
789 - reverser: left, right."""
790
791 def __init__(self, flight):
792 super(DH8D, self).__init__(flight, minLandingFuel = 2000)
793 self.dow = 17185
794 self.mtow = 29257
795 self.mlw = 28009
796 self.mzfw = 25855
797 self.gearSpeedLimit = 215
798 self.flapSpeedLimits = { 5 : 200,
799 10 : 181,
800 15 : 172,
801 35 : 158 }
802 self.maxTakeOffPitch = 8.0
803 self.maxTouchDownPitch = 7.0
804
805#---------------------------------------------------------------------------------------
806
807class Boeing767(Aircraft):
808 """Base class for the various aircraft in the Boeing 767 family.
809
810 The aircraft type-specific values in the aircraft state have the following
811 structure:
812 - fuel: left, centre, right
813 - n1: left, right
814 - reverser: left, right"""
815
816 def __init__(self, flight, minLandingFuel = 9000):
817 super(Boeing767, self).__init__(flight, minLandingFuel = minLandingFuel)
818 self.gearSpeedLimit = 270
819 self.flapSpeedLimits = { 1 : 255,
820 5 : 235,
821 10 : 215,
822 20 : 215,
823 25 : 185,
824 30 : 175 }
825
826 @property
827 def derateType(self):
828 """Get the derate type for this type."""
829 return DERATE_BOEING
830
831#---------------------------------------------------------------------------------------
832
833class B762(Boeing767):
834 """Boeing 767-200 aircraft."""
835 def __init__(self, flight):
836 super(B762, self).__init__(flight)
837 self.dow = 84507
838 self.mtow = 175540
839 self.mlw = 126098
840 self.mzfw = 114758
841 self.maxTakeOffPitch = 13.1
842 self.maxTouchDownPitch = 11.6
843
844#---------------------------------------------------------------------------------------
845
846class B763(Boeing767):
847 """Boeing 767-300 aircraft."""
848 def __init__(self, flight):
849 super(B763, self).__init__(flight)
850 self.dow = 91311
851 self.mtow = 181436
852 self.mlw = 137892
853 self.mzfw = 130635
854 self.maxTakeOffPitch = 9.6
855 self.maxTouchDownPitch = 8.1
856
857#---------------------------------------------------------------------------------------
858
859class CRJ2(Aircraft):
860 """Bombardier CRJ-200 aircraft.
861
862 The aircraft type-specific values in the aircraft state have the following
863 structure:
864 - fuel: left, centre, right
865 - n1: left, right
866 - reverser: left, right."""
867 def __init__(self, flight):
868 super(CRJ2, self).__init__(flight, minLandingFuel = 1000)
869 self.dow = 14549
870 self.mtow = 22995
871 self.mlw = 21319
872 self.mzfw = 19958
873 self.gearSpeedLimit = 240
874 self.flapSpeedLimits = { 8 : 260,
875 20 : 220,
876 30 : 190,
877 45 : 175 }
878 self.maxTakeOffPitch = 18.0
879 self.maxTouchDownPitch = 18.0
880
881#---------------------------------------------------------------------------------------
882
883class F70(Aircraft):
884 """Fokker 70 aircraft.
885
886 The aircraft type-specific values in the aircraft state have the following
887 structure:
888 - fuel: left, centre, right
889 - n1: left, right
890 - reverser: left, right."""
891 def __init__(self, flight):
892 super(F70, self).__init__(flight, minLandingFuel = 1900)
893 self.dow = 24283
894 self.mtow = 38100 # FIXME: differentiate by registration number,
895 # MTOW of HA-LMF: 41955
896 self.mlw = 36740
897 self.mzfw = 32655
898 self.gearSpeedLimit = 200
899 self.flapSpeedLimits = { 8 : 250,
900 15 : 220,
901 25 : 220,
902 42 : 180 }
903 self.reverseMinSpeed = 50
904 self.maxTakeOffPitch = 16.0
905 self.maxTouchDownPitch = 16.0
906
907 @property
908 def derateType(self):
909 """Get the derate type for this type."""
910 return DERATE_EPR
911
912#---------------------------------------------------------------------------------------
913
914class DC3(Aircraft):
915 """Lisunov Li-2 (DC-3) aircraft.
916
917 The aircraft type-specific values in the aircraft state have the following
918 structure:
919 - fuel: left aux, left, right, right aux
920 - rpm: left, right
921 - reverser: left, right."""
922 def __init__(self, flight):
923 super(DC3, self).__init__(flight)
924 self.dow = 8627
925 self.mtow = 11884
926 self.mlw = 11793
927 self.mzfw = 11780
928 self.gearSpeedLimit = 148
929 self.flapSpeedLimits = { 15 : 135,
930 30 : 99,
931 45 : 97 }
932
933 def _checkFlightEnd(self, aircraftState):
934 """Check if the end of the flight has arrived.
935
936 This implementation checks the RPM values to be 0."""
937 for rpm in aircraftState.rpm:
938 if rpm>0: return False
939 return True
940
941#---------------------------------------------------------------------------------------
942
943class T134(Aircraft):
944 """Tupolev Tu-134 aircraft.
945
946 The aircraft type-specific values in the aircraft state have the following
947 structure:
948 - fuel: left tip, left aux, centre, right aux, right tip, external 1,
949 external 2
950 - n1: left, right
951 - reverser: left, right."""
952 def __init__(self, flight):
953 super(T134, self).__init__(flight, minLandingFuel = 3000)
954 self.dow = 29500
955 self.mtow = 49000
956 self.mlw = 43000
957 self.mzfw = 38500
958 self.gearSpeedLimit = 216
959 self.flapSpeedLimits = { 10 : 450,
960 20 : 400,
961 30 : 300 }
962 self.reverseMinSpeed = 50
963
964 self.needNoStrobeSpeedCheck = True
965
966 self.maxTakeOffPitch = 16.0
967 self.maxTouchDownPitch = 16.0
968
969 @property
970 def derateType(self):
971 """Get the derate type for this type."""
972 return DERATE_TUPOLEV
973
974 @property
975 def speedInKnots(self):
976 """Indicate if the speed is in knots."""
977 return False
978
979 def _appendLightsLoggers(self):
980 """Append the loggers needed for the lights."""
981 self._checkers.append(checks.AnticollisionLightsLogger())
982 self._checkers.append(checks.LandingLightsLogger())
983 self._checkers.append(checks.NavLightsLogger())
984
985 def _appendLightsCheckers(self):
986 """Append the checkers needed for the lights."""
987 self._checkers.append(checks.TupolevAntiCollisionLightsChecker())
988 self._checkers.append(checks.LandingLightsChecker())
989 self._checkers.append(checks.NavLightsChecker())
990
991#---------------------------------------------------------------------------------------
992
993class T154(Aircraft):
994 """Tupolev Tu-154 aircraft.
995
996 The aircraft type-specific values in the aircraft state have the following
997 structure:
998 - fuel: left aux, left, centre, centre 2, right, right aux
999 - n1: left, centre, right
1000 - reverser: left, right"""
1001 def __init__(self, flight):
1002 super(T154, self).__init__(flight, minLandingFuel = 5000)
1003 self.dow = 53259
1004 self.mtow = 98000
1005 self.mlw = 78000
1006 self.mzfw = 72000
1007 self.gearSpeedLimit = 216
1008 self.flapSpeedLimits = { 15 : 227,
1009 28 : 194,
1010 45 : 162 }
1011 self.reverseMinSpeed = 50
1012
1013 self.needNoStrobeSpeedCheck = True
1014
1015 self.maxTakeOffPitch = 16.0
1016 self.maxTouchDownPitch = 16.0
1017
1018 @property
1019 def speedInKnots(self):
1020 """Indicate if the speed is in knots."""
1021 return False
1022
1023 @property
1024 def derateType(self):
1025 """Get the derate type for this type."""
1026 return DERATE_TUPOLEV
1027
1028 def _appendLightsLoggers(self):
1029 """Append the loggers needed for the lights."""
1030 self._checkers.append(checks.AnticollisionLightsLogger())
1031 self._checkers.append(checks.LandingLightsLogger())
1032 self._checkers.append(checks.NavLightsLogger())
1033
1034 def _appendLightsCheckers(self):
1035 """Append the checkers needed for the lights."""
1036 self._checkers.append(checks.AntiCollisionLightsChecker())
1037 self._checkers.append(checks.LandingLightsChecker())
1038 self._checkers.append(checks.NavLightsChecker())
1039
1040#---------------------------------------------------------------------------------------
1041
1042class YK40(Aircraft):
1043 """Yakovlev Yak-40 aircraft.
1044
1045 The aircraft type-specific values in the aircraft state have the following
1046 structure:
1047 - fuel: left, right
1048 - n1: left, right
1049 - reverser: left, right"""
1050 def __init__(self, flight):
1051 super(YK40, self).__init__(flight)
1052 self.dow = 9400
1053 self.mtow = 17200
1054 self.mlw = 16800
1055 self.mzfw = 12100
1056 self.gearSpeedLimit = 165
1057 self.flapSpeedLimits = { 20 : 165,
1058 35 : 135 }
1059
1060 self.needNoStrobeSpeedCheck = True
1061
1062 @property
1063 def speedInKnots(self):
1064 """Indicate if the speed is in knots."""
1065 return False
1066
1067 @property
1068 def derateType(self):
1069 """Get the derate type for this type."""
1070 return DERATE_TUPOLEV
1071
1072 def _appendLightsLoggers(self):
1073 """Append the loggers needed for the lights."""
1074 self._checkers.append(checks.AnticollisionLightsLogger())
1075 self._checkers.append(checks.LandingLightsLogger())
1076 self._checkers.append(checks.NavLightsLogger())
1077
1078 def _appendLightsCheckers(self):
1079 """Append the checkers needed for the lights."""
1080 self._checkers.append(checks.AntiCollisionLightsChecker())
1081 self._checkers.append(checks.LandingLightsChecker())
1082 self._checkers.append(checks.NavLightsChecker())
1083
1084#---------------------------------------------------------------------------------------
1085
1086class B462(Aircraft):
1087 """British Aerospace BAe-146 aircraft.
1088
1089 The aircraft type-specific values in the aircraft state have the following
1090 structure:
1091 - fuel: left, centre, right
1092 - n1: left outer, left inner, right inner, right outer
1093 - reverser: empty (the plane has no reversers)"""
1094 def __init__(self, flight):
1095 super(B462, self).__init__(flight)
1096 self.dow = 25706
1097 self.mtow = 43998
1098 self.mlw = 38599
1099 self.mzfw = 33792
1100 self.gearSpeedLimit = 210
1101 self.flapSpeedLimits = { 18 : 217,
1102 24 : 180,
1103 30 : 170,
1104 33 : 150 }
1105
1106 @property
1107 def derateType(self):
1108 """Get the derate type for this type."""
1109 return DERATE_B462
1110
1111#---------------------------------------------------------------------------------------
1112
1113mostFuelTanks = [const.FUELTANK_LEFT_TIP, const.FUELTANK_EXTERNAL1,
1114 const.FUELTANK_LEFT_AUX,
1115 const.FUELTANK_CENTRE,
1116 const.FUELTANK_RIGHT_AUX,
1117 const.FUELTANK_EXTERNAL2, const.FUELTANK_RIGHT_TIP]
1118
1119#---------------------------------------------------------------------------------------
1120
1121_classes = { const.AIRCRAFT_B736 : B736,
1122 const.AIRCRAFT_B737 : B737,
1123 const.AIRCRAFT_B738 : B738,
1124 const.AIRCRAFT_B738C : B738Charter,
1125 const.AIRCRAFT_B733 : B733,
1126 const.AIRCRAFT_B734 : B734,
1127 const.AIRCRAFT_B735 : B735,
1128 const.AIRCRAFT_DH8D : DH8D,
1129 const.AIRCRAFT_B762 : B762,
1130 const.AIRCRAFT_B763 : B763,
1131 const.AIRCRAFT_CRJ2 : CRJ2,
1132 const.AIRCRAFT_F70 : F70,
1133 const.AIRCRAFT_DC3 : DC3,
1134 const.AIRCRAFT_T134 : T134,
1135 const.AIRCRAFT_T154 : T154,
1136 const.AIRCRAFT_YK40 : YK40,
1137 const.AIRCRAFT_B462 : B462 }
1138
1139#---------------------------------------------------------------------------------------
1140
1141if __name__ == "__main__":
1142 value = SmoothedValue()
1143
1144 print "Adding 1, 12.0"
1145 value.add(1, 12.0)
1146 print value.get()
1147
1148 print "Adding 1, 15.0"
1149 value.add(1, 15.0)
1150 print value.get()
1151
1152 print "Adding 2, 18.0"
1153 value.add(2, 18.0)
1154 print value.get()
1155
1156 print "Adding 2, 20.0"
1157 value.add(2, 20.0)
1158 print value.get()
1159
1160 print "Adding 5, 22.0"
1161 value.add(5, 22.0)
1162 print value.get()
1163
1164 print "Adding 5, 25.0"
1165 value.add(5, 25.0)
1166 print value.get()
1167
1168 print "Adding 5, 29.0"
1169 value.add(5, 29.0)
1170 print value.get()
1171
1172 print "Adding 5, 21.0"
1173 value.add(5, 21.0)
1174 print value.get()
1175
1176 print "Adding 5, 26.0"
1177 value.add(5, 26.0)
1178 print value.get()
1179
1180 print "Adding 2, 30.0"
1181 value.add(2, 30.0)
1182 print value.get()
1183
1184 print "Adding 2, 55.0"
1185 value.add(2, 55.0)
1186 print value.get()
1187
1188#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.