source: src/mlx/acft.py@ 622:21bb632b0961

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

The overspeed and stall conditions are logged with the winds (re #259)

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