source: src/mlx/acft.py@ 789:0be8d7e8f7c8

cef
Last change on this file since 789:0be8d7e8f7c8 was 709:44177c159b98, checked in by István Váradi <ivaradi@…>, 8 years ago

Added support for SimBrief data for each aircraft type (re #279).

File size: 46.9 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 B733(Boeing737CL):
792 """Boeing 737-300 aircraft."""
793 def __init__(self, flight):
794 super(B733, 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 B734(Boeing737CL):
805 """Boeing 737-400 aircraft."""
806 def __init__(self, flight):
807 super(B734, self).__init__(flight)
808 self.dow = 33200
809 self.mtow = 68050
810 self.mlw = 56200
811 self.mzfw = 53100
812 self.maxTakeOffPitch = 11.4
813 self.maxTouchDownPitch = 10
814
815#---------------------------------------------------------------------------------------
816
817class B735(Boeing737CL):
818 """Boeing 737-500 aircraft."""
819 def __init__(self, flight):
820 super(B735, self).__init__(flight)
821 self.dow = 31300
822 self.mtow = 60550
823 self.mlw = 50000
824 self.mzfw = 46700
825 self.maxTakeOffPitch = 14.7
826 self.maxTouchDownPitch = 13.2
827
828#---------------------------------------------------------------------------------------
829
830class DH8D(Aircraft):
831 """Bombardier Dash-8 Q400 aircraft.
832
833 The aircraft type-specific values in the aircraft state have the following
834 structure:
835 - fuel: left, right
836 - n1: left, right
837 - reverser: left, right."""
838
839 def __init__(self, flight):
840 super(DH8D, self).__init__(flight, minLandingFuel = 2000)
841 self.dow = 17185
842 self.mtow = 29257
843 self.mlw = 28009
844 self.mzfw = 25855
845 self.gearSpeedLimit = 215
846 self.flapSpeedLimits = { 5 : 200,
847 10 : 181,
848 15 : 172,
849 35 : 158 }
850 self.maxTakeOffPitch = 8.0
851 self.maxTouchDownPitch = 7.0
852 self.simBriefData = SimBriefData(["I-900", "II-900", "III-900",
853 "I-850", "II-850", "III-850"],
854 ["MCR", "ISC", "LRC", "HSC"],
855 ["I-850", "II-850", "III-850"])
856
857#---------------------------------------------------------------------------------------
858
859class Boeing767(Aircraft):
860 """Base class for the various aircraft in the Boeing 767 family.
861
862 The aircraft type-specific values in the aircraft state have the following
863 structure:
864 - fuel: left, centre, right
865 - n1: left, right
866 - reverser: left, right"""
867
868 def __init__(self, flight, minLandingFuel = 7000):
869 super(Boeing767, self).__init__(flight, minLandingFuel = minLandingFuel)
870 self.gearSpeedLimit = 270
871 self.flapSpeedLimits = { 1 : 255,
872 5 : 235,
873 10 : 215,
874 20 : 215,
875 25 : 185,
876 30 : 175 }
877
878 @property
879 def derateType(self):
880 """Get the derate type for this type."""
881 return DERATE_BOEING
882
883#---------------------------------------------------------------------------------------
884
885class B762(Boeing767):
886 """Boeing 767-200 aircraft."""
887 def __init__(self, flight):
888 super(B762, self).__init__(flight)
889 self.dow = 84507
890 self.mtow = 175540
891 self.mlw = 126098
892 self.mzfw = 114758
893 self.maxTakeOffPitch = 13.1
894 self.maxTouchDownPitch = 11.6
895
896#---------------------------------------------------------------------------------------
897
898class B763(Boeing767):
899 """Boeing 767-300 aircraft."""
900 def __init__(self, flight):
901 super(B763, self).__init__(flight)
902 self.dow = 91311
903 self.mtow = 181436
904 self.mlw = 137892
905 self.mzfw = 130635
906 self.maxTakeOffPitch = 9.6
907 self.maxTouchDownPitch = 8.1
908 self.simBriefData = SimBriefData(["250/290/78"],
909 ["CI", "M76", "M78", "M80", "M82", "M84", "LRC"],
910 ["78/290/250"])
911
912#---------------------------------------------------------------------------------------
913
914class CRJ2(Aircraft):
915 """Bombardier CRJ-200 aircraft.
916
917 The aircraft type-specific values in the aircraft state have the following
918 structure:
919 - fuel: left, centre, right
920 - n1: left, right
921 - reverser: left, right."""
922 def __init__(self, flight):
923 super(CRJ2, self).__init__(flight, minLandingFuel = 1000)
924 self.dow = 14549
925 self.mtow = 22995
926 self.mlw = 21319
927 self.mzfw = 19958
928 self.gearSpeedLimit = 240
929 self.flapSpeedLimits = { 8 : 260,
930 20 : 220,
931 30 : 190,
932 45 : 175 }
933 self.maxTakeOffPitch = 18.0
934 self.maxTouchDownPitch = 18.0
935 self.simBriefData = SimBriefData(["250/70", "290/74"],
936 ["CI", "LRC", "M70", "M72", "M74", "M77", "M80"],
937 ["74/290/250", "77/320/250"])
938
939#---------------------------------------------------------------------------------------
940
941class F70(Aircraft):
942 """Fokker 70 aircraft.
943
944 The aircraft type-specific values in the aircraft state have the following
945 structure:
946 - fuel: left, centre, right
947 - n1: left, right
948 - reverser: left, right."""
949 def __init__(self, flight):
950 super(F70, self).__init__(flight, minLandingFuel = 1900)
951 self.dow = 24283
952 self.mtow = 38100 # FIXME: differentiate by registration number,
953 # MTOW of HA-LMF: 41955
954 self.mlw = 36740
955 self.mzfw = 32655
956 self.gearSpeedLimit = 200
957 self.flapSpeedLimits = { 8 : 250,
958 15 : 220,
959 25 : 220,
960 42 : 180 }
961 self.reverseMinSpeed = 50
962 self.maxTakeOffPitch = 16.0
963 self.maxTouchDownPitch = 16.0
964
965 @property
966 def derateType(self):
967 """Get the derate type for this type."""
968 return DERATE_EPR
969
970#---------------------------------------------------------------------------------------
971
972class DC3(Aircraft):
973 """Lisunov Li-2 (DC-3) aircraft.
974
975 The aircraft type-specific values in the aircraft state have the following
976 structure:
977 - fuel: left aux, left, right, right aux
978 - rpm: left, right
979 - reverser: left, right."""
980 def __init__(self, flight):
981 super(DC3, self).__init__(flight)
982 self.dow = 8627
983 self.mtow = 11884
984 self.mlw = 11793
985 self.mzfw = 11780
986 self.gearSpeedLimit = 148
987 self.flapSpeedLimits = { 15 : 135,
988 30 : 99,
989 45 : 97 }
990
991 def _checkFlightEnd(self, aircraftState):
992 """Check if the end of the flight has arrived.
993
994 This implementation checks the RPM values to be 0."""
995 for rpm in aircraftState.rpm:
996 if rpm>0: return False
997 return True
998
999#---------------------------------------------------------------------------------------
1000
1001class T134(Aircraft):
1002 """Tupolev Tu-134 aircraft.
1003
1004 The aircraft type-specific values in the aircraft state have the following
1005 structure:
1006 - fuel: left tip, left aux, centre, right aux, right tip, external 1,
1007 external 2
1008 - n1: left, right
1009 - reverser: left, right."""
1010 def __init__(self, flight):
1011 super(T134, self).__init__(flight, minLandingFuel = 3000)
1012 self.dow = 29500
1013 self.mtow = 49000
1014 self.mlw = 43000
1015 self.mzfw = 38500
1016 self.gearSpeedLimit = 216
1017 self.flapSpeedLimits = { 10 : 450,
1018 20 : 400,
1019 30 : 300 }
1020 self.reverseMinSpeed = 50
1021
1022 self.hasStrobeLight = False
1023
1024 self.maxTakeOffPitch = 16.0
1025 self.maxTouchDownPitch = 16.0
1026
1027 @property
1028 def derateType(self):
1029 """Get the derate type for this type."""
1030 return DERATE_TUPOLEV
1031
1032 @property
1033 def speedInKnots(self):
1034 """Indicate if the speed is in knots."""
1035 return False
1036
1037 @property
1038 def aglInFeet(self):
1039 """Indicate if AGL altituedes are in feet."""
1040 return False
1041
1042 def _appendLightsLoggers(self):
1043 """Append the loggers needed for the lights."""
1044 self._checkers.append(checks.AnticollisionLightsLogger())
1045 self._checkers.append(checks.LandingLightsLogger())
1046 self._checkers.append(checks.NavLightsLogger())
1047
1048 def _appendLightsCheckers(self):
1049 """Append the checkers needed for the lights."""
1050 self._checkers.append(checks.TupolevAntiCollisionLightsChecker())
1051 self._checkers.append(checks.TupolevLandingLightsChecker())
1052 self._checkers.append(checks.LandingLightsChecker())
1053 self._checkers.append(checks.NavLightsChecker())
1054
1055#---------------------------------------------------------------------------------------
1056
1057class T154(Aircraft):
1058 """Tupolev Tu-154 aircraft.
1059
1060 The aircraft type-specific values in the aircraft state have the following
1061 structure:
1062 - fuel: left aux, left, centre, centre 2, right, right aux
1063 - n1: left, centre, right
1064 - reverser: left, right"""
1065 def __init__(self, flight):
1066 super(T154, self).__init__(flight, minLandingFuel = 5000)
1067 self.dow = 53259
1068 self.mtow = 98000
1069 self.mlw = 78000
1070 self.mzfw = 72000
1071 self.gearSpeedLimit = 216
1072 self.flapSpeedLimits = { 15 : 227,
1073 28 : 194,
1074 45 : 162 }
1075 self.reverseMinSpeed = 50
1076
1077 self.hasStrobeLight = False
1078
1079 self.maxTakeOffPitch = 16.0
1080 self.maxTouchDownPitch = 16.0
1081 self.simBriefData = SimBriefData(["AUTO"],
1082 ["300/M80"],
1083 ["AUTO"])
1084
1085 @property
1086 def speedInKnots(self):
1087 """Indicate if the speed is in knots."""
1088 return False
1089
1090 @property
1091 def aglInFeet(self):
1092 """Indicate if AGL altituedes are in feet."""
1093 return False
1094
1095 @property
1096 def derateType(self):
1097 """Get the derate type for this type."""
1098 return DERATE_TUPOLEV
1099
1100 def _appendLightsLoggers(self):
1101 """Append the loggers needed for the lights."""
1102 self._checkers.append(checks.AnticollisionLightsLogger())
1103 self._checkers.append(checks.LandingLightsLogger())
1104 self._checkers.append(checks.NavLightsLogger())
1105
1106 def _appendLightsCheckers(self):
1107 """Append the checkers needed for the lights."""
1108 self._checkers.append(checks.AntiCollisionLightsChecker())
1109 self._checkers.append(checks.TupolevLandingLightsChecker())
1110 self._checkers.append(checks.LandingLightsChecker())
1111 self._checkers.append(checks.NavLightsChecker())
1112
1113#---------------------------------------------------------------------------------------
1114
1115class YK40(Aircraft):
1116 """Yakovlev Yak-40 aircraft.
1117
1118 The aircraft type-specific values in the aircraft state have the following
1119 structure:
1120 - fuel: left, right
1121 - n1: left, right
1122 - reverser: left, right"""
1123 def __init__(self, flight):
1124 super(YK40, self).__init__(flight)
1125 self.dow = 9400
1126 self.mtow = 17200
1127 self.mlw = 16800
1128 self.mzfw = 12100
1129 self.gearSpeedLimit = 165
1130 self.flapSpeedLimits = { 20 : 165,
1131 35 : 135 }
1132
1133 self.hasStrobeLight = False
1134
1135 @property
1136 def speedInKnots(self):
1137 """Indicate if the speed is in knots."""
1138 return False
1139
1140 @property
1141 def aglInFeet(self):
1142 """Indicate if AGL altituedes are in feet."""
1143 return False
1144
1145 @property
1146 def derateType(self):
1147 """Get the derate type for this type."""
1148 return DERATE_TUPOLEV
1149
1150 def _appendLightsLoggers(self):
1151 """Append the loggers needed for the lights."""
1152 self._checkers.append(checks.AnticollisionLightsLogger())
1153 self._checkers.append(checks.LandingLightsLogger())
1154 self._checkers.append(checks.NavLightsLogger())
1155
1156 def _appendLightsCheckers(self):
1157 """Append the checkers needed for the lights."""
1158 self._checkers.append(checks.AntiCollisionLightsChecker())
1159 self._checkers.append(checks.LandingLightsChecker())
1160 self._checkers.append(checks.NavLightsChecker())
1161
1162#---------------------------------------------------------------------------------------
1163
1164class B462(Aircraft):
1165 """British Aerospace BAe-146 aircraft.
1166
1167 The aircraft type-specific values in the aircraft state have the following
1168 structure:
1169 - fuel: left, centre, right
1170 - n1: left outer, left inner, right inner, right outer
1171 - reverser: empty (the plane has no reversers)"""
1172 def __init__(self, flight):
1173 super(B462, self).__init__(flight)
1174 self.dow = 25706
1175 self.mtow = 43998
1176 self.mlw = 38599
1177 self.mzfw = 33792
1178 self.gearSpeedLimit = 210
1179 self.flapSpeedLimits = { 18 : 217,
1180 24 : 180,
1181 30 : 170,
1182 33 : 150 }
1183
1184 @property
1185 def derateType(self):
1186 """Get the derate type for this type."""
1187 return DERATE_B462
1188
1189#---------------------------------------------------------------------------------------
1190
1191mostFuelTanks = [const.FUELTANK_LEFT_TIP, const.FUELTANK_EXTERNAL1,
1192 const.FUELTANK_LEFT_AUX,
1193 const.FUELTANK_CENTRE,
1194 const.FUELTANK_RIGHT_AUX,
1195 const.FUELTANK_EXTERNAL2, const.FUELTANK_RIGHT_TIP]
1196
1197#---------------------------------------------------------------------------------------
1198
1199_classes = { const.AIRCRAFT_B736 : B736,
1200 const.AIRCRAFT_B737 : B737,
1201 const.AIRCRAFT_B738 : B738,
1202 const.AIRCRAFT_B738C : B738Charter,
1203 const.AIRCRAFT_B733 : B733,
1204 const.AIRCRAFT_B734 : B734,
1205 const.AIRCRAFT_B735 : B735,
1206 const.AIRCRAFT_DH8D : DH8D,
1207 const.AIRCRAFT_B762 : B762,
1208 const.AIRCRAFT_B763 : B763,
1209 const.AIRCRAFT_CRJ2 : CRJ2,
1210 const.AIRCRAFT_F70 : F70,
1211 const.AIRCRAFT_DC3 : DC3,
1212 const.AIRCRAFT_T134 : T134,
1213 const.AIRCRAFT_T154 : T154,
1214 const.AIRCRAFT_YK40 : YK40,
1215 const.AIRCRAFT_B462 : B462 }
1216
1217#---------------------------------------------------------------------------------------
1218
1219if __name__ == "__main__":
1220 value = SmoothedValue()
1221
1222 print "Adding 1, 12.0"
1223 value.add(1, 12.0)
1224 print value.get()
1225
1226 print "Adding 1, 15.0"
1227 value.add(1, 15.0)
1228 print value.get()
1229
1230 print "Adding 2, 18.0"
1231 value.add(2, 18.0)
1232 print value.get()
1233
1234 print "Adding 2, 20.0"
1235 value.add(2, 20.0)
1236 print value.get()
1237
1238 print "Adding 5, 22.0"
1239 value.add(5, 22.0)
1240 print value.get()
1241
1242 print "Adding 5, 25.0"
1243 value.add(5, 25.0)
1244 print value.get()
1245
1246 print "Adding 5, 29.0"
1247 value.add(5, 29.0)
1248 print value.get()
1249
1250 print "Adding 5, 21.0"
1251 value.add(5, 21.0)
1252 print value.get()
1253
1254 print "Adding 5, 26.0"
1255 value.add(5, 26.0)
1256 print value.get()
1257
1258 print "Adding 2, 30.0"
1259 value.add(2, 30.0)
1260 print value.get()
1261
1262 print "Adding 2, 55.0"
1263 value.add(2, 55.0)
1264 print value.get()
1265
1266#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.