source: src/mlx/acft.py@ 591:347c98077c1f

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

Added a check for the amount of landing fuel being not less than the minimum (re #236)

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