source: src/mlx/flight.py@ 356:a9dd7d794212

Last change on this file since 356:a9dd7d794212 was 354:4efeae885059, checked in by István Váradi <ivaradi@…>, 12 years ago

Some further smaller refinements of the strobeless takeoff detection (#143)

File size: 13.8 KB
Line 
1
2from soundsched import SoundScheduler, ChecklistScheduler
3from checks import SpeedChecker
4
5import const
6import util
7
8import threading
9
10#---------------------------------------------------------------------------------------
11
12## @package mlx.flight
13#
14# The global flight state.
15#
16# This module defines a single class, \ref Flight, which represents the flight
17# in progress.
18
19#---------------------------------------------------------------------------------------
20
21class Flight(object):
22 """The object with the global flight state.
23
24 It is also the hub for the other main objects participating in the handling of
25 the flight."""
26 def __init__(self, logger, gui):
27 """Construct the flight."""
28 self._stage = None
29 self.logger = logger
30 self._gui = gui
31
32 gui.resetFlightStatus()
33
34 self._pilotHotkeyPressed = False
35 self._checklistHotkeyPressed = False
36
37 self.flareTimeFromFS = False
38
39 self.aircraftType = None
40 self.aircraft = None
41 self.simulator = None
42
43 self.blockTimeStart = None
44 self.flightTimeStart = None
45 self.flightTimeEnd = None
46 self.blockTimeEnd = None
47
48 self._rtoState = None
49 self._rtoLogEntryID = None
50
51 self._lastDistanceTime = None
52 self._previousLatitude = None
53 self._previousLongitude = None
54 self.flownDistance = 0.0
55
56 self.startFuel = None
57 self.endFuel = None
58
59 self._endCondition = threading.Condition()
60
61 self._flareStart = None
62 self._flareStartFS = None
63
64 self._tdRate = None
65
66 self._soundScheduler = SoundScheduler(self)
67 self._checklistScheduler = ChecklistScheduler(self)
68
69 @property
70 def config(self):
71 """Get the configuration."""
72 return self._gui.config
73
74 @property
75 def stage(self):
76 """Get the flight stage."""
77 return self._stage
78
79 @property
80 def loggedIn(self):
81 """Indicate if the user has logged in properly."""
82 return self._gui.loggedIn
83
84 @property
85 def entranceExam(self):
86 """Get whether an entrance exam is being performed."""
87 return self._gui.entranceExam
88
89 @property
90 def bookedFlight(self):
91 """Get the booked flight."""
92 return self._gui.bookedFlight
93
94 @property
95 def numCrew(self):
96 """Get the number of crew members on the flight."""
97 return self._gui.numCrew
98
99 @property
100 def numPassengers(self):
101 """Get the number of passengers on the flight."""
102 return self._gui.numPassengers
103
104 @property
105 def bagWeight(self):
106 """Get the baggage weight for the flight."""
107 return self._gui.bagWeight
108
109 @property
110 def cargoWeight(self):
111 """Get the cargo weight for the flight."""
112 return self._gui.cargoWeight
113
114 @property
115 def mailWeight(self):
116 """Get the mail weight for the flight."""
117 return self._gui.mailWeight
118
119 @property
120 def zfw(self):
121 """Get the Zero-Fuel Weight of the flight."""
122 return self._gui.zfw
123
124 @property
125 def filedCruiseAltitude(self):
126 """Get the filed cruise altitude."""
127 return self._gui.filedCruiseAltitude
128
129 @property
130 def cruiseAltitude(self):
131 """Get the cruise altitude of the flight."""
132 return self._gui.cruiseAltitude
133
134 @property
135 def route(self):
136 """Get the route of the flight."""
137 return self._gui.route
138
139 @property
140 def departureMETAR(self):
141 """Get the departure METAR of the flight."""
142 return self._gui.departureMETAR
143
144 @property
145 def arrivalMETAR(self):
146 """Get the arrival METAR of the flight."""
147 return self._gui.arrivalMETAR
148
149 @property
150 def departureRunway(self):
151 """Get the departure runway."""
152 return self._gui.departureRunway
153
154 @property
155 def sid(self):
156 """Get the SID followed."""
157 return self._gui.sid
158
159 @property
160 def v1(self):
161 """Get the V1 speed of the flight."""
162 return self._gui.v1
163
164 @property
165 def vr(self):
166 """Get the Vr speed of the flight."""
167 return self._gui.vr
168
169 @property
170 def v2(self):
171 """Get the V2 speed of the flight."""
172 return self._gui.v2
173
174 @property
175 def star(self):
176 """Get the STAR planned."""
177 return self._gui.star
178
179 @property
180 def transition(self):
181 """Get the transition planned."""
182 return self._gui.transition
183
184 @property
185 def approachType(self):
186 """Get the approach type."""
187 return self._gui.approachType
188
189 @property
190 def arrivalRunway(self):
191 """Get the arrival runway."""
192 return self._gui.arrivalRunway
193
194 @property
195 def vref(self):
196 """Get the VRef speed of the flight."""
197 return self._gui.vref
198
199 @property
200 def tdRate(self):
201 """Get the touchdown rate if known, None otherwise."""
202 return self._tdRate
203
204 @property
205 def flightType(self):
206 """Get the type of the flight."""
207 return self._gui.flightType
208
209 @property
210 def online(self):
211 """Get whether the flight was an online flight."""
212 return self._gui.online
213
214 @property
215 def comments(self):
216 """Get the comments made by the pilot."""
217 return self._gui.comments
218
219 @property
220 def flightDefects(self):
221 """Get the flight defects reported by the pilot."""
222 return self._gui.flightDefects
223
224 @property
225 def delayCodes(self):
226 """Get the delay codes."""
227 return self._gui.delayCodes
228
229 @property
230 def speedInKnots(self):
231 """Determine if the speeds for the flight are to be expressed in
232 knots."""
233 return self.aircraft.speedInKnots if self.aircraft is not None \
234 else True
235
236 @property
237 def hasRTO(self):
238 """Determine if we have an RTO state."""
239 return self._rtoState is not None
240
241 @property
242 def rtoState(self):
243 """Get the RTO state."""
244 return self._rtoState
245
246 def handleState(self, oldState, currentState):
247 """Handle a new state information."""
248 self._updateFlownDistance(currentState)
249
250 self.endFuel = currentState.totalFuel
251 if self.startFuel is None:
252 self.startFuel = self.endFuel
253
254 self._soundScheduler.schedule(currentState,
255 self._pilotHotkeyPressed)
256 self._pilotHotkeyPressed = False
257
258 if self._checklistHotkeyPressed:
259 self._checklistScheduler.hotkeyPressed()
260 self._checklistHotkeyPressed = False
261
262 def setStage(self, timestamp, stage):
263 """Set the flight stage.
264
265 Returns if the stage has really changed."""
266 if stage!=self._stage:
267 self._stage = stage
268 self._gui.setStage(stage)
269 self.logger.stage(timestamp, stage)
270 if stage==const.STAGE_PUSHANDTAXI:
271 self.blockTimeStart = timestamp
272 elif stage==const.STAGE_TAKEOFF:
273 self.flightTimeStart = timestamp
274 elif stage==const.STAGE_TAXIAFTERLAND:
275 self.flightTimeEnd = timestamp
276 elif stage==const.STAGE_PARKING:
277 self.blockTimeEnd = timestamp
278 elif stage==const.STAGE_END:
279 with self._endCondition:
280 self._endCondition.notify()
281 return True
282 else:
283 return False
284
285 def handleFault(self, faultID, timestamp, what, score,
286 updatePrevious = False, updateID = None):
287 """Handle the given fault.
288
289 faultID as a unique ID for the given kind of fault. If another fault of
290 this ID has been reported earlier, it will be reported again only if
291 the score is greater than last time. This ID can be, e.g. the checker
292 the report comes from."""
293 id = self.logger.fault(faultID, timestamp, what, score,
294 updatePrevious = updatePrevious,
295 updateID = updateID)
296 self._gui.setRating(self.logger.getRating())
297 return id
298
299 def handleNoGo(self, faultID, timestamp, what, shortReason):
300 """Handle a No-Go fault."""
301 self.logger.noGo(faultID, timestamp, what)
302 self._gui.setNoGo(shortReason)
303
304 def setRTOState(self, state):
305 """Set the state that might be used as the RTO state.
306
307 If there has been no RTO state, the GUI is notified that from now on
308 the user may select to report an RTO."""
309 hadNoRTOState = self._rtoState is None
310
311 self._rtoState = state
312 self._rtoLogEntryID = \
313 SpeedChecker.logSpeedFault(self, state,
314 stage = const.STAGE_PUSHANDTAXI)
315
316 if hadNoRTOState:
317 self._gui.updateRTO()
318
319 def rtoToggled(self, indicated):
320 """Called when the user has toggled the RTO indication."""
321 if self._rtoState is not None:
322 if indicated:
323 self.logger.clearFault(self._rtoLogEntryID,
324 "RTO at %d knots" %
325 (self._rtoState.groundSpeed,))
326 self._gui.setRating(self.logger.getRating())
327 if self._stage == const.STAGE_PUSHANDTAXI:
328 self.setStage(self.aircraft.state.timestamp,
329 const.STAGE_RTO)
330 else:
331 SpeedChecker.logSpeedFault(self, self._rtoState,
332 stage = const.STAGE_PUSHANDTAXI,
333 updateID = self._rtoLogEntryID)
334 if self._stage == const.STAGE_RTO:
335 self.setStage(self.aircraft.state.timestamp,
336 const.STAGE_PUSHANDTAXI)
337
338 def flareStarted(self, flareStart, flareStartFS):
339 """Called when the flare time has started."""
340 self._flareStart = flareStart
341 self._flareStartFS = flareStartFS
342
343 def flareFinished(self, flareEnd, flareEndFS, tdRate):
344 """Called when the flare time has ended.
345
346 Return a tuple of the following items:
347 - a boolean indicating if FS time is used
348 - the flare time
349 """
350 self._tdRate = tdRate
351 if self.flareTimeFromFS:
352 return (True, flareEndFS - self._flareStartFS)
353 else:
354 return (False, flareEnd - self._flareStart)
355
356 def wait(self):
357 """Wait for the flight to end."""
358 with self._endCondition:
359 while self._stage!=const.STAGE_END:
360 self._endCondition.wait(1)
361
362 def getFleet(self, callback, force = False):
363 """Get the fleet and call the given callback."""
364 self._gui.getFleetAsync(callback = callback, force = force)
365
366 def pilotHotkeyPressed(self):
367 """Called when the pilot hotkey is pressed."""
368 self._pilotHotkeyPressed = True
369
370 def checklistHotkeyPressed(self):
371 """Called when the checklist hotkey is pressed."""
372 self._checklistHotkeyPressed = True
373
374 def speedFromKnots(self, knots):
375 """Convert the given speed value expressed in knots into the flight's
376 speed unit."""
377 return knots if self.speedInKnots else knots * const.KNOTSTOKMPH
378
379 def speedToKnots(self, speed):
380 """Convert the given speed expressed in the flight's speed unit into
381 knots."""
382 return speed if self.speedInKnots else speed * const.KMPHTOKNOTS
383
384 def getEnglishSpeedUnit(self):
385 """Get the English name of the speed unit used by the flight."""
386 return "knots" if self.speedInKnots else "km/h"
387
388 def getI18NSpeedUnit(self):
389 """Get the speed unit suffix for i18n message identifiers."""
390 return "_knots" if self.speedInKnots else "_kmph"
391
392 def logFuel(self, aircraftState):
393 """Log the amount of fuel"""
394 fuelStr = ""
395 for (tank, amount) in aircraftState.fuel:
396 if fuelStr: fuelStr += " - "
397 fuelStr += "%s=%.0f kg" % (const.fuelTank2logString(tank), amount)
398
399 self.logger.message(aircraftState.timestamp, "Fuel: " + fuelStr)
400 self.logger.message(aircraftState.timestamp,
401 "Total fuel: %.0f kg" % (aircraftState.totalFuel,))
402
403 def cruiseLevelChanged(self):
404 """Called when the cruise level hass changed."""
405 if self._stage in [const.STAGE_CRUISE, const.STAGE_DESCENT,
406 const.STAGE_LANDING]:
407 message = "Cruise altitude modified to %d feet" % \
408 (self.cruiseAltitude,)
409 self.logger.message(self.aircraft.timestamp, message)
410
411 def _updateFlownDistance(self, currentState):
412 """Update the flown distance."""
413 if not currentState.onTheGround:
414 updateData = False
415 if self._lastDistanceTime is None or \
416 self._previousLatitude is None or \
417 self._previousLongitude is None:
418 updateData = True
419 elif currentState.timestamp >= (self._lastDistanceTime + 30.0):
420 updateData = True
421 self.flownDistance += self._getDistance(currentState)
422
423 if updateData:
424 self._previousLatitude = currentState.latitude
425 self._previousLongitude = currentState.longitude
426 self._lastDistanceTime = currentState.timestamp
427 else:
428 if self._lastDistanceTime is not None and \
429 self._previousLatitude is not None and \
430 self._previousLongitude is not None:
431 self.flownDistance += self._getDistance(currentState)
432
433 self._lastDistanceTime = None
434
435 def _getDistance(self, currentState):
436 """Get the distance between the previous and the current state."""
437 return util.getDistCourse(self._previousLatitude, self._previousLongitude,
438 currentState.latitude, currentState.longitude)[0]
439
440#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.