source: src/checks.py@ 21:0b447152d28b

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

Added support for validating the data read

File size: 37.5 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._spoilersExtension = 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.spoilersExtension!=self._spoilersExtension:
131 logger.message(state.timestamp, "Spoilers deployed")
132 self._logged = True
133 else:
134 self._spoilersExtension = state.spoilersExtension
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._faultStarted = state.timestamp
441 else:
442 self._faultStarted = None
443
444#---------------------------------------------------------------------------------------
445
446class AntiCollisionLightsChecker(PatientFaultChecker):
447 """Check for the anti-collision light being off at high N1 values."""
448 def isCondition(self, flight, aircraft, oldState, state):
449 """Check if the fault condition holds."""
450 return (flight.stage!=const.STAGE_PARKING or \
451 not flight.options.fs2Crew) and \
452 not state.antiCollisionLightsOn and \
453 max(state.n1)>5
454
455 def logFault(self, flight, aircraft, logger, oldState, state):
456 """Log the fault."""
457 logger.fault(AntiCollisionLightsChecker, state.timestamp,
458 FaultChecker._appendDuring(flight, "Anti-collision lights were off"),
459 1)
460
461#---------------------------------------------------------------------------------------
462
463class BankChecker(SimpleFaultChecker):
464 """Check for the anti-collision light being off at high N1 values."""
465 def isCondition(self, flight, aircraft, oldState, state):
466 """Check if the fault condition holds."""
467 if flight.stage==const.STAGE_CRUISE:
468 bankLimit = 30
469 elif flight.stage in [const.STAGE_TAKEOFF, const.STAGE_CLIMB,
470 const.STAGE_DESCENT, const.STAGE_LANDING]:
471 bankLimit = 35
472 else:
473 return False
474
475 return state.bank>bankLimit or state.bank<-bankLimit
476
477 def logFault(self, flight, aircraft, logger, oldState, state):
478 """Log the fault."""
479 logger.fault(BankChecker, state.timestamp,
480 FaultChecker._appendDuring(flight, "Bank too steep"),
481 2)
482
483#---------------------------------------------------------------------------------------
484
485class FlapsRetractChecker(SimpleFaultChecker):
486 """Check if the flaps are not retracted too early."""
487 def __init__(self):
488 """Construct the flaps checker."""
489 self._timeStart = None
490
491 def isCondition(self, flight, aircraft, oldState, state):
492 """Check if the fault condition holds.
493
494 FIXME: check if this really is the intention (FlapsRetractedMistake.java)"""
495 if (flight.stage==const.STAGE_TAKEOFF and not state.onTheGround) or \
496 (flight.stage==const.STAGE_LANDING and state.onTheGround):
497 if self._timeStart is None:
498 self._timeStart = state.timestamp
499
500 if state.flapsSet==0 and state.timestamp<=(self._timeStart+2.0):
501 return True
502 else:
503 self._timeStart = None
504 return False
505
506 def logFault(self, flight, aircraft, logger, oldState, state):
507 """Log the fault."""
508 logger.fault(FlapsRetractChecker, state.timestamp,
509 FaultChecker._appendDuring(flight, "Flaps retracted"),
510 20)
511
512#---------------------------------------------------------------------------------------
513
514class FlapsSpeedLimitChecker(SimpleFaultChecker):
515 """Check if the flaps are extended only at the right speeds."""
516 def isCondition(self, flight, aircraft, oldState, state):
517 """Check if the fault condition holds."""
518 speedLimit = aircraft.getFlapsSpeedLimit(state.flapsSet)
519 return speedLimit is not None and state.ias>speedLimit
520
521 def logFault(self, flight, aircraft, logger, oldState, state):
522 """Log the fault."""
523 logger.fault(FlapsSpeedLimitChecker, state.timestamp,
524 FaultChecker._appendDuring(flight, "Flap speed limit fault"),
525 5)
526
527#---------------------------------------------------------------------------------------
528
529class GearsDownChecker(SimpleFaultChecker):
530 """Check if the gears are down at low altitudes."""
531 def isCondition(self, flight, aircraft, oldState, state):
532 """Check if the fault condition holds."""
533 return state.radioAltitude<10 and not state.gearsDown and \
534 flight.stage!=const.STAGE_TAKEOFF
535
536 def logFault(self, flight, aircraft, logger, oldState, state):
537 """Log the fault."""
538 logger.noGo(GearsDownChecker, state.timestamp,
539 "Gears not down at %.0f feet radio altitude" % \
540 (state.radioAltitude,),
541 "GEAR DOWN NO GO")
542
543#---------------------------------------------------------------------------------------
544
545class GearSpeedLimitChecker(PatientFaultChecker):
546 """Check if the gears not down at too high a speed."""
547 def isCondition(self, flight, aircraft, oldState, state):
548 """Check if the fault condition holds."""
549 return state.gearsDown and state.ias>aircraft.gearSpeedLimit
550
551 def logFault(self, flight, aircraft, logger, oldState, state):
552 """Log the fault."""
553 logger.fault(GearSpeedLinmitChecker, state.timestamp,
554 FaultChecker._appendDuring(flight, "Gear speed limit fault"),
555 5)
556
557#---------------------------------------------------------------------------------------
558
559class GLoadChecker(SimpleFaultChecker):
560 """Check if the G-load does not exceed 2 except during flare."""
561 def isCondition(self, flight, aircraft, oldState, state):
562 """Check if the fault condition holds."""
563 return state.gLoad>2.0 and (flight.stage!=const.STAGE_LANDING or \
564 state.radioAltitude>=50)
565
566 def logFault(self, flight, aircraft, logger, oldState, state):
567 """Log the fault."""
568 logger.fault(GLoadChecker, state.timestamp,
569 "G-load was %.2f" % (state.gLoad,),
570 10)
571
572#---------------------------------------------------------------------------------------
573
574class LandingLightsChecker(PatientFaultChecker):
575 """Check if the landing lights are used properly."""
576 def getTimeout(self, flight, aircraft, oldState, state):
577 """Get the timeout.
578
579 It is the default timeout except for landing and takeoff."""
580 return 0.0 if flight.stage in [const.STAGE_TAKEOFF,
581 const.STAGE_LANDING] else self._timeout
582
583 def isCondition(self, flight, aircraft, oldState, state):
584 """Check if the fault condition holds."""
585 return (flight.stage==const.STAGE_BOARDING and \
586 state.landingLightsOn and state.onTheGround) or \
587 (flight.stage==const.STAGE_TAKEOFF and \
588 not state.landingLightsOn and not state.onTheGround) or \
589 (flight.stage in [const.STAGE_CLIMB, const.STAGE_CRUISE,
590 const.STAGE_DESCENT] and \
591 state.landingLightsOn and state.altitude>12500) or \
592 (flight.stage==const.STAGE_LANDING and \
593 not state.landingLightsOn and state.onTheGround) or \
594 (flight.stage==const.STAGE_PARKING and \
595 state.landingLightsOn and state.onTheGround)
596
597 def logFault(self, flight, aircraft, logger, oldState, state):
598 """Log the fault."""
599 score = 0 if flight.stage==const.STAGE_LANDING else 1
600 message = "Landing lights were %s" % (("on" if state.landingLightsOn else "off"),)
601 logger.fault(LandingLightsChecker, state.timestamp,
602 FaultChecker._appendDuring(flight, message),
603 score)
604
605#---------------------------------------------------------------------------------------
606
607class WeightChecker(PatientFaultChecker):
608 """Base class for checkers that check that some limit is not exceeded."""
609 def __init__(self, name):
610 """Construct the checker."""
611 super(WeightChecker, self).__init__(timeout = 5.0)
612 self._name = name
613
614 def isCondition(self, flight, aircraft, oldState, state):
615 """Check if the fault condition holds."""
616 if flight.entranceExam:
617 return False
618
619 limit = self.getLimit(flight, aircraft, state)
620 if limit is not None:
621 if flight.options.compensation is not None:
622 limit += flight.options.compensation
623 return self.getWeight(state)>limit
624
625 return False
626
627 def logFault(self, flight, aircraft, logger, oldState, state):
628 """Log the fault."""
629 mname = "M" + self._name
630 logger.noGo(self.__class__, state.timestamp,
631 "%s exceeded: %s is %.0f kg" % \
632 (mname, self._name, self.getWeight(state)),
633 "%s NO GO" % (mname,))
634
635 def getWeight(self, state):
636 """Get the weight that is interesting for us."""
637 return state.grossWeight
638
639#---------------------------------------------------------------------------------------
640
641class MLWChecker(WeightChecker):
642 """Checks if the MLW is not exceeded on landing."""
643 def __init__(self):
644 """Construct the checker."""
645 super(MLWChecker, self).__init__("LW")
646
647 def getLimit(self, flight, aircraft, state):
648 """Get the limit if we are in the right state."""
649 return aircraft.mlw if flight.stage==const.STAGE_LANDING and \
650 state.onTheGround else None
651
652#---------------------------------------------------------------------------------------
653
654class MTOWChecker(WeightChecker):
655 """Checks if the MTOW is not exceeded on landing."""
656 def __init__(self):
657 """Construct the checker."""
658 super(MTOWChecker, self).__init__("TOW")
659
660 def getLimit(self, flight, aircraft, state):
661 """Get the limit if we are in the right state."""
662 return aircraft.mtow if flight.stage==const.STAGE_TAKEOFF else None
663
664#---------------------------------------------------------------------------------------
665
666class MZFWChecker(WeightChecker):
667 """Checks if the MTOW is not exceeded on landing."""
668 def __init__(self):
669 """Construct the checker."""
670 super(MZFWChecker, self).__init__("ZFW")
671
672 def getLimit(self, flight, aircraft, state):
673 """Get the limit if we are in the right state."""
674 return aircraft.mzfw
675
676 def getWeight(self, state):
677 """Get the weight that is interesting for us."""
678 return state.zfw
679
680#---------------------------------------------------------------------------------------
681
682class NavLightsChecker(PatientFaultChecker):
683 """Check if the navigational lights are used properly."""
684 def isCondition(self, flight, aircraft, oldState, state):
685 """Check if the fault condition holds."""
686 return flight.stage!=const.STAGE_BOARDING and \
687 flight.stage!=const.STAGE_PARKING and \
688 not state.navLightsOn
689
690 def logFault(self, flight, aircraft, logger, oldState, state):
691 """Log the fault."""
692 logger.fault(NavLightsChecker, state.timestamp,
693 FaultChecker._appendDuring(flight, "Navigation lights were off"),
694 1)
695
696#---------------------------------------------------------------------------------------
697
698class OverspeedChecker(PatientFaultChecker):
699 """Check if Vne has been exceeded."""
700 def __init__(self):
701 """Construct the checker."""
702 super(OverspeedChecker, self).__init__(timeout = 5.0)
703
704 def isCondition(self, flight, aircraft, oldState, state):
705 """Check if the fault condition holds."""
706 return state.overspeed
707
708 def logFault(self, flight, aircraft, logger, oldState, state):
709 """Log the fault."""
710 logger.fault(OverspeedChecker, state.timestamp,
711 FaultChecker._appendDuring(flight, "Overspeed"),
712 20)
713
714#---------------------------------------------------------------------------------------
715
716class PayloadChecker(SimpleFaultChecker):
717 """Check if the payload matches the specification."""
718 TOLERANCE=550
719
720 def isCondition(self, flight, aircraft, oldState, state):
721 """Check if the fault condition holds."""
722 return flight.stage==const.STAGE_PUSHANDTAXI and \
723 (state.zfw < (flight.zfw - PayloadChecker.TOLERANCE) or \
724 state.zfw > (flight.zfw + PayloadChecker.TOLERANCE))
725
726 def logFault(self, flight, aircraft, logger, oldState, state):
727 """Log the fault."""
728 logger.noGo(PayloadChecker, state.timestamp,
729 "ZFW difference is more than %d kgs" % (PayloadChecker.TOLERANCE,),
730 "ZFW NO GO")
731
732#---------------------------------------------------------------------------------------
733
734class PitotChecker(PatientFaultChecker):
735 """Check if pitot heat is on."""
736 def __init__(self):
737 """Construct the checker."""
738 super(PitotChecker, self).__init__(timeout = 3.0)
739
740 def isCondition(self, flight, aircraft, oldState, state):
741 """Check if the fault condition holds."""
742 return state.groundSpeed>80 and not state.pitotHeatOn
743
744 def logFault(self, flight, aircraft, logger, oldState, state):
745 """Log the fault."""
746 score = 2 if flight.stage in [const.STAGE_TAKEOFF, const.STAGE_CLIMB,
747 const.STAGE_CRUISE, const.STAGE_DESCENT,
748 const.STAGE_LANDING] else 0
749 logger.fault(PitotChecker, state.timestamp,
750 FaultChecker._appendDuring(flight, "Pitot heat was off"),
751 score)
752
753#---------------------------------------------------------------------------------------
754
755class ReverserChecker(SimpleFaultChecker):
756 """Check if the reverser is not used below 60 knots."""
757 def isCondition(self, flight, aircraft, oldState, state):
758 """Check if the fault condition holds."""
759 return flight.stage in [const.STAGE_DESCENT, const.STAGE_LANDING,
760 const.STAGE_TAXIAFTERLAND] and \
761 state.groundSpeed<80 and max(state.reverser)
762
763 def logFault(self, flight, aircraft, logger, oldState, state):
764 """Log the fault."""
765 logger.fault(ReverserChecker, state.timestamp,
766 FaultChecker._appendDuring(flight, "Reverser used below 60 knots"),
767 15)
768
769#---------------------------------------------------------------------------------------
770
771class SpeedChecker(SimpleFaultChecker):
772 """Check if the speed is in the prescribed limits."""
773 def isCondition(self, flight, aircraft, oldState, state):
774 """Check if the fault condition holds."""
775 return flight.stage in [const.STAGE_PUSHANDTAXI,
776 const.STAGE_TAXIAFTERLAND] and \
777 state.groundSpeed>50
778
779 def logFault(self, flight, aircraft, logger, oldState, state):
780 """Log the fault."""
781 logger.fault(SpeedChecker, state.timestamp,
782 FaultChecker._appendDuring(flight, "Taxi speed over 50 knots"),
783 FaultChecker._getLinearScore(50, 80, 10, 15, state.groundSpeed))
784
785#---------------------------------------------------------------------------------------
786
787class StallChecker(PatientFaultChecker):
788 """Check if stall occured."""
789 def isCondition(self, flight, aircraft, oldState, state):
790 """Check if the fault condition holds."""
791 return flight.stage in [const.STAGE_TAKEOFF, const.STAGE_CLIMB,
792 const.STAGE_CRUISE, const.STAGE_DESCENT,
793 const.STAGE_LANDING] and state.stalled
794
795 def logFault(self, flight, aircraft, logger, oldState, state):
796 """Log the fault."""
797 score = 40 if flight.stage in [const.STAGE_TAKEOFF,
798 const.STAGE_LANDING] else 30
799 logger.fault(StallChecker, state.timestamp,
800 FaultChecker._appendDuring(flight, "Stalled"),
801 score)
802
803#---------------------------------------------------------------------------------------
804
805class StrobeLightsChecker(PatientFaultChecker):
806 """Check if the strobe lights are used properly."""
807 def isCondition(self, flight, aircraft, oldState, state):
808 """Check if the fault condition holds."""
809 return (flight.stage==const.STAGE_BOARDING and \
810 state.strobeLightsOn and state.onTheGround) or \
811 (flight.stage==const.STAGE_TAKEOFF and \
812 not state.strobeLightsOn and not state.gearsDown) or \
813 (flight.stage in [const.STAGE_CLIMB, const.STAGE_CRUISE,
814 const.STAGE_DESCENT] and \
815 not state.strobeLightsOn and not state.onTheGround) or \
816 (flight.stage==const.STAGE_PARKING and \
817 state.strobeLightsOn and state.onTheGround)
818
819 def logFault(self, flight, aircraft, logger, oldState, state):
820 """Log the fault."""
821 message = "Strobe lights were %s" % (("on" if state.strobeLightsOn else "off"),)
822 logger.fault(StrobeLightsChecker, state.timestamp,
823 FaultChecker._appendDuring(flight, message),
824 1)
825
826#---------------------------------------------------------------------------------------
827
828class ThrustChecker(SimpleFaultChecker):
829 """Check if the thrust setting is not too high during takeoff.
830
831 FIXME: is this really so general, for all aircraft?"""
832 def isCondition(self, flight, aircraft, oldState, state):
833 """Check if the fault condition holds."""
834 return flight.stage==const.STAGE_TAKEOFF and max(state.n1)>97
835
836 def logFault(self, flight, aircraft, logger, oldState, state):
837 """Log the fault."""
838 print state.n1
839 logger.fault(LandingLightsChecker, state.timestamp,
840 FaultChecker._appendDuring(flight, "Thrust setting was too high (>97%)"),
841 FaultChecker._getLinearScore(97, 110, 0, 10, max(state.n1)))
842
843#---------------------------------------------------------------------------------------
844
845class VSChecker(SimpleFaultChecker):
846 """Check if the vertical speed is not too low at certain altitudes"""
847 BELOW10000 = -5000
848 BELOW5000 = -2500
849 BELOW2500 = -1500
850 BELOW500 = -1000
851 TOLERANCE = 1.2
852
853 def isCondition(self, flight, aircraft, oldState, state):
854 """Check if the fault condition holds."""
855 vs = state.vs
856 altitude = state.altitude
857 return vs < -8000 or vs > 8000 or \
858 (altitude<500 and vs < (VSChecker.BELOW500 *
859 VSChecker.TOLERANCE)) or \
860 (altitude<2500 and vs < (VSChecker.BELOW2500 *
861 VSChecker.TOLERANCE)) or \
862 (altitude<5000 and vs < (VSChecker.BELOW5000 *
863 VSChecker.TOLERANCE)) or \
864 (altitude<10000 and vs < (VSChecker.BELOW10000 *
865 VSChecker.TOLERANCE))
866
867 def logFault(self, flight, aircraft, logger, oldState, state):
868 """Log the fault."""
869 vs = state.vs
870
871 message = "Vertical speed was %.0f feet/min" % (vs,)
872 if vs>-8000 and vs<8000:
873 message += " at %.0f feet (exceeds company limit)" % (state.altitude,)
874
875 score = 10 if vs<-8000 or vs>8000 else 0
876
877 logger.fault(VSChecker, state.timestamp,
878 FaultChecker._appendDuring(flight, message),
879 score)
880
881#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.