source: src/mlx/acft.py@ 634:b695e9445e24

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

A go-around is logged as a fault with no score and an explanation is required (re #266)

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