source: src/mlx/acft.py@ 623:33edcb39a260

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

The strobeless speed checker is used for all aircraft to detect the takeoff stage (re #260)

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