source: src/mlx/acft.py@ 601:16ff9fcc527c

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

The initial climb speed is logged (re #246)

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