source: src/mlx/acft.py@ 391:0f2e90eae832

Last change on this file since 391:0f2e90eae832 was 391:0f2e90eae832, checked in by István Váradi <ivaradi@…>, 11 years ago

Added support for logging the state of the anti-ice system (re #159)

File size: 39.1 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 = 5.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.logger.message(aircraftState.timestamp,
292 "Wind %03.0f/%.0f" % \
293 (aircraftState.windDirection,
294 aircraftState.windSpeed))
295 self._logRadios(aircraftState)
296 self._logV1R2(aircraftState)
297 self._logDerate(aircraftState)
298 self._logTakeoffAntiIce(aircraftState)
299 elif newStage==const.STAGE_DESCENT or newStage==const.STAGE_LANDING:
300 self._logRadios(aircraftState)
301 elif newStage==const.STAGE_TAXIAFTERLAND:
302 flight = self._flight
303 bookedFlight = flight.bookedFlight
304 config = flight.config
305 if config.onlineGateSystem and \
306 flight.loggedIn and \
307 not flight.entranceExam and \
308 bookedFlight.arrivalICAO=="LHBP" and \
309 config.isMessageTypeFS(const.MESSAGETYPE_GATE_SYSTEM):
310 self._flight.getFleet(callback = self._fleetRetrieved,
311 force = True)
312 self.logger.message(aircraftState.timestamp, "Flight time end")
313 self._flight.logFuel(aircraftState)
314 self.logger.message(aircraftState.timestamp,
315 "Landing weight: %.0f kg, MLW: %.0f" % \
316 (aircraftState.grossWeight, self.mlw))
317 self.logger.message(aircraftState.timestamp,
318 "Vertical speed range: %.0f..%.0f feet/min" % \
319 (self._minVS, self._maxVS))
320 # elif newStage==const.STAGE_PARKING:
321 # self.logger.message(aircraftState.timestamp, "Block time end")
322 elif newStage==const.STAGE_END:
323 flightLength = self._flight.flightTimeEnd - self._flight.flightTimeStart
324 self.logger.message(aircraftState.timestamp, "Block time end")
325 self.logger.message(aircraftState.timestamp,
326 "Flight time: " +
327 util.getTimeIntervalString(flightLength))
328 self.logger.message(aircraftState.timestamp,
329 "Flown distance: %.2f NM" % \
330 (self._flight.flownDistance,))
331 blockLength = self._flight.blockTimeEnd - self._flight.blockTimeStart
332 self.logger.message(aircraftState.timestamp,
333 "Block time: " +
334 util.getTimeIntervalString(blockLength))
335
336 def prepareFlare(self):
337 """Called when it is detected that we will soon flare.
338
339 On the first call, it should start monitoring some parameters more
340 closely to determine flare time."""
341 self.flight.simulator.startFlare()
342
343 def flareStarted(self, windSpeed, windDirection, visibility,
344 flareStart, flareStartFS):
345 """Called when the flare has started."""
346 self.logger.message(self._aircraftState.timestamp, "The flare has begun")
347 self.logger.message(self._aircraftState.timestamp,
348 "Wind %03.0f/%.0f" % \
349 (windDirection, windSpeed))
350 self.logger.message(self._aircraftState.timestamp,
351 "Visibility: %.0f metres" % (visibility,))
352 self.logger.message(self._aircraftState.timestamp,
353 "Altimeter setting: %.0f hPa" % \
354 (self._aircraftState.altimeter,))
355 self._logVRef()
356 self._logLandingAntiIce(self._aircraftState)
357 self.flight.flareStarted(flareStart, flareStartFS)
358 fs.sendMessage(const.MESSAGETYPE_INFORMATION, "Flare-time", 3)
359
360 def flareFinished(self, flareEnd, flareEndFS, tdRate, tdRateCalculatedByFS,
361 ias, pitch, bank, heading):
362 """Called when the flare has finished."""
363 (flareTimeFromFS, flareTime) = self.flight.flareFinished(flareEnd,
364 flareEndFS,
365 tdRate)
366 self.logger.message(self._aircraftState.timestamp,
367 "Flaretime: %.3f (from %s)" % \
368 (flareTime,
369 "the simulator" if flareTimeFromFS else "real time",))
370 self.logger.message(self._aircraftState.timestamp,
371 "Touchdown rate: %.0f feet/min" % (tdRate,))
372 self.logger.message(self._aircraftState.timestamp,
373 "Touchdown rate was calculated by the %s" % \
374 ("simulator" if tdRateCalculatedByFS else "logger",))
375 flight = self._flight
376 self.logger.message(self._aircraftState.timestamp,
377 "Touchdown speed: %.0f %s" % \
378 (flight.speedFromKnots(ias),
379 flight.getEnglishSpeedUnit()))
380 self.logger.message(self._aircraftState.timestamp,
381 "Touchdown pitch: %.1f degrees" % (pitch,))
382 self.logger.message(self._aircraftState.timestamp,
383 "Touchdown bank: %.1f degrees" % (bank,))
384 self.logger.message(self._aircraftState.timestamp,
385 "Touchdown heading: %03.0f degrees" % (heading,))
386 self.logger.message(self._aircraftState.timestamp,
387 "CG: %.1f%%" % \
388 (self._aircraftState.cog*100.0,))
389
390 def cancelFlare(self):
391 """Cancel flare, if it has started."""
392 self.flight.simulator.cancelFlare()
393
394 def checkFlightEnd(self, aircraftState):
395 """Check if the end of the flight has arrived.
396
397 This default implementation checks the N1 values, but for
398 piston-powered aircraft you need to check the RPMs."""
399 for n1 in aircraftState.n1:
400 if n1>=0.5: return False
401 return True
402
403 def updateV1R2(self):
404 """Update the V1, Vr and V2 values from the flight, if the these values
405 have already been logged."""
406 if self._v1r2LineIndex is not None:
407 self._logV1R2()
408
409 def updateDerate(self):
410 """Update the derate value from the flight, if the these values
411 have already been logged."""
412 if self._derateLineID is not None:
413 self._logDerate()
414
415 def updateTakeoffAntiIce(self):
416 """Update the take-off anti-ice setting."""
417 if self._takeoffAntiIceLineID is not None:
418 self._logTakeoffAntiIce()
419
420 def _appendLightsLoggers(self):
421 """Append the loggers needed for the lights.
422
423 This default implementation adds the loggers for the anti-collision
424 lights, the landing lights, the strobe lights and the NAV lights."""
425 self._checkers.append(checks.AnticollisionLightsLogger())
426 self._checkers.append(checks.LandingLightsLogger())
427 self._checkers.append(checks.StrobeLightsLogger())
428 self._checkers.append(checks.NavLightsLogger())
429
430 def _appendLightsCheckers(self):
431 """Append the checkers needed for the lights.
432
433 This default implementation adds the checkers for the anti-collision
434 lights, the landing lights, the strobe lights and the NAV lights."""
435 self._checkers.append(checks.AntiCollisionLightsChecker())
436 self._checkers.append(checks.LandingLightsChecker())
437 self._checkers.append(checks.NavLightsChecker())
438 self._checkers.append(checks.StrobeLightsChecker())
439
440 def _appendSpeedChecker(self):
441 """Append the speed checker.
442
443 This default implementation used the SpeedChecker."""
444 self._checkers.append(checks.SpeedChecker())
445
446 def _speedToLog(self, speed):
447 """Convert the given speed (being either None or expressed in the
448 flight's speed unit into a string."""
449 if speed is None:
450 return "-"
451 else:
452 return str(speed) + " " + self._flight.getEnglishSpeedUnit()
453
454 def _logV1R2(self, state = None):
455 """Log the V1, Vr and V2 value either newly, or by updating the
456 corresponding line."""
457 message = "Speeds calculated by the pilot: V1: %s, VR: %s, V2: %s" % \
458 (self._speedToLog(self._flight.v1),
459 self._speedToLog(self._flight.vr),
460 self._speedToLog(self._flight.v2))
461
462 if self._v1r2LineIndex is None:
463 if state is None:
464 state = self._aircraftState
465 self._v1r2LineIndex = \
466 self.logger.message(state.timestamp, message)
467 else:
468 self.logger.updateLine(self._v1r2LineIndex, message)
469
470 def _logDerate(self, state = None):
471 """Log the derate values either newly or by updating the corresponding
472 line."""
473 derateTemplate = self.derateTemplate
474 if derateTemplate is None:
475 return
476
477 derate = self._flight.derate
478 message = derateTemplate % ("-" if derate is None else derate)
479 if self._derateLineID is None:
480 if state is None:
481 state = self._aircraftState
482 self._derateLineID = \
483 self.logger.message(state.timestamp, message)
484 else:
485 self.logger.updateLine(self._derateLineID, message)
486
487 def _logTakeoffAntiIce(self, state = None):
488 """Log the take-off anti-ice setting either newly or by updating the
489 corresponding line."""
490 antiIceOn = self._flight.takeoffAntiIceOn
491 if state is not None:
492 antiIceOn = antiIceOn or state.antiIceOn is True
493 self._flight.takeoffAntiIceOn = antiIceOn
494
495 message = "Anti-ice was turned %s" % \
496 ("ON" if antiIceOn else "OFF")
497
498 if self._takeoffAntiIceLineID is None:
499 if state is None:
500 state = self._aircraftState
501 self._takeoffAntiIceLineID = \
502 self.logger.message(state.timestamp, message)
503 else:
504 self.logger.updateLine(self._takeoffAntiIceLineID, message)
505
506 def updateVRef(self):
507 """Update the Vref value from the flight, if the Vref value has already
508 been logged."""
509 if self._vrefLineIndex is not None:
510 self._logVRef()
511
512 def _logVRef(self):
513 """Log the Vref value either newly, or by updating the corresponding
514 line."""
515 message = "VRef speed calculated by the pilot: %s" % \
516 (self._speedToLog(self._flight.vref),)
517 if self._vrefLineIndex is None:
518 self._vrefLineIndex = \
519 self.logger.message(self._aircraftState.timestamp, message)
520 else:
521 self.logger.updateLine(self._vrefLineIndex, message)
522
523 def updateLandingAntiIce(self):
524 """Update the landing anti-ice setting."""
525 if self._landingAntiIceLineID is not None:
526 self._logLandingAntiIce()
527
528 def _logLandingAntiIce(self, state = None):
529 """Log the landing anti-ice setting either newly or by updating the
530 corresponding line."""
531 antiIceOn = self._flight.landingAntiIceOn
532 if state is not None:
533 antiIceOn = antiIceOn or state.antiIceOn is True
534 self._flight.landingAntiIceOn = antiIceOn
535
536 message = "Anti-ice was turned %s" % \
537 ("ON" if antiIceOn else "OFF")
538
539 if self._landingAntiIceLineID is None:
540 if state is None:
541 state = self._aircraftState
542 self._landingAntiIceLineID = \
543 self.logger.message(state.timestamp, message)
544 else:
545 self.logger.updateLine(self._landingAntiIceLineID, message)
546
547 def _fleetRetrieved(self, fleet):
548 """Callback for the fleet retrieval result."""
549 if fleet is not None:
550 gateList = ""
551 occupiedGateNumbers = fleet.getOccupiedGateNumbers()
552 for gateNumber in const.lhbpGateNumbers:
553 if gateNumber not in occupiedGateNumbers:
554 if gateList: gateList += ", "
555 gateList += gateNumber
556 fs.sendMessage(const.MESSAGETYPE_GATE_SYSTEM,
557 "Free gates: " + gateList, 20)
558
559
560 def _logRadios(self, aircraftState):
561 """Log the radios from the given aircraft state."""
562 flight = self._flight
563 logger = flight.logger
564
565 self._ilsLogger.forceLog(flight, logger, aircraftState)
566 self._nav1Logger.forceLog(flight, logger, aircraftState)
567 self._nav2Logger.forceLog(flight, logger, aircraftState)
568 self._adf1Logger.forceLog(flight, logger, aircraftState)
569 self._adf2Logger.forceLog(flight, logger, aircraftState)
570
571 def _logNameAndModel(self, timestamp):
572 """Log the aircraft's name and model with taking the timestamp from the
573 given state."""
574 self._flight.logger.message(timestamp,
575 "Aircraft: name='%s', model='%s'" % \
576 (self._name, self._modelName))
577
578#---------------------------------------------------------------------------------------
579
580class Boeing737(Aircraft):
581 """Base class for the various aircraft in the Boeing 737 family.
582
583 The aircraft type-specific values in the aircraft state have the following
584 structure:
585 - fuel: left, centre, right
586 - n1: left, right
587 - reverser: left, right"""
588 def __init__(self, flight):
589 super(Boeing737, self).__init__(flight)
590 self._checkers.append(checks.ThrustChecker())
591
592 self.gearSpeedLimit = 270
593 self.flapSpeedLimits = { 1 : 260,
594 2 : 260,
595 5 : 250,
596 10 : 210,
597 15 : 200,
598 25 : 190,
599 30 : 175,
600 40 : 162 }
601
602 @property
603 def derateLabels(self):
604 """Get the derate strings for this type."""
605 return (xstr("takeoff_derate_boeing"), "%")
606
607 @property
608 def derateTemplate(self):
609 """Get the derate template for this aicraft type."""
610 return "Derate calculated by the pilot: %s %%"
611
612 # def _appendSpeedChecker(self):
613 # """Append the NoStrobeSpeedChecker to the checkers.
614
615 # FOR TESTING ONLY, THIS FUNCTION SHOULD NORMALLY BE COMMENTED OUT!!!"""
616 # self._checkers.append(checks.NoStrobeSpeedChecker())
617
618#---------------------------------------------------------------------------------------
619
620class B736(Boeing737):
621 """Boeing 737-600 aircraft."""
622 def __init__(self, flight):
623 super(B736, self).__init__(flight)
624 self.dow = 38307
625 self.mtow = 58328
626 self.mlw = 54657
627 self.mzfw = 51482
628
629#---------------------------------------------------------------------------------------
630
631class B737(Boeing737):
632 """Boeing 737-700 aircraft."""
633 def __init__(self, flight):
634 super(B737, self).__init__(flight)
635 self.dow = 39250
636 self.mtow = 61410
637 self.mlw = 58059
638 self.mzfw = 54657
639
640#---------------------------------------------------------------------------------------
641
642class B738(Boeing737):
643 """Boeing 737-800 aircraft."""
644 def __init__(self, flight):
645 super(B738, self).__init__(flight)
646 self.dow = 42690
647 self.mtow = 71709
648 self.mlw = 65317
649 self.mzfw = 61688
650
651#---------------------------------------------------------------------------------------
652
653class B738Charter(B738):
654 """Boeing 737-800 aircraft used for charters."""
655 def __init__(self, flight):
656 super(B738Charter, self).__init__(flight)
657 self.mtow = 77791
658
659#---------------------------------------------------------------------------------------
660
661class B733(Boeing737):
662 """Boeing 737-300 aircraft."""
663 def __init__(self, flight):
664 super(B733, self).__init__(flight)
665 self.dow = 32700
666 self.mtow = 62820
667 self.mlw = 51700
668 self.mzfw = 48410
669
670#---------------------------------------------------------------------------------------
671
672class B734(Boeing737):
673 """Boeing 737-400 aircraft."""
674 def __init__(self, flight):
675 super(B734, self).__init__(flight)
676 self.dow = 33200
677 self.mtow = 68050
678 self.mlw = 56200
679 self.mzfw = 53100
680
681#---------------------------------------------------------------------------------------
682
683class B735(Boeing737):
684 """Boeing 737-500 aircraft."""
685 def __init__(self, flight):
686 super(B735, self).__init__(flight)
687 self.dow = 31300
688 self.mtow = 60550
689 self.mlw = 50000
690 self.mzfw = 46700
691
692#---------------------------------------------------------------------------------------
693
694class DH8D(Aircraft):
695 """Bombardier Dash-8 Q400 aircraft.
696
697 The aircraft type-specific values in the aircraft state have the following
698 structure:
699 - fuel: left, right
700 - n1: left, right
701 - reverser: left, right."""
702
703 def __init__(self, flight):
704 super(DH8D, self).__init__(flight)
705 self.dow = 17185
706 self.mtow = 29257
707 self.mlw = 28009
708 self.mzfw = 25855
709 self.gearSpeedLimit = 215
710 self.flapSpeedLimits = { 5 : 200,
711 10 : 181,
712 15 : 172,
713 35 : 158 }
714
715#---------------------------------------------------------------------------------------
716
717class Boeing767(Aircraft):
718 """Base class for the various aircraft in the Boeing 767 family.
719
720 The aircraft type-specific values in the aircraft state have the following
721 structure:
722 - fuel: left, centre, right
723 - n1: left, right
724 - reverser: left, right"""
725
726 def __init__(self, flight):
727 super(Boeing767, self).__init__(flight)
728 self._checkers.append(checks.ThrustChecker())
729 self.gearSpeedLimit = 270
730 self.flapSpeedLimits = { 1 : 255,
731 5 : 235,
732 10 : 215,
733 20 : 215,
734 25 : 185,
735 30 : 175 }
736
737 @property
738 def derateLabels(self):
739 """Get the derate strings for this type."""
740 return (xstr("takeoff_derate_boeing"), "%")
741
742 @property
743 def derateTemplate(self):
744 """Get the derate template for this aicraft type."""
745 return "Derate calculated by the pilot: %s %%"
746
747#---------------------------------------------------------------------------------------
748
749class B762(Boeing767):
750 """Boeing 767-200 aircraft."""
751 def __init__(self, flight):
752 super(B762, self).__init__(flight)
753 self.dow = 84507
754 self.mtow = 175540
755 self.mlw = 126098
756 self.mzfw = 114758
757
758#---------------------------------------------------------------------------------------
759
760class B763(Boeing767):
761 """Boeing 767-300 aircraft."""
762 def __init__(self, flight):
763 super(B763, self).__init__(flight)
764 self.dow = 91311
765 self.mtow = 181436
766 self.mlw = 137892
767 self.mzfw = 130635
768
769#---------------------------------------------------------------------------------------
770
771class CRJ2(Aircraft):
772 """Bombardier CRJ-200 aircraft.
773
774 The aircraft type-specific values in the aircraft state have the following
775 structure:
776 - fuel: left, centre, right
777 - n1: left, right
778 - reverser: left, right."""
779 def __init__(self, flight):
780 super(CRJ2, self).__init__(flight)
781 self._checkers.append(checks.ThrustChecker())
782 self.dow = 14549
783 self.mtow = 22995
784 self.mlw = 21319
785 self.mzfw = 19958
786 self.gearSpeedLimit = 240
787 self.flapSpeedLimits = { 8 : 260,
788 20 : 220,
789 30 : 190,
790 45 : 175 }
791
792#---------------------------------------------------------------------------------------
793
794class F70(Aircraft):
795 """Fokker 70 aircraft.
796
797 The aircraft type-specific values in the aircraft state have the following
798 structure:
799 - fuel: left, centre, right
800 - n1: left, right
801 - reverser: left, right."""
802 def __init__(self, flight):
803 super(F70, self).__init__(flight)
804 self._checkers.append(checks.ThrustChecker())
805 self.dow = 24283
806 self.mtow = 38100 # FIXME: differentiate by registration number,
807 # MTOW of HA-LMF: 41955
808 self.mlw = 36740
809 self.mzfw = 32655
810 self.gearSpeedLimit = 200
811 self.flapSpeedLimits = { 8 : 250,
812 15 : 220,
813 25 : 220,
814 42 : 180 }
815 self.reverseMinSpeed = 50
816
817 @property
818 def derateLabels(self):
819 """Get the derate strings for this type."""
820 return ("EPR", None)
821
822 @property
823 def derateTemplate(self):
824 """Get the derate template for this aicraft type."""
825 return "EPR calculated by the pilot: %s"
826
827#---------------------------------------------------------------------------------------
828
829class DC3(Aircraft):
830 """Lisunov Li-2 (DC-3) aircraft.
831
832 The aircraft type-specific values in the aircraft state have the following
833 structure:
834 - fuel: left aux, left, right, right aux
835 - rpm: left, right
836 - reverser: left, right."""
837 def __init__(self, flight):
838 super(DC3, self).__init__(flight)
839 self.dow = 8627
840 self.mtow = 11884
841 self.mlw = 11793
842 self.mzfw = 11780
843 self.gearSpeedLimit = 148
844 self.flapSpeedLimits = { 15 : 135,
845 30 : 99,
846 45 : 97 }
847
848 def _checkFlightEnd(self, aircraftState):
849 """Check if the end of the flight has arrived.
850
851 This implementation checks the RPM values to be 0."""
852 for rpm in aircraftState.rpm:
853 if rpm>0: return False
854 return True
855
856#---------------------------------------------------------------------------------------
857
858class T134(Aircraft):
859 """Tupolev Tu-134 aircraft.
860
861 The aircraft type-specific values in the aircraft state have the following
862 structure:
863 - fuel: left tip, left aux, centre, right aux, right tip, external 1,
864 external 2
865 - n1: left, right
866 - reverser: left, right."""
867 def __init__(self, flight):
868 super(T134, self).__init__(flight)
869 self._checkers.append(checks.ThrustChecker())
870 self.dow = 29500
871 self.mtow = 49000
872 self.mlw = 43000
873 self.mzfw = 38500
874 self.gearSpeedLimit = 216
875 self.flapSpeedLimits = { 10 : 450,
876 20 : 400,
877 30 : 300 }
878 self.reverseMinSpeed = 50
879
880 @property
881 def derateLabels(self):
882 """Get the derate strings for this type."""
883 return (xstr("takeoff_derate_tupolev"), None)
884
885 @property
886 def derateTemplate(self):
887 """Get the derate template for this aicraft type."""
888 return "Nominal/takeoff power calculated by the pilot: %s"
889
890 @property
891 def speedInKnots(self):
892 """Indicate if the speed is in knots."""
893 return False
894
895 def _appendLightsLoggers(self):
896 """Append the loggers needed for the lights."""
897 self._checkers.append(checks.AnticollisionLightsLogger())
898 self._checkers.append(checks.LandingLightsLogger())
899 self._checkers.append(checks.NavLightsLogger())
900
901 def _appendLightsCheckers(self):
902 """Append the checkers needed for the lights."""
903 self._checkers.append(checks.TupolevAntiCollisionLightsChecker())
904 self._checkers.append(checks.LandingLightsChecker())
905 self._checkers.append(checks.NavLightsChecker())
906
907 def _appendSpeedChecker(self):
908 """Append the NoStrobeSpeedChecker to the checkers."""
909 self._checkers.append(checks.NoStrobeSpeedChecker())
910
911#---------------------------------------------------------------------------------------
912
913class T154(Aircraft):
914 """Tupolev Tu-154 aircraft.
915
916 The aircraft type-specific values in the aircraft state have the following
917 structure:
918 - fuel: left aux, left, centre, centre 2, right, right aux
919 - n1: left, centre, right
920 - reverser: left, right"""
921 def __init__(self, flight):
922 super(T154, self).__init__(flight)
923 self.dow = 53259
924 self.mtow = 98000
925 self.mlw = 78000
926 self.mzfw = 72000
927 self.gearSpeedLimit = 216
928 self.flapSpeedLimits = { 15 : 227,
929 28 : 194,
930 45 : 162 }
931 self.reverseMinSpeed = 50
932
933 @property
934 def speedInKnots(self):
935 """Indicate if the speed is in knots."""
936 return False
937
938 @property
939 def derateLabels(self):
940 """Get the derate strings for this type."""
941 return (xstr("takeoff_derate_tupolev"), None)
942
943 @property
944 def derateTemplate(self):
945 """Get the derate template for this aicraft type."""
946 return "Nominal/takeoff power calculated by the pilot: %s"
947
948 def _appendLightsLoggers(self):
949 """Append the loggers needed for the lights."""
950 self._checkers.append(checks.AnticollisionLightsLogger())
951 self._checkers.append(checks.LandingLightsLogger())
952 self._checkers.append(checks.NavLightsLogger())
953
954 def _appendLightsCheckers(self):
955 """Append the checkers needed for the lights."""
956 self._checkers.append(checks.AntiCollisionLightsChecker())
957 self._checkers.append(checks.LandingLightsChecker())
958 self._checkers.append(checks.NavLightsChecker())
959
960#---------------------------------------------------------------------------------------
961
962class YK40(Aircraft):
963 """Yakovlev Yak-40 aircraft.
964
965 The aircraft type-specific values in the aircraft state have the following
966 structure:
967 - fuel: left, right
968 - n1: left, right
969 - reverser: left, right"""
970 def __init__(self, flight):
971 super(YK40, self).__init__(flight)
972 self._checkers.append(checks.ThrustChecker())
973 self.dow = 9400
974 self.mtow = 17200
975 self.mlw = 16800
976 self.mzfw = 12100
977 self.gearSpeedLimit = 165
978 self.flapSpeedLimits = { 20 : 165,
979 35 : 135 }
980
981 @property
982 def speedInKnots(self):
983 """Indicate if the speed is in knots."""
984 return False
985
986 @property
987 def derateLabels(self):
988 """Get the derate strings for this type."""
989 return (xstr("takeoff_derate_tupolev"), None)
990
991 @property
992 def derateTemplate(self):
993 """Get the derate template for this aicraft type."""
994 return "Nominal/takeoff power calculated by the pilot: %s"
995
996 def _appendLightsLoggers(self):
997 """Append the loggers needed for the lights."""
998 self._checkers.append(checks.AnticollisionLightsLogger())
999 self._checkers.append(checks.LandingLightsLogger())
1000 self._checkers.append(checks.NavLightsLogger())
1001
1002 def _appendLightsCheckers(self):
1003 """Append the checkers needed for the lights."""
1004 self._checkers.append(checks.AntiCollisionLightsChecker())
1005 self._checkers.append(checks.LandingLightsChecker())
1006 self._checkers.append(checks.NavLightsChecker())
1007
1008#---------------------------------------------------------------------------------------
1009
1010mostFuelTanks = [const.FUELTANK_LEFT_TIP, const.FUELTANK_EXTERNAL1,
1011 const.FUELTANK_LEFT_AUX,
1012 const.FUELTANK_CENTRE,
1013 const.FUELTANK_RIGHT_AUX,
1014 const.FUELTANK_EXTERNAL2, const.FUELTANK_RIGHT_TIP]
1015
1016#---------------------------------------------------------------------------------------
1017
1018_classes = { const.AIRCRAFT_B736 : B736,
1019 const.AIRCRAFT_B737 : B737,
1020 const.AIRCRAFT_B738 : B738,
1021 const.AIRCRAFT_B738C : B738Charter,
1022 const.AIRCRAFT_B733 : B733,
1023 const.AIRCRAFT_B734 : B734,
1024 const.AIRCRAFT_B735 : B735,
1025 const.AIRCRAFT_DH8D : DH8D,
1026 const.AIRCRAFT_B762 : B762,
1027 const.AIRCRAFT_B763 : B763,
1028 const.AIRCRAFT_CRJ2 : CRJ2,
1029 const.AIRCRAFT_F70 : F70,
1030 const.AIRCRAFT_DC3 : DC3,
1031 const.AIRCRAFT_T134 : T134,
1032 const.AIRCRAFT_T154 : T154,
1033 const.AIRCRAFT_YK40 : YK40 }
1034
1035#---------------------------------------------------------------------------------------
1036
1037if __name__ == "__main__":
1038 value = SmoothedValue()
1039
1040 print "Adding 1, 12.0"
1041 value.add(1, 12.0)
1042 print value.get()
1043
1044 print "Adding 1, 15.0"
1045 value.add(1, 15.0)
1046 print value.get()
1047
1048 print "Adding 2, 18.0"
1049 value.add(2, 18.0)
1050 print value.get()
1051
1052 print "Adding 2, 20.0"
1053 value.add(2, 20.0)
1054 print value.get()
1055
1056 print "Adding 5, 22.0"
1057 value.add(5, 22.0)
1058 print value.get()
1059
1060 print "Adding 5, 25.0"
1061 value.add(5, 25.0)
1062 print value.get()
1063
1064 print "Adding 5, 29.0"
1065 value.add(5, 29.0)
1066 print value.get()
1067
1068 print "Adding 5, 21.0"
1069 value.add(5, 21.0)
1070 print value.get()
1071
1072 print "Adding 5, 26.0"
1073 value.add(5, 26.0)
1074 print value.get()
1075
1076 print "Adding 2, 30.0"
1077 value.add(2, 30.0)
1078 print value.get()
1079
1080 print "Adding 2, 55.0"
1081 value.add(2, 55.0)
1082 print value.get()
1083
1084#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.