source: src/checks.py@ 11:433f7e61f9f3

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

Added the checks

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