source: src/mlx/acft.py@ 623:33edcb39a260

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

The strobeless speed checker is used for all aircraft to detect the takeoff stage (re #260)

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