source: src/mlx/acft.py@ 592:1d9795bde55c

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

Added help about the minimum and recommended (if known) amounts of landing fuel to the fuel page (re #236)

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.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.handleNoGo(self.__class__,
377 aircraftState.timestamp,
378 "The amount of the landing fuel is less than the minimum for this type: %ukgs" %
379 (self._minLandingFuel,),
380 "LANDING FUEL NO GO")
381 self.logger.message(aircraftState.timestamp,
382 "Landing weight: %.0f kg, MLW: %.0f" % \
383 (aircraftState.grossWeight, self.mlw))
384 self.logger.message(aircraftState.timestamp,
385 "Vertical speed range: %.0f..%.0f feet/min" % \
386 (self._minVS, self._maxVS))
387 # elif newStage==const.STAGE_PARKING:
388 # self.logger.message(aircraftState.timestamp, "Block time end")
389 elif newStage==const.STAGE_END:
390 flightLength = self._flight.flightTimeEnd - self._flight.flightTimeStart
391 self.logger.message(aircraftState.timestamp, "Block time end")
392 self.logger.message(aircraftState.timestamp,
393 "Flight time: " +
394 util.getTimeIntervalString(flightLength))
395 self.logger.message(aircraftState.timestamp,
396 "Flown distance: %.2f NM" % \
397 (self._flight.flownDistance,))
398 blockLength = self._flight.blockTimeEnd - self._flight.blockTimeStart
399 self.logger.message(aircraftState.timestamp,
400 "Block time: " +
401 util.getTimeIntervalString(blockLength))
402
403 def prepareFlare(self):
404 """Called when it is detected that we will soon flare.
405
406 On the first call, it should start monitoring some parameters more
407 closely to determine flare time."""
408 self.flight.simulator.startFlare()
409
410 def flareStarted(self, windSpeed, windDirection, visibility,
411 flareStart, flareStartFS):
412 """Called when the flare has started."""
413 self.logger.message(self._aircraftState.timestamp, "The flare has begun")
414 self.logger.message(self._aircraftState.timestamp,
415 "Wind %03.0f/%.0f" % \
416 (windDirection, windSpeed))
417 self.logger.message(self._aircraftState.timestamp,
418 "Visibility: %.0f metres" % (visibility,))
419 self._logQNH(self._aircraftState)
420 self._logVRef()
421 self._logLandingAntiIce(self._aircraftState)
422 self.flight.flareStarted(flareStart, flareStartFS)
423 fs.sendMessage(const.MESSAGETYPE_INFORMATION, "Flare-time", 3)
424
425 def flareFinished(self, flareEnd, flareEndFS, tdRate, tdRateCalculatedByFS,
426 ias, pitch, bank, heading):
427 """Called when the flare has finished."""
428 (flareTimeFromFS, flareTime) = self.flight.flareFinished(flareEnd,
429 flareEndFS,
430 tdRate)
431 self.logger.message(self._aircraftState.timestamp,
432 "Flaretime: %.3f (from %s)" % \
433 (flareTime,
434 "the simulator" if flareTimeFromFS else "real time",))
435 self.logger.message(self._aircraftState.timestamp,
436 "Touchdown rate: %.0f feet/min" % (tdRate,))
437 self.logger.message(self._aircraftState.timestamp,
438 "Touchdown rate was calculated by the %s" % \
439 ("simulator" if tdRateCalculatedByFS else "logger",))
440 flight = self._flight
441 self.logger.message(self._aircraftState.timestamp,
442 "Touchdown speed: %.0f %s" % \
443 (flight.speedFromKnots(ias),
444 flight.getEnglishSpeedUnit()))
445 self.logger.message(self._aircraftState.timestamp,
446 "Touchdown pitch: %.1f degrees" % (pitch,))
447 self.logger.message(self._aircraftState.timestamp,
448 "Touchdown bank: %.1f degrees" % (bank,))
449 self.logger.message(self._aircraftState.timestamp,
450 "Touchdown heading: %03.0f degrees" % (heading,))
451 self.logger.message(self._aircraftState.timestamp,
452 "CG: %.1f%%" % \
453 (self._aircraftState.cog*100.0,))
454
455 def cancelFlare(self):
456 """Cancel flare, if it has started."""
457 self.flight.simulator.cancelFlare()
458
459 def checkFlightEnd(self, aircraftState):
460 """Check if the end of the flight has arrived.
461
462 This default implementation checks the N1 values, but for
463 piston-powered aircraft you need to check the RPMs."""
464 for n1 in aircraftState.n1:
465 if n1>=0.5: return False
466 return True
467
468 def updateV1R2(self):
469 """Update the V1, Vr and V2 values from the flight, if the these values
470 have already been logged."""
471 if self._v1r2LineIndex is not None:
472 self._logV1R2()
473
474 def updateDerate(self):
475 """Update the derate value from the flight, if the these values
476 have already been logged."""
477 if self._derateLineID is not None:
478 self._logDerate()
479
480 def updateTakeoffAntiIce(self):
481 """Update the take-off anti-ice setting."""
482 if self._takeoffAntiIceLineID is not None:
483 self._logTakeoffAntiIce()
484
485 def _appendLightsLoggers(self):
486 """Append the loggers needed for the lights.
487
488 This default implementation adds the loggers for the anti-collision
489 lights, the landing lights, the strobe lights and the NAV lights."""
490 self._checkers.append(checks.AnticollisionLightsLogger())
491 self._checkers.append(checks.LandingLightsLogger())
492 self._checkers.append(checks.StrobeLightsLogger())
493 self._checkers.append(checks.NavLightsLogger())
494
495 def _appendLightsCheckers(self):
496 """Append the checkers needed for the lights.
497
498 This default implementation adds the checkers for the anti-collision
499 lights, the landing lights, the strobe lights and the NAV lights."""
500 self._checkers.append(checks.AntiCollisionLightsChecker())
501 self._checkers.append(checks.LandingLightsChecker())
502 self._checkers.append(checks.NavLightsChecker())
503 self._checkers.append(checks.StrobeLightsChecker())
504
505 def _speedToLog(self, speed):
506 """Convert the given speed (being either None or expressed in the
507 flight's speed unit into a string."""
508 if speed is None:
509 return "-"
510 else:
511 return str(speed) + " " + self._flight.getEnglishSpeedUnit()
512
513 def _logV1R2(self, state = None):
514 """Log the V1, Vr and V2 value either newly, or by updating the
515 corresponding line."""
516 message = "Calc. TO speeds: V1: %s, VR: %s, V2: %s" % \
517 (self._speedToLog(self._flight.v1),
518 self._speedToLog(self._flight.vr),
519 self._speedToLog(self._flight.v2))
520
521 if self._v1r2LineIndex is None:
522 if state is None:
523 state = self._aircraftState
524 self._v1r2LineIndex = \
525 self.logger.message(state.timestamp, message)
526 else:
527 self.logger.updateLine(self._v1r2LineIndex, message)
528
529 def _logDerate(self, state = None):
530 """Log the derate values either newly or by updating the corresponding
531 line."""
532 dt = self.derateType
533 if dt==DERATE_NONE:
534 return
535
536 message = self.getDerateLine(self._flight.derate)
537 if message is not None:
538 if self._derateLineID is None:
539 if state is None:
540 state = self._aircraftState
541 self._derateLineID = \
542 self.logger.message(state.timestamp, message)
543 else:
544 self.logger.updateLine(self._derateLineID, message)
545
546 def _logTakeoffAntiIce(self, state = None):
547 """Log the take-off anti-ice setting either newly or by updating the
548 corresponding line."""
549 antiIceOn = self._flight.takeoffAntiIceOn
550 if state is not None:
551 antiIceOn = antiIceOn or state.antiIceOn is True
552 self._flight.takeoffAntiIceOn = antiIceOn
553
554 message = "Anti-ice was turned %s" % \
555 ("ON" if antiIceOn else "OFF")
556
557 if self._takeoffAntiIceLineID is None:
558 if state is None:
559 state = self._aircraftState
560 self._takeoffAntiIceLineID = \
561 self.logger.message(state.timestamp, message)
562 else:
563 self.logger.updateLine(self._takeoffAntiIceLineID, message)
564
565 def updateVRef(self):
566 """Update the Vref value from the flight, if the Vref value has already
567 been logged."""
568 if self._vrefLineIndex is not None:
569 self._logVRef()
570
571 def _logVRef(self):
572 """Log the Vref value either newly, or by updating the corresponding
573 line."""
574 message = "VRef speed calculated by the pilot: %s" % \
575 (self._speedToLog(self._flight.vref),)
576 if self._vrefLineIndex is None:
577 self._vrefLineIndex = \
578 self.logger.message(self._aircraftState.timestamp, message)
579 else:
580 self.logger.updateLine(self._vrefLineIndex, message)
581
582 def updateLandingAntiIce(self):
583 """Update the landing anti-ice setting."""
584 if self._landingAntiIceLineID is not None:
585 self._logLandingAntiIce()
586
587 def _logLandingAntiIce(self, state = None):
588 """Log the landing anti-ice setting either newly or by updating the
589 corresponding line."""
590 antiIceOn = self._flight.landingAntiIceOn
591 if state is not None:
592 antiIceOn = antiIceOn or state.antiIceOn is True
593 self._flight.landingAntiIceOn = antiIceOn
594
595 message = "Anti-ice was turned %s" % \
596 ("ON" if antiIceOn else "OFF")
597
598 if self._landingAntiIceLineID is None:
599 if state is None:
600 state = self._aircraftState
601 self._landingAntiIceLineID = \
602 self.logger.message(state.timestamp, message)
603 else:
604 self.logger.updateLine(self._landingAntiIceLineID, message)
605
606 def _fleetRetrieved(self, fleet):
607 """Callback for the fleet retrieval result."""
608 if fleet is not None:
609 gateList = ""
610 occupiedGateNumbers = fleet.getOccupiedGateNumbers()
611 for gateNumber in const.lhbpGateNumbers:
612 if gateNumber not in occupiedGateNumbers:
613 if gateList: gateList += ", "
614 gateList += gateNumber
615 fs.sendMessage(const.MESSAGETYPE_GATE_SYSTEM,
616 "Free gates: " + gateList, 20)
617
618
619 def _logRadios(self, aircraftState):
620 """Log the radios from the given aircraft state."""
621 flight = self._flight
622 logger = flight.logger
623
624 self._ilsLogger.forceLog(flight, logger, aircraftState)
625 self._nav1Logger.forceLog(flight, logger, aircraftState)
626 self._nav2Logger.forceLog(flight, logger, aircraftState)
627 self._adf1Logger.forceLog(flight, logger, aircraftState)
628 self._adf2Logger.forceLog(flight, logger, aircraftState)
629
630 def _logQNH(self, aircraftState):
631 """Log the current QNH along with the altimeter setting."""
632 self.logger.message(aircraftState.timestamp,
633 "QNH: %.2f hPa, altimeter: %.2f hPa" % \
634 (aircraftState.qnh, aircraftState.altimeter))
635
636 def _logNameAndModel(self, timestamp):
637 """Log the aircraft's name and model with taking the timestamp from the
638 given state."""
639 self._flight.logger.message(timestamp,
640 "Aircraft: name='%s', model='%s'" % \
641 (self._name, self._modelName))
642
643#---------------------------------------------------------------------------------------
644
645class Boeing737(Aircraft):
646 """Base class for the various aircraft in the Boeing 737 family.
647
648 The aircraft type-specific values in the aircraft state have the following
649 structure:
650 - fuel: left, centre, right
651 - n1: left, right
652 - reverser: left, right"""
653 def __init__(self, flight, minLandingFuel = 2500,
654 recommendedLandingFuel = 3500):
655 super(Boeing737, self).__init__(flight,
656 minLandingFuel = minLandingFuel,
657 recommendedLandingFuel =
658 recommendedLandingFuel)
659
660 self.gearSpeedLimit = 270
661 self.flapSpeedLimits = { 1 : 260,
662 2 : 260,
663 5 : 250,
664 10 : 210,
665 15 : 200,
666 25 : 190,
667 30 : 175,
668 40 : 162 }
669
670 @property
671 def derateType(self):
672 """Get the derate type for this type."""
673 return DERATE_BOEING
674
675#---------------------------------------------------------------------------------------
676
677class B736(Boeing737):
678 """Boeing 737-600 aircraft."""
679 def __init__(self, flight):
680 super(B736, self).__init__(flight)
681 self.dow = 38307
682 self.mtow = 58328
683 self.mlw = 54657
684 self.mzfw = 51482
685
686#---------------------------------------------------------------------------------------
687
688class B737(Boeing737):
689 """Boeing 737-700 aircraft."""
690 def __init__(self, flight):
691 super(B737, self).__init__(flight)
692 self.dow = 39250
693 self.mtow = 61410
694 self.mlw = 58059
695 self.mzfw = 54657
696
697#---------------------------------------------------------------------------------------
698
699class B738(Boeing737):
700 """Boeing 737-800 aircraft."""
701 def __init__(self, flight):
702 super(B738, self).__init__(flight)
703 self.dow = 42690
704 self.mtow = 71709
705 self.mlw = 65317
706 self.mzfw = 61688
707
708#---------------------------------------------------------------------------------------
709
710class B738Charter(B738):
711 """Boeing 737-800 aircraft used for charters."""
712 def __init__(self, flight):
713 super(B738Charter, self).__init__(flight)
714 self.mtow = 77791
715
716#---------------------------------------------------------------------------------------
717
718class Boeing737CL(Boeing737):
719 """Base class for the various aircraft in the Boeing 737 Classic family."""
720 def __init__(self, flight):
721 super(Boeing737CL, self).__init__(flight, minLandingFuel = 3500,
722 recommendedLandingFuel = None)
723
724#---------------------------------------------------------------------------------------
725
726class B733(Boeing737CL):
727 """Boeing 737-300 aircraft."""
728 def __init__(self, flight):
729 super(B733, self).__init__(flight)
730 self.dow = 32700
731 self.mtow = 62820
732 self.mlw = 51700
733 self.mzfw = 48410
734
735#---------------------------------------------------------------------------------------
736
737class B734(Boeing737CL):
738 """Boeing 737-400 aircraft."""
739 def __init__(self, flight):
740 super(B734, self).__init__(flight)
741 self.dow = 33200
742 self.mtow = 68050
743 self.mlw = 56200
744 self.mzfw = 53100
745
746#---------------------------------------------------------------------------------------
747
748class B735(Boeing737CL):
749 """Boeing 737-500 aircraft."""
750 def __init__(self, flight):
751 super(B735, self).__init__(flight)
752 self.dow = 31300
753 self.mtow = 60550
754 self.mlw = 50000
755 self.mzfw = 46700
756
757#---------------------------------------------------------------------------------------
758
759class DH8D(Aircraft):
760 """Bombardier Dash-8 Q400 aircraft.
761
762 The aircraft type-specific values in the aircraft state have the following
763 structure:
764 - fuel: left, right
765 - n1: left, right
766 - reverser: left, right."""
767
768 def __init__(self, flight):
769 super(DH8D, self).__init__(flight, minLandingFuel = 2000)
770 self.dow = 17185
771 self.mtow = 29257
772 self.mlw = 28009
773 self.mzfw = 25855
774 self.gearSpeedLimit = 215
775 self.flapSpeedLimits = { 5 : 200,
776 10 : 181,
777 15 : 172,
778 35 : 158 }
779
780#---------------------------------------------------------------------------------------
781
782class Boeing767(Aircraft):
783 """Base class for the various aircraft in the Boeing 767 family.
784
785 The aircraft type-specific values in the aircraft state have the following
786 structure:
787 - fuel: left, centre, right
788 - n1: left, right
789 - reverser: left, right"""
790
791 def __init__(self, flight, minLandingFuel = 9000):
792 super(Boeing767, self).__init__(flight, minLandingFuel = minLandingFuel)
793 self.gearSpeedLimit = 270
794 self.flapSpeedLimits = { 1 : 255,
795 5 : 235,
796 10 : 215,
797 20 : 215,
798 25 : 185,
799 30 : 175 }
800
801 @property
802 def derateType(self):
803 """Get the derate type for this type."""
804 return DERATE_BOEING
805
806#---------------------------------------------------------------------------------------
807
808class B762(Boeing767):
809 """Boeing 767-200 aircraft."""
810 def __init__(self, flight):
811 super(B762, self).__init__(flight)
812 self.dow = 84507
813 self.mtow = 175540
814 self.mlw = 126098
815 self.mzfw = 114758
816
817#---------------------------------------------------------------------------------------
818
819class B763(Boeing767):
820 """Boeing 767-300 aircraft."""
821 def __init__(self, flight):
822 super(B763, self).__init__(flight)
823 self.dow = 91311
824 self.mtow = 181436
825 self.mlw = 137892
826 self.mzfw = 130635
827
828#---------------------------------------------------------------------------------------
829
830class CRJ2(Aircraft):
831 """Bombardier CRJ-200 aircraft.
832
833 The aircraft type-specific values in the aircraft state have the following
834 structure:
835 - fuel: left, centre, right
836 - n1: left, right
837 - reverser: left, right."""
838 def __init__(self, flight):
839 super(CRJ2, self).__init__(flight, minLandingFuel = 1000)
840 self.dow = 14549
841 self.mtow = 22995
842 self.mlw = 21319
843 self.mzfw = 19958
844 self.gearSpeedLimit = 240
845 self.flapSpeedLimits = { 8 : 260,
846 20 : 220,
847 30 : 190,
848 45 : 175 }
849
850#---------------------------------------------------------------------------------------
851
852class F70(Aircraft):
853 """Fokker 70 aircraft.
854
855 The aircraft type-specific values in the aircraft state have the following
856 structure:
857 - fuel: left, centre, right
858 - n1: left, right
859 - reverser: left, right."""
860 def __init__(self, flight):
861 super(F70, self).__init__(flight, minLandingFuel = 1900)
862 self.dow = 24283
863 self.mtow = 38100 # FIXME: differentiate by registration number,
864 # MTOW of HA-LMF: 41955
865 self.mlw = 36740
866 self.mzfw = 32655
867 self.gearSpeedLimit = 200
868 self.flapSpeedLimits = { 8 : 250,
869 15 : 220,
870 25 : 220,
871 42 : 180 }
872 self.reverseMinSpeed = 50
873
874 @property
875 def derateType(self):
876 """Get the derate type for this type."""
877 return DERATE_EPR
878
879#---------------------------------------------------------------------------------------
880
881class DC3(Aircraft):
882 """Lisunov Li-2 (DC-3) aircraft.
883
884 The aircraft type-specific values in the aircraft state have the following
885 structure:
886 - fuel: left aux, left, right, right aux
887 - rpm: left, right
888 - reverser: left, right."""
889 def __init__(self, flight):
890 super(DC3, self).__init__(flight)
891 self.dow = 8627
892 self.mtow = 11884
893 self.mlw = 11793
894 self.mzfw = 11780
895 self.gearSpeedLimit = 148
896 self.flapSpeedLimits = { 15 : 135,
897 30 : 99,
898 45 : 97 }
899
900 def _checkFlightEnd(self, aircraftState):
901 """Check if the end of the flight has arrived.
902
903 This implementation checks the RPM values to be 0."""
904 for rpm in aircraftState.rpm:
905 if rpm>0: return False
906 return True
907
908#---------------------------------------------------------------------------------------
909
910class T134(Aircraft):
911 """Tupolev Tu-134 aircraft.
912
913 The aircraft type-specific values in the aircraft state have the following
914 structure:
915 - fuel: left tip, left aux, centre, right aux, right tip, external 1,
916 external 2
917 - n1: left, right
918 - reverser: left, right."""
919 def __init__(self, flight):
920 super(T134, self).__init__(flight, minLandingFuel = 3000)
921 self.dow = 29500
922 self.mtow = 49000
923 self.mlw = 43000
924 self.mzfw = 38500
925 self.gearSpeedLimit = 216
926 self.flapSpeedLimits = { 10 : 450,
927 20 : 400,
928 30 : 300 }
929 self.reverseMinSpeed = 50
930
931 self.needNoStrobeSpeedCheck = True
932
933 @property
934 def derateType(self):
935 """Get the derate type for this type."""
936 return DERATE_TUPOLEV
937
938 @property
939 def speedInKnots(self):
940 """Indicate if the speed is in knots."""
941 return False
942
943 def _appendLightsLoggers(self):
944 """Append the loggers needed for the lights."""
945 self._checkers.append(checks.AnticollisionLightsLogger())
946 self._checkers.append(checks.LandingLightsLogger())
947 self._checkers.append(checks.NavLightsLogger())
948
949 def _appendLightsCheckers(self):
950 """Append the checkers needed for the lights."""
951 self._checkers.append(checks.TupolevAntiCollisionLightsChecker())
952 self._checkers.append(checks.LandingLightsChecker())
953 self._checkers.append(checks.NavLightsChecker())
954
955#---------------------------------------------------------------------------------------
956
957class T154(Aircraft):
958 """Tupolev Tu-154 aircraft.
959
960 The aircraft type-specific values in the aircraft state have the following
961 structure:
962 - fuel: left aux, left, centre, centre 2, right, right aux
963 - n1: left, centre, right
964 - reverser: left, right"""
965 def __init__(self, flight):
966 super(T154, self).__init__(flight, minLandingFuel = 5000)
967 self.dow = 53259
968 self.mtow = 98000
969 self.mlw = 78000
970 self.mzfw = 72000
971 self.gearSpeedLimit = 216
972 self.flapSpeedLimits = { 15 : 227,
973 28 : 194,
974 45 : 162 }
975 self.reverseMinSpeed = 50
976
977 self.needNoStrobeSpeedCheck = True
978
979 @property
980 def speedInKnots(self):
981 """Indicate if the speed is in knots."""
982 return False
983
984 @property
985 def derateType(self):
986 """Get the derate type for this type."""
987 return DERATE_TUPOLEV
988
989 def _appendLightsLoggers(self):
990 """Append the loggers needed for the lights."""
991 self._checkers.append(checks.AnticollisionLightsLogger())
992 self._checkers.append(checks.LandingLightsLogger())
993 self._checkers.append(checks.NavLightsLogger())
994
995 def _appendLightsCheckers(self):
996 """Append the checkers needed for the lights."""
997 self._checkers.append(checks.AntiCollisionLightsChecker())
998 self._checkers.append(checks.LandingLightsChecker())
999 self._checkers.append(checks.NavLightsChecker())
1000
1001#---------------------------------------------------------------------------------------
1002
1003class YK40(Aircraft):
1004 """Yakovlev Yak-40 aircraft.
1005
1006 The aircraft type-specific values in the aircraft state have the following
1007 structure:
1008 - fuel: left, right
1009 - n1: left, right
1010 - reverser: left, right"""
1011 def __init__(self, flight):
1012 super(YK40, self).__init__(flight)
1013 self.dow = 9400
1014 self.mtow = 17200
1015 self.mlw = 16800
1016 self.mzfw = 12100
1017 self.gearSpeedLimit = 165
1018 self.flapSpeedLimits = { 20 : 165,
1019 35 : 135 }
1020
1021 self.needNoStrobeSpeedCheck = True
1022
1023 @property
1024 def speedInKnots(self):
1025 """Indicate if the speed is in knots."""
1026 return False
1027
1028 @property
1029 def derateType(self):
1030 """Get the derate type for this type."""
1031 return DERATE_TUPOLEV
1032
1033 def _appendLightsLoggers(self):
1034 """Append the loggers needed for the lights."""
1035 self._checkers.append(checks.AnticollisionLightsLogger())
1036 self._checkers.append(checks.LandingLightsLogger())
1037 self._checkers.append(checks.NavLightsLogger())
1038
1039 def _appendLightsCheckers(self):
1040 """Append the checkers needed for the lights."""
1041 self._checkers.append(checks.AntiCollisionLightsChecker())
1042 self._checkers.append(checks.LandingLightsChecker())
1043 self._checkers.append(checks.NavLightsChecker())
1044
1045#---------------------------------------------------------------------------------------
1046
1047class B462(Aircraft):
1048 """British Aerospace BAe-146 aircraft.
1049
1050 The aircraft type-specific values in the aircraft state have the following
1051 structure:
1052 - fuel: left, centre, right
1053 - n1: left outer, left inner, right inner, right outer
1054 - reverser: empty (the plane has no reversers)"""
1055 def __init__(self, flight):
1056 super(B462, self).__init__(flight)
1057 self.dow = 25706
1058 self.mtow = 43998
1059 self.mlw = 38599
1060 self.mzfw = 33792
1061 self.gearSpeedLimit = 210
1062 self.flapSpeedLimits = { 18 : 217,
1063 24 : 180,
1064 30 : 170,
1065 33 : 150 }
1066
1067 @property
1068 def derateType(self):
1069 """Get the derate type for this type."""
1070 return DERATE_B462
1071
1072#---------------------------------------------------------------------------------------
1073
1074mostFuelTanks = [const.FUELTANK_LEFT_TIP, const.FUELTANK_EXTERNAL1,
1075 const.FUELTANK_LEFT_AUX,
1076 const.FUELTANK_CENTRE,
1077 const.FUELTANK_RIGHT_AUX,
1078 const.FUELTANK_EXTERNAL2, const.FUELTANK_RIGHT_TIP]
1079
1080#---------------------------------------------------------------------------------------
1081
1082_classes = { const.AIRCRAFT_B736 : B736,
1083 const.AIRCRAFT_B737 : B737,
1084 const.AIRCRAFT_B738 : B738,
1085 const.AIRCRAFT_B738C : B738Charter,
1086 const.AIRCRAFT_B733 : B733,
1087 const.AIRCRAFT_B734 : B734,
1088 const.AIRCRAFT_B735 : B735,
1089 const.AIRCRAFT_DH8D : DH8D,
1090 const.AIRCRAFT_B762 : B762,
1091 const.AIRCRAFT_B763 : B763,
1092 const.AIRCRAFT_CRJ2 : CRJ2,
1093 const.AIRCRAFT_F70 : F70,
1094 const.AIRCRAFT_DC3 : DC3,
1095 const.AIRCRAFT_T134 : T134,
1096 const.AIRCRAFT_T154 : T154,
1097 const.AIRCRAFT_YK40 : YK40,
1098 const.AIRCRAFT_B462 : B462 }
1099
1100#---------------------------------------------------------------------------------------
1101
1102if __name__ == "__main__":
1103 value = SmoothedValue()
1104
1105 print "Adding 1, 12.0"
1106 value.add(1, 12.0)
1107 print value.get()
1108
1109 print "Adding 1, 15.0"
1110 value.add(1, 15.0)
1111 print value.get()
1112
1113 print "Adding 2, 18.0"
1114 value.add(2, 18.0)
1115 print value.get()
1116
1117 print "Adding 2, 20.0"
1118 value.add(2, 20.0)
1119 print value.get()
1120
1121 print "Adding 5, 22.0"
1122 value.add(5, 22.0)
1123 print value.get()
1124
1125 print "Adding 5, 25.0"
1126 value.add(5, 25.0)
1127 print value.get()
1128
1129 print "Adding 5, 29.0"
1130 value.add(5, 29.0)
1131 print value.get()
1132
1133 print "Adding 5, 21.0"
1134 value.add(5, 21.0)
1135 print value.get()
1136
1137 print "Adding 5, 26.0"
1138 value.add(5, 26.0)
1139 print value.get()
1140
1141 print "Adding 2, 30.0"
1142 value.add(2, 30.0)
1143 print value.get()
1144
1145 print "Adding 2, 55.0"
1146 value.add(2, 55.0)
1147 print value.get()
1148
1149#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.