source: src/mlx/acft.py@ 790:e69a08004f40

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

Added support for the Boeing 737-200 aircraft type (weight data is still missing, re #302)

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