source: src/mlx/acft.py@ 510:8590198176ae

Last change on this file since 510:8590198176ae was 447:3c4f141b2d42, checked in by István Váradi <ivaradi@…>, 12 years ago

Overspeed is tolerated for at least 30 seconds now (re #184)

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