source: src/mlx/acft.py@ 598:85f4667214b4

Last change on this file since 598:85f4667214b4 was 598:85f4667214b4, checked in by István Váradi <ivaradi@…>, 9 years ago

The reverser use is logged and is checked against IAS with a lower limit of 50 kts (re #242)

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