source: src/mlx/acft.py@ 798:d2fd22159c9f

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

The booked flight is passed to the aircraft created.

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