source: src/mlx/acft.py@ 574:92975e9a1637

Last change on this file since 574:92975e9a1637 was 539:750f64278751, checked in by István Váradi <ivaradi@…>, 11 years ago

The no-strobe speed checker also works for models without strobe and transponder (re #211)

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