source: src/checks.py@ 15:579098859ff8

Last change on this file since 15:579098859ff8 was 13:7cdc145ceef9, checked in by István Váradi <ivaradi@…>, 13 years ago

Fixed some types and small problems

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