source: src/mlx/checks.py@ 36:f79362793664

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

Fault messages are routed through the Flight object

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