source: src/mlx/checks.py@ 27:d95d48685969

Last change on this file since 27:d95d48685969 was 27:d95d48685969, checked in by István Váradi <ivaradi@…>, 12 years ago

Created icons, reorganized sources and made the Windows installer work

File size: 39.2 KB
Line 
1# The various checks that may be performed during flight
2
3#---------------------------------------------------------------------------------------
4
5import const
6
7#---------------------------------------------------------------------------------------
8
9class StateChecker(object):
10 """Base class for classes the instances of which check the aircraft's state
11 from some aspect.
12
13 As a result of the check they may log something, or notify of some fault, etc."""
14 def check(self, flight, aircraft, logger, oldState, state):
15 """Perform the check and do whatever is needed.
16
17 This default implementation raises a NotImplementedError."""
18 raise NotImplementedError()
19
20#---------------------------------------------------------------------------------------
21
22class StageChecker(StateChecker):
23 """Check the flight stage transitions."""
24 def __init__(self):
25 """Construct the stage checker."""
26 self._flareStarted = False
27
28 def check(self, flight, aircraft, logger, oldState, state):
29 """Check the stage of the aircraft."""
30 stage = flight.stage
31 if stage==None:
32 aircraft.setStage(state, const.STAGE_BOARDING)
33 elif stage==const.STAGE_BOARDING:
34 if not state.parking or \
35 (not state.trickMode and state.groundSpeed>5.0):
36 aircraft.setStage(state, const.STAGE_PUSHANDTAXI)
37 elif stage==const.STAGE_PUSHANDTAXI or stage==const.STAGE_RTO:
38 if state.landingLightsOn or state.strobeLightsOn or \
39 state.groundSpeed>80.0:
40 aircraft.setStage(state, const.STAGE_TAKEOFF)
41 elif stage==const.STAGE_TAKEOFF:
42 if not state.gearsDown or \
43 (state.radioAltitude>3000.0 and state.vs>0):
44 aircraft.setStage(state, const.STAGE_CLIMB)
45 elif not state.landingLightsOn and \
46 not state.strobeLightsOn and \
47 state.onTheGround and \
48 state.groundSpeed<50.0:
49 aircraft.setStage(state, const.STAGE_RTO)
50 elif stage==const.STAGE_CLIMB:
51 if (state.altitude+2000) > flight.cruiseAltitude:
52 aircraft.setStage(state, const.STAGE_CRUISE)
53 elif state.radioAltitude<2000.0 and \
54 state.vs < 0.0 and state.gearsDown:
55 aircraft.setStage(state, const.STAGE_LANDING)
56 elif stage==const.STAGE_CRUISE:
57 if (state.altitude+2000) < flight.cruiseAltitude:
58 aircraft.setStage(state, const.STAGE_DESCENT)
59 elif stage==const.STAGE_DESCENT or stage==const.STAGE_GOAROUND:
60 if state.gearsDown and state.radioAltitude<2000.0:
61 aircraft.setStage(state, const.STAGE_LANDING)
62 elif (state.altitude+2000) > flight.cruiseAltitude:
63 aircraft.setStage(state, const.STAGE_CRUISE)
64 elif stage==const.STAGE_LANDING:
65 if state.onTheGround and state.groundSpeed<50.0:
66 aircraft.setStage(state, const.STAGE_TAXIAFTERLAND)
67 elif not state.gearsDown:
68 aircraft.setStage(state, const.STAGE_GOAROUND)
69 elif state.radioAltitude>200 and self._flareStarted:
70 aircraft.cancelFlare()
71 self._flareStarted = False
72 elif state.radioAltitude<150 and not state.onTheGround and \
73 not self._flareStarted:
74 self._flareStarted = True
75 aircraft.prepareFlare()
76 elif stage==const.STAGE_TAXIAFTERLAND:
77 if state.parking:
78 aircraft.setStage(state, const.STAGE_PARKING)
79 elif stage==const.STAGE_PARKING:
80 if aircraft.checkFlightEnd(state):
81 aircraft.setStage(state, const.STAGE_END)
82
83#---------------------------------------------------------------------------------------
84
85class TakeOffLogger(StateChecker):
86 """Logger for the cruise speed."""
87 def __init__(self):
88 """Construct the logger."""
89 self._onTheGround = True
90
91 def check(self, flight, aircraft, logger, oldState, state):
92 """Log the cruise speed if necessary."""
93 if flight.stage==const.STAGE_TAKEOFF and \
94 self._onTheGround and not state.onTheGround:
95 logger.message(state.timestamp,
96 "Takeoff speed: %.0f knots" % (state.ias,))
97 logger.message(state.timestamp,
98 "Takeoff heading: %03.0f degrees" % (state.heading,))
99 logger.message(state.timestamp,
100 "Takeoff pitch: %.1f degrees" % (state.pitch,))
101 self._onTheGround = False
102
103#---------------------------------------------------------------------------------------
104
105class CruiseSpeedLogger(StateChecker):
106 """Logger for the cruise speed."""
107 def __init__(self):
108 """Construct the logger."""
109 self._lastTime = None
110
111 def check(self, flight, aircraft, logger, oldState, state):
112 """Log the cruise speed if necessary."""
113 if flight.stage==const.STAGE_CRUISE and \
114 (self._lastTime is None or \
115 (self._lastTime+800)<=state.timestamp):
116 if state.altitude>24500.0:
117 logger.message(state.timestamp,
118 "Cruise speed: %.3f mach" % (state.mach,))
119 else:
120 logger.message(state.timestamp,
121 "Cruise speed: %.0f knots" % (state.ias,))
122 self._lastTime = state.timestamp
123
124#---------------------------------------------------------------------------------------
125
126class SpoilerLogger(StateChecker):
127 """Logger for the cruise speed."""
128 def __init__(self):
129 """Construct the logger."""
130 self._logged = False
131 self._spoilersExtension = None
132
133 def check(self, flight, aircraft, logger, oldState, state):
134 """Log the cruise speed if necessary."""
135 if flight.stage==const.STAGE_LANDING and not self._logged:
136 if state.onTheGround:
137 if state.spoilersExtension!=self._spoilersExtension:
138 logger.message(state.timestamp, "Spoilers deployed")
139 self._logged = True
140 else:
141 self._spoilersExtension = state.spoilersExtension
142
143#---------------------------------------------------------------------------------------
144
145class StateChangeLogger(StateChecker):
146 """Base class for classes the instances of which check if a specific change has
147 occured in the aircraft's state, and log such change."""
148 def __init__(self, logInitial = True):
149 """Construct the logger.
150
151 If logInitial is True, the initial value will be logged, not just the
152 changes later.
153
154 Child classes should define the following functions:
155 - _changed(self, oldState, state): returns a boolean indicating if the
156 value has changed or not
157 - _getMessage(self, state): return a strings containing the message to log
158 with the new value
159 """
160 self._logInitial = logInitial
161
162 def _getLogTimestamp(self, state):
163 """Get the log timestamp."""
164 return state.timestamp
165
166 def check(self, flight, aircraft, logger, oldState, state):
167 """Check if the state has changed, and if so, log the new state."""
168 shouldLog = False
169 if oldState is None:
170 shouldLog = self._logInitial
171 else:
172 shouldLog = self._changed(oldState, state)
173
174 if shouldLog:
175 logger.message(self._getLogTimestamp(state), self._getMessage(state))
176
177#---------------------------------------------------------------------------------------
178
179class SimpleChangeMixin(object):
180 """A mixin that defines a _changed() function which simply calls a function
181 to retrieve the value two monitor for both states and compares them.
182
183 Child classes should define the following function:
184 - _getValue(state): get the value we are interested in."""
185 def _changed(self, oldState, state):
186 """Determine if the value has changed."""
187 return self._getValue(oldState)!=self._getValue(state)
188
189#---------------------------------------------------------------------------------------
190
191class SingleValueMixin(object):
192 """A mixin that provides a _getValue() function to query a value from the
193 state using the name of the attribute."""
194 def __init__(self, attrName):
195 """Construct the mixin with the given attribute name."""
196 self._attrName = attrName
197
198 def _getValue(self, state):
199 """Get the value of the attribute from the state."""
200 return getattr(state, self._attrName)
201
202#---------------------------------------------------------------------------------------
203
204class DelayedChangeMixin(object):
205 """A mixin to a StateChangeLogger that stores the old value and reports a
206 change only if the change has been there for a certain amount of time.
207
208 Child classes should define the following function:
209 - _getValue(state): get the value we are interested in."""
210 def __init__(self, minDelay = 3.0, maxDelay = 10.0):
211 """Construct the mixin with the given delay in seconds."""
212 self._minDelay = minDelay
213 self._maxDelay = maxDelay
214 self._oldValue = None
215 self._firstChange = None
216 self._lastChangeState = None
217
218 def _changed(self, oldState, state):
219 """Determine if the value has changed."""
220 if self._oldValue is None:
221 self._oldValue = self._getValue(oldState)
222
223 newValue = self._getValue(state)
224 if self._oldValue!=newValue:
225 if self._firstChange is None:
226 self._firstChange = state.timestamp
227 self._lastChangeState = state
228 self._oldValue = newValue
229
230 if self._firstChange is not None:
231 if state.timestamp >= min(self._lastChangeState.timestamp + self._minDelay,
232 self._firstChange + self._maxDelay):
233 self._firstChange = None
234 return True
235
236 return False
237
238 def _getLogTimestamp(self, state):
239 """Get the log timestamp."""
240 return self._lastChangeState.timestamp if \
241 self._lastChangeState is not None else state.timestamp
242
243#---------------------------------------------------------------------------------------
244
245class TemplateMessageMixin(object):
246 """Mixin to generate a message based on a template.
247
248 Child classes should define the following function:
249 - _getValue(state): get the value we are interested in."""
250 def __init__(self, template):
251 """Construct the mixin."""
252 self._template = template
253
254 def _getMessage(self, state):
255 """Get the message."""
256 return self._template % (self._getValue(state),)
257
258#---------------------------------------------------------------------------------------
259
260class GenericStateChangeLogger(StateChangeLogger, SingleValueMixin,
261 DelayedChangeMixin, TemplateMessageMixin):
262 """Base for generic state change loggers that monitor a single value in the
263 state possibly with a delay and the logged message comes from a template"""
264 def __init__(self, attrName, template, logInitial = True,
265 minDelay = 0.0, maxDelay = 0.0):
266 """Construct the object."""
267 StateChangeLogger.__init__(self, logInitial = logInitial)
268 SingleValueMixin.__init__(self, attrName)
269 DelayedChangeMixin.__init__(self, minDelay = minDelay, maxDelay = maxDelay)
270 TemplateMessageMixin.__init__(self, template)
271 self._getLogTimestamp = lambda state: \
272 DelayedChangeMixin._getLogTimestamp(self, state)
273
274#---------------------------------------------------------------------------------------
275
276class AltimeterLogger(StateChangeLogger, SingleValueMixin,
277 DelayedChangeMixin):
278 """Logger for the altimeter setting."""
279 def __init__(self):
280 """Construct the logger."""
281 StateChangeLogger.__init__(self, logInitial = True)
282 SingleValueMixin.__init__(self, "altimeter")
283 DelayedChangeMixin.__init__(self)
284 self._getLogTimestamp = lambda state: \
285 DelayedChangeMixin._getLogTimestamp(self, state)
286
287 def _getMessage(self, state):
288 """Get the message to log on a change."""
289 logState = self._lastChangeState if \
290 self._lastChangeState is not None else state
291 return "Altimeter: %.0f hPa at %.0f feet" % \
292 (logState.altimeter, logState.altitude)
293
294#---------------------------------------------------------------------------------------
295
296class NAV1Logger(GenericStateChangeLogger):
297 """Logger for the NAV1 radio setting."""
298 def __init__(self):
299 """Construct the logger."""
300 super(NAV1Logger, self).__init__("nav1", "NAV1 frequency: %s MHz")
301
302#---------------------------------------------------------------------------------------
303
304class NAV2Logger(GenericStateChangeLogger):
305 """Logger for the NAV2 radio setting."""
306 def __init__(self):
307 """Construct the logger."""
308 super(NAV2Logger, self).__init__("nav2", "NAV2 frequency: %s MHz")
309
310#---------------------------------------------------------------------------------------
311
312class SquawkLogger(GenericStateChangeLogger):
313 """Logger for the squawk setting."""
314 def __init__(self):
315 """Construct the logger."""
316 super(SquawkLogger, self).__init__("squawk", "Squawk code: %s",
317 minDelay = 3.0, maxDelay = 10.0)
318
319#---------------------------------------------------------------------------------------
320
321class LightsLogger(StateChangeLogger, SingleValueMixin, SimpleChangeMixin):
322 """Base class for the loggers of the various lights."""
323 def __init__(self, attrName, template):
324 """Construct the logger."""
325 StateChangeLogger.__init__(self)
326 SingleValueMixin.__init__(self, attrName)
327
328 self._template = template
329
330 def _getMessage(self, state):
331 """Get the message from the given state."""
332 return self._template % ("ON" if self._getValue(state) else "OFF")
333
334#---------------------------------------------------------------------------------------
335
336class AnticollisionLightsLogger(LightsLogger):
337 """Logger for the anti-collision lights."""
338 def __init__(self):
339 LightsLogger.__init__(self, "antiCollisionLightsOn",
340 "Anti-collision lights: %s")
341
342#---------------------------------------------------------------------------------------
343
344class LandingLightsLogger(LightsLogger):
345 """Logger for the landing lights."""
346 def __init__(self):
347 LightsLogger.__init__(self, "landingLightsOn",
348 "Landing lights: %s")
349
350#---------------------------------------------------------------------------------------
351
352class StrobeLightsLogger(LightsLogger):
353 """Logger for the strobe lights."""
354 def __init__(self):
355 LightsLogger.__init__(self, "strobeLightsOn",
356 "Strobe lights: %s")
357
358#---------------------------------------------------------------------------------------
359
360class NavLightsLogger(LightsLogger):
361 """Logger for the navigational lights."""
362 def __init__(self):
363 LightsLogger.__init__(self, "navLightsOn",
364 "Navigational lights: %s")
365
366#---------------------------------------------------------------------------------------
367
368class FlapsLogger(StateChangeLogger, SingleValueMixin, SimpleChangeMixin):
369 """Logger for the flaps setting."""
370 def __init__(self):
371 """Construct the logger."""
372 StateChangeLogger.__init__(self, logInitial = True)
373 SingleValueMixin.__init__(self, "flapsSet")
374
375 def _getMessage(self, state):
376 """Get the message to log on a change."""
377 speed = state.groundSpeed if state.groundSpeed<80.0 else state.ias
378 return "Flaps set to %.0f at %.0f knots" % (state.flapsSet, speed)
379
380#---------------------------------------------------------------------------------------
381
382class GearsLogger(StateChangeLogger, SingleValueMixin, SimpleChangeMixin):
383 """Logger for the gears state."""
384 def __init__(self):
385 """Construct the logger."""
386 StateChangeLogger.__init__(self, logInitial = True)
387 SingleValueMixin.__init__(self, "gearsDown")
388
389 def _getMessage(self, state):
390 """Get the message to log on a change."""
391 return "Gears %s at %.0f knots, %.0f feet" % \
392 ("DOWN" if state.gearsDown else "UP", state.ias, state.altitude)
393
394#---------------------------------------------------------------------------------------
395
396class FaultChecker(StateChecker):
397 """Base class for checkers that look for faults."""
398 @staticmethod
399 def _appendDuring(flight, message):
400 """Append a 'during XXX' test to the given message, depending on the
401 flight stage."""
402 stageStr = const.stage2string(flight.stage)
403 return message if stageStr is None \
404 else (message + " during " + stageStr.upper())
405
406 @staticmethod
407 def _getLinearScore(minFaultValue, maxFaultValue, minScore, maxScore,
408 value):
409 """Get the score for a faulty value where the score is calculated
410 linearly within a certain range."""
411 if value<minFaultValue:
412 return 0
413 elif value>maxFaultValue:
414 return maxScore
415 else:
416 return minScore + (maxScore-minScore) * (value-minFaultValue) / \
417 (maxFaultValue - minFaultValue)
418
419#---------------------------------------------------------------------------------------
420
421class SimpleFaultChecker(FaultChecker):
422 """Base class for fault checkers that check for a single occurence of a
423 faulty condition.
424
425 Child classes should implement the following functions:
426 - isCondition(self, flight, aircraft, oldState, state): should return whether the
427 condition holds
428 - logFault(self, flight, aircraft, logger, oldState, state): log the fault
429 via the logger."""
430 def check(self, flight, aircraft, logger, oldState, state):
431 """Perform the check."""
432 if self.isCondition(flight, aircraft, oldState, state):
433 self.logFault(flight, aircraft, logger, oldState, state)
434
435#---------------------------------------------------------------------------------------
436
437class PatientFaultChecker(FaultChecker):
438 """A fault checker that does not decides on a fault when the condition
439 arises immediately, but can wait some time.
440
441 Child classes should implement the following functions:
442 - isCondition(self, flight, aircraft, oldState, state): should return whether the
443 condition holds
444 - logFault(self, flight, aircraft, logger, oldState, state): log the fault
445 via the logger
446 """
447 def __init__(self, timeout = 2.0):
448 """Construct the fault checker with the given timeout."""
449 self._timeout = timeout
450 self._faultStarted = None
451
452 def getTimeout(self, flight, aircraft, oldState, state):
453 """Get the timeout.
454
455 This default implementation returns the timeout given in the
456 constructor, but child classes might want to enforce a different
457 policy."""
458 return self._timeout
459
460 def check(self, flight, aircraft, logger, oldState, state):
461 """Perform the check."""
462 if self.isCondition(flight, aircraft, oldState, state):
463 if self._faultStarted is None:
464 self._faultStarted = state.timestamp
465 timeout = self.getTimeout(flight, aircraft, oldState, state)
466 if state.timestamp>=(self._faultStarted + timeout):
467 self.logFault(flight, aircraft, logger, oldState, state)
468 self._faultStarted = state.timestamp
469 else:
470 self._faultStarted = None
471
472#---------------------------------------------------------------------------------------
473
474class AntiCollisionLightsChecker(PatientFaultChecker):
475 """Check for the anti-collision light being off at high N1 values."""
476 def isCondition(self, flight, aircraft, oldState, state):
477 """Check if the fault condition holds."""
478 isTupolev = aircraft.type in [const.AIRCRAFT_T134,
479 const.AIRCRAFT_T154]
480 return (flight.stage!=const.STAGE_PARKING or \
481 not flight.options.fs2Crew) and \
482 not state.antiCollisionLightsOn and \
483 ((isTupolev and max(state.n1[1:])>5) or \
484 (not isTupolev and max(state.n1)>5))
485
486 def logFault(self, flight, aircraft, logger, oldState, state):
487 """Log the fault."""
488 logger.fault(AntiCollisionLightsChecker, state.timestamp,
489 FaultChecker._appendDuring(flight, "Anti-collision lights were off"),
490 1)
491
492#---------------------------------------------------------------------------------------
493
494class BankChecker(SimpleFaultChecker):
495 """Check for the anti-collision light being off at high N1 values."""
496 def isCondition(self, flight, aircraft, oldState, state):
497 """Check if the fault condition holds."""
498 if flight.stage==const.STAGE_CRUISE:
499 bankLimit = 30
500 elif flight.stage in [const.STAGE_TAKEOFF, const.STAGE_CLIMB,
501 const.STAGE_DESCENT, const.STAGE_LANDING]:
502 bankLimit = 35
503 else:
504 return False
505
506 return state.bank>bankLimit or state.bank<-bankLimit
507
508 def logFault(self, flight, aircraft, logger, oldState, state):
509 """Log the fault."""
510 logger.fault(BankChecker, state.timestamp,
511 FaultChecker._appendDuring(flight, "Bank too steep"),
512 2)
513
514#---------------------------------------------------------------------------------------
515
516class FlapsRetractChecker(SimpleFaultChecker):
517 """Check if the flaps are not retracted too early."""
518 def __init__(self):
519 """Construct the flaps checker."""
520 self._timeStart = None
521
522 def isCondition(self, flight, aircraft, oldState, state):
523 """Check if the fault condition holds.
524
525 FIXME: check if this really is the intention (FlapsRetractedMistake.java)"""
526 if (flight.stage==const.STAGE_TAKEOFF and not state.onTheGround) or \
527 (flight.stage==const.STAGE_LANDING and state.onTheGround):
528 if self._timeStart is None:
529 self._timeStart = state.timestamp
530
531 if state.flapsSet==0 and state.timestamp<=(self._timeStart+2.0):
532 return True
533 else:
534 self._timeStart = None
535 return False
536
537 def logFault(self, flight, aircraft, logger, oldState, state):
538 """Log the fault."""
539 logger.fault(FlapsRetractChecker, state.timestamp,
540 FaultChecker._appendDuring(flight, "Flaps retracted"),
541 20)
542
543#---------------------------------------------------------------------------------------
544
545class FlapsSpeedLimitChecker(SimpleFaultChecker):
546 """Check if the flaps are extended only at the right speeds."""
547 def isCondition(self, flight, aircraft, oldState, state):
548 """Check if the fault condition holds."""
549 speedLimit = aircraft.getFlapsSpeedLimit(state.flapsSet)
550 return speedLimit is not None and state.ias>speedLimit
551
552 def logFault(self, flight, aircraft, logger, oldState, state):
553 """Log the fault."""
554 logger.fault(FlapsSpeedLimitChecker, state.timestamp,
555 FaultChecker._appendDuring(flight, "Flap speed limit fault"),
556 5)
557
558#---------------------------------------------------------------------------------------
559
560class GearsDownChecker(SimpleFaultChecker):
561 """Check if the gears are down at low altitudes."""
562 def isCondition(self, flight, aircraft, oldState, state):
563 """Check if the fault condition holds."""
564 return state.radioAltitude<10 and not state.gearsDown and \
565 flight.stage!=const.STAGE_TAKEOFF
566
567 def logFault(self, flight, aircraft, logger, oldState, state):
568 """Log the fault."""
569 logger.noGo(GearsDownChecker, state.timestamp,
570 "Gears not down at %.0f feet radio altitude" % \
571 (state.radioAltitude,),
572 "GEAR DOWN NO GO")
573
574#---------------------------------------------------------------------------------------
575
576class GearSpeedLimitChecker(PatientFaultChecker):
577 """Check if the gears not down at too high a speed."""
578 def isCondition(self, flight, aircraft, oldState, state):
579 """Check if the fault condition holds."""
580 return state.gearsDown and state.ias>aircraft.gearSpeedLimit
581
582 def logFault(self, flight, aircraft, logger, oldState, state):
583 """Log the fault."""
584 logger.fault(GearSpeedLimitChecker, state.timestamp,
585 FaultChecker._appendDuring(flight, "Gear speed limit fault"),
586 5)
587
588#---------------------------------------------------------------------------------------
589
590class GLoadChecker(SimpleFaultChecker):
591 """Check if the G-load does not exceed 2 except during flare."""
592 def isCondition(self, flight, aircraft, oldState, state):
593 """Check if the fault condition holds."""
594 return state.gLoad>2.0 and (flight.stage!=const.STAGE_LANDING or \
595 state.radioAltitude>=50)
596
597 def logFault(self, flight, aircraft, logger, oldState, state):
598 """Log the fault."""
599 logger.fault(GLoadChecker, state.timestamp,
600 "G-load was %.2f" % (state.gLoad,),
601 10)
602
603#---------------------------------------------------------------------------------------
604
605class LandingLightsChecker(PatientFaultChecker):
606 """Check if the landing lights are used properly."""
607 def getTimeout(self, flight, aircraft, oldState, state):
608 """Get the timeout.
609
610 It is the default timeout except for landing and takeoff."""
611 return 0.0 if flight.stage in [const.STAGE_TAKEOFF,
612 const.STAGE_LANDING] else self._timeout
613
614 def isCondition(self, flight, aircraft, oldState, state):
615 """Check if the fault condition holds."""
616 return (flight.stage==const.STAGE_BOARDING and \
617 state.landingLightsOn and state.onTheGround) or \
618 (flight.stage==const.STAGE_TAKEOFF and \
619 not state.landingLightsOn and not state.onTheGround) or \
620 (flight.stage in [const.STAGE_CLIMB, const.STAGE_CRUISE,
621 const.STAGE_DESCENT] and \
622 state.landingLightsOn and state.altitude>12500) or \
623 (flight.stage==const.STAGE_LANDING and \
624 not state.landingLightsOn and state.onTheGround) or \
625 (flight.stage==const.STAGE_PARKING and \
626 state.landingLightsOn and state.onTheGround)
627
628 def logFault(self, flight, aircraft, logger, oldState, state):
629 """Log the fault."""
630 score = 0 if flight.stage==const.STAGE_LANDING else 1
631 message = "Landing lights were %s" % (("on" if state.landingLightsOn else "off"),)
632 logger.fault(LandingLightsChecker, state.timestamp,
633 FaultChecker._appendDuring(flight, message),
634 score)
635
636#---------------------------------------------------------------------------------------
637
638class WeightChecker(PatientFaultChecker):
639 """Base class for checkers that check that some limit is not exceeded."""
640 def __init__(self, name):
641 """Construct the checker."""
642 super(WeightChecker, self).__init__(timeout = 5.0)
643 self._name = name
644
645 def isCondition(self, flight, aircraft, oldState, state):
646 """Check if the fault condition holds."""
647 if flight.entranceExam:
648 return False
649
650 limit = self.getLimit(flight, aircraft, state)
651 if limit is not None:
652 if flight.options.compensation is not None:
653 limit += flight.options.compensation
654 return self.getWeight(state)>limit
655
656 return False
657
658 def logFault(self, flight, aircraft, logger, oldState, state):
659 """Log the fault."""
660 mname = "M" + self._name
661 logger.noGo(self.__class__, state.timestamp,
662 "%s exceeded: %s is %.0f kg" % \
663 (mname, self._name, self.getWeight(state)),
664 "%s NO GO" % (mname,))
665
666 def getWeight(self, state):
667 """Get the weight that is interesting for us."""
668 return state.grossWeight
669
670#---------------------------------------------------------------------------------------
671
672class MLWChecker(WeightChecker):
673 """Checks if the MLW is not exceeded on landing."""
674 def __init__(self):
675 """Construct the checker."""
676 super(MLWChecker, self).__init__("LW")
677
678 def getLimit(self, flight, aircraft, state):
679 """Get the limit if we are in the right state."""
680 return aircraft.mlw if flight.stage==const.STAGE_LANDING and \
681 state.onTheGround else None
682
683#---------------------------------------------------------------------------------------
684
685class MTOWChecker(WeightChecker):
686 """Checks if the MTOW is not exceeded on landing."""
687 def __init__(self):
688 """Construct the checker."""
689 super(MTOWChecker, self).__init__("TOW")
690
691 def getLimit(self, flight, aircraft, state):
692 """Get the limit if we are in the right state."""
693 return aircraft.mtow if flight.stage==const.STAGE_TAKEOFF else None
694
695#---------------------------------------------------------------------------------------
696
697class MZFWChecker(WeightChecker):
698 """Checks if the MTOW is not exceeded on landing."""
699 def __init__(self):
700 """Construct the checker."""
701 super(MZFWChecker, self).__init__("ZFW")
702
703 def getLimit(self, flight, aircraft, state):
704 """Get the limit if we are in the right state."""
705 return aircraft.mzfw
706
707 def getWeight(self, state):
708 """Get the weight that is interesting for us."""
709 return state.zfw
710
711#---------------------------------------------------------------------------------------
712
713class NavLightsChecker(PatientFaultChecker):
714 """Check if the navigational lights are used properly."""
715 def isCondition(self, flight, aircraft, oldState, state):
716 """Check if the fault condition holds."""
717 return flight.stage!=const.STAGE_BOARDING and \
718 flight.stage!=const.STAGE_PARKING and \
719 not state.navLightsOn
720
721 def logFault(self, flight, aircraft, logger, oldState, state):
722 """Log the fault."""
723 logger.fault(NavLightsChecker, state.timestamp,
724 FaultChecker._appendDuring(flight, "Navigation lights were off"),
725 1)
726
727#---------------------------------------------------------------------------------------
728
729class OverspeedChecker(PatientFaultChecker):
730 """Check if Vne has been exceeded."""
731 def __init__(self):
732 """Construct the checker."""
733 super(OverspeedChecker, self).__init__(timeout = 5.0)
734
735 def isCondition(self, flight, aircraft, oldState, state):
736 """Check if the fault condition holds."""
737 return state.overspeed
738
739 def logFault(self, flight, aircraft, logger, oldState, state):
740 """Log the fault."""
741 logger.fault(OverspeedChecker, state.timestamp,
742 FaultChecker._appendDuring(flight, "Overspeed"),
743 20)
744
745#---------------------------------------------------------------------------------------
746
747class PayloadChecker(SimpleFaultChecker):
748 """Check if the payload matches the specification."""
749 TOLERANCE=550
750
751 def isCondition(self, flight, aircraft, oldState, state):
752 """Check if the fault condition holds."""
753 return flight.stage==const.STAGE_PUSHANDTAXI and \
754 (state.zfw < (flight.zfw - PayloadChecker.TOLERANCE) or \
755 state.zfw > (flight.zfw + PayloadChecker.TOLERANCE))
756
757 def logFault(self, flight, aircraft, logger, oldState, state):
758 """Log the fault."""
759 logger.noGo(PayloadChecker, state.timestamp,
760 "ZFW difference is more than %d kgs" % (PayloadChecker.TOLERANCE,),
761 "ZFW NO GO")
762
763#---------------------------------------------------------------------------------------
764
765class PitotChecker(PatientFaultChecker):
766 """Check if pitot heat is on."""
767 def __init__(self):
768 """Construct the checker."""
769 super(PitotChecker, self).__init__(timeout = 3.0)
770
771 def isCondition(self, flight, aircraft, oldState, state):
772 """Check if the fault condition holds."""
773 return state.groundSpeed>80 and not state.pitotHeatOn
774
775 def logFault(self, flight, aircraft, logger, oldState, state):
776 """Log the fault."""
777 score = 2 if flight.stage in [const.STAGE_TAKEOFF, const.STAGE_CLIMB,
778 const.STAGE_CRUISE, const.STAGE_DESCENT,
779 const.STAGE_LANDING] else 0
780 logger.fault(PitotChecker, state.timestamp,
781 FaultChecker._appendDuring(flight, "Pitot heat was off"),
782 score)
783
784#---------------------------------------------------------------------------------------
785
786class ReverserChecker(SimpleFaultChecker):
787 """Check if the reverser is not used below 60 knots."""
788 def isCondition(self, flight, aircraft, oldState, state):
789 """Check if the fault condition holds."""
790 return flight.stage in [const.STAGE_DESCENT, const.STAGE_LANDING,
791 const.STAGE_TAXIAFTERLAND] and \
792 state.groundSpeed<60 and max(state.reverser)
793
794 def logFault(self, flight, aircraft, logger, oldState, state):
795 """Log the fault."""
796 logger.fault(ReverserChecker, state.timestamp,
797 FaultChecker._appendDuring(flight, "Reverser used below 60 knots"),
798 15)
799
800#---------------------------------------------------------------------------------------
801
802class SpeedChecker(SimpleFaultChecker):
803 """Check if the speed is in the prescribed limits."""
804 def isCondition(self, flight, aircraft, oldState, state):
805 """Check if the fault condition holds."""
806 return flight.stage in [const.STAGE_PUSHANDTAXI,
807 const.STAGE_TAXIAFTERLAND] and \
808 state.groundSpeed>50
809
810 def logFault(self, flight, aircraft, logger, oldState, state):
811 """Log the fault."""
812 logger.fault(SpeedChecker, state.timestamp,
813 FaultChecker._appendDuring(flight, "Taxi speed over 50 knots"),
814 FaultChecker._getLinearScore(50, 80, 10, 15, state.groundSpeed))
815
816#---------------------------------------------------------------------------------------
817
818class StallChecker(PatientFaultChecker):
819 """Check if stall occured."""
820 def isCondition(self, flight, aircraft, oldState, state):
821 """Check if the fault condition holds."""
822 return flight.stage in [const.STAGE_TAKEOFF, const.STAGE_CLIMB,
823 const.STAGE_CRUISE, const.STAGE_DESCENT,
824 const.STAGE_LANDING] and state.stalled
825
826 def logFault(self, flight, aircraft, logger, oldState, state):
827 """Log the fault."""
828 score = 40 if flight.stage in [const.STAGE_TAKEOFF,
829 const.STAGE_LANDING] else 30
830 logger.fault(StallChecker, state.timestamp,
831 FaultChecker._appendDuring(flight, "Stalled"),
832 score)
833
834#---------------------------------------------------------------------------------------
835
836class StrobeLightsChecker(PatientFaultChecker):
837 """Check if the strobe lights are used properly."""
838 def isCondition(self, flight, aircraft, oldState, state):
839 """Check if the fault condition holds."""
840 if aircraft.type in [const.AIRCRAFT_T134,
841 const.AIRCRAFT_T154,
842 const.AIRCRAFT_YK40]:
843 return False
844 else:
845 return (flight.stage==const.STAGE_BOARDING and \
846 state.strobeLightsOn and state.onTheGround) or \
847 (flight.stage==const.STAGE_TAKEOFF and \
848 not state.strobeLightsOn and not state.gearsDown) or \
849 (flight.stage in [const.STAGE_CLIMB, const.STAGE_CRUISE,
850 const.STAGE_DESCENT] and \
851 not state.strobeLightsOn and not state.onTheGround) or \
852 (flight.stage==const.STAGE_PARKING and \
853 state.strobeLightsOn and state.onTheGround)
854
855 def logFault(self, flight, aircraft, logger, oldState, state):
856 """Log the fault."""
857 message = "Strobe lights were %s" % (("on" if state.strobeLightsOn else "off"),)
858 logger.fault(StrobeLightsChecker, state.timestamp,
859 FaultChecker._appendDuring(flight, message),
860 1)
861
862#---------------------------------------------------------------------------------------
863
864class ThrustChecker(SimpleFaultChecker):
865 """Check if the thrust setting is not too high during takeoff.
866
867 FIXME: is this really so general, for all aircraft?"""
868 def isCondition(self, flight, aircraft, oldState, state):
869 """Check if the fault condition holds."""
870 return flight.stage==const.STAGE_TAKEOFF and max(state.n1)>97
871
872 def logFault(self, flight, aircraft, logger, oldState, state):
873 """Log the fault."""
874 print state.n1
875 logger.fault(ThrustChecker, state.timestamp,
876 FaultChecker._appendDuring(flight, "Thrust setting was too high (>97%)"),
877 FaultChecker._getLinearScore(97, 110, 0, 10, max(state.n1)))
878
879#---------------------------------------------------------------------------------------
880
881class VSChecker(SimpleFaultChecker):
882 """Check if the vertical speed is not too low at certain altitudes"""
883 BELOW10000 = -5000
884 BELOW5000 = -2500
885 BELOW2500 = -1500
886 BELOW500 = -1000
887 TOLERANCE = 1.2
888
889 def isCondition(self, flight, aircraft, oldState, state):
890 """Check if the fault condition holds."""
891 vs = state.vs
892 altitude = state.altitude
893 return vs < -8000 or vs > 8000 or \
894 (altitude<500 and vs < (VSChecker.BELOW500 *
895 VSChecker.TOLERANCE)) or \
896 (altitude<2500 and vs < (VSChecker.BELOW2500 *
897 VSChecker.TOLERANCE)) or \
898 (altitude<5000 and vs < (VSChecker.BELOW5000 *
899 VSChecker.TOLERANCE)) or \
900 (altitude<10000 and vs < (VSChecker.BELOW10000 *
901 VSChecker.TOLERANCE))
902
903 def logFault(self, flight, aircraft, logger, oldState, state):
904 """Log the fault."""
905 vs = state.vs
906
907 message = "Vertical speed was %.0f feet/min" % (vs,)
908 if vs>-8000 and vs<8000:
909 message += " at %.0f feet (exceeds company limit)" % (state.altitude,)
910
911 score = 10 if vs<-8000 or vs>8000 else 0
912
913 logger.fault(VSChecker, state.timestamp,
914 FaultChecker._appendDuring(flight, message),
915 score)
916
917#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.