source: src/mlx/acft.py@ 597:79a1c9f14bc5

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

It is no longer a NOGO if the amount of landing fuel is too low (re #239)

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