source: src/mlx/acft.py@ 384:97052bda0e22

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

Implemented support for entering derate values (#158)

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