source: src/mlx/acft.py@ 624:e501c5d66286

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

It is a fault if less than 10 minutes pass between an RTO and the next takeoff attempt (re #261)

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