source: src/mlx/acft.py

python3 tip
Last change on this file was 1137:87a29e5e4b2d, checked in by István Váradi <ivaradi@…>, 4 weeks ago

The G-load is measured during flare and the value at touchdown is logged (re #385)

File size: 51.2 KB
Line 
1
2from . import const
3from . import gates
4from . import checks
5from . import fs
6from .i18n import xstr
7from . import util
8
9import sys
10import time
11import traceback
12
13from collections import deque
14
15#---------------------------------------------------------------------------------------
16
17## @package mlx.acft
18#
19# The simulator-independent aircraft classes.
20#
21# This module contains the aircraft classes that contain some general data
22# about each aircraft type in the MAVA Fleet. The base class, \ref Aircraft
23# also implements some parts of the logging and traces some data. The classes
24# are also responsible for creating the \ref mlx.checks "checkers". Most of
25# them are created by the \ref Aircraft class' constructor, but there can be
26# type-specific differences. For example the lights are different for different
27# types, so there is a \ref Aircraft._appendLightsLoggers "function" which can
28# be reimplemented in child classes, if needed. This class maintains also the
29# \ref SmoothedValue "smoothed values" of the IAS and the VS and set these
30# values in the \ref mlx.fs.AircraftState "aircraft state" when it is received
31# from the simulator.
32
33#---------------------------------------------------------------------------------------
34
35# Derate type: no derate possible
36DERATE_NONE = 0
37
38# Derate type: Boeing, i.e. a percentage value.
39# For logging, the percentage value is expected as a string (i.e. whatever the
40# pilot enters into the text field).
41DERATE_BOEING = 1
42
43# Derate type: EPR, i.e. an EPR value.
44# For logging, the EPR value is expected as a string (i.e. whatever the pilot
45# enters into the text field).
46DERATE_EPR = 2
47
48# Derate type: Tupolev, i.e. nominal or takeoff
49# For logging, one of the DERATE_TUPOLEV_xxx values are expected.
50DERATE_TUPOLEV = 3
51
52# Tupolev derate value: nominal
53DERATE_TUPOLEV_NOMINAL = 1
54
55# Tupolev derate value: takeoff
56DERATE_TUPOLEV_TAKEOFF = 2
57
58# Derate type: BAe-146, i.e. enabled or not
59# For logging, a boolean is expected.
60DERATE_B462 = 4
61
62#---------------------------------------------------------------------------------------
63
64class SmoothedValue(object):
65 """A smoothed value."""
66 def __init__(self):
67 """Construct the value."""
68 self._deque = deque()
69 self._sum = 0
70
71 def add(self, length, value):
72 """Add the given value and smooth with the given length."""
73 dequeLength = len(self._deque)
74 while dequeLength>=length:
75 self._sum -= self._deque.popleft()
76 dequeLength -= 1
77
78 self._sum += value
79 self._deque.append(value)
80
81 def get(self):
82 """Get the average."""
83 return self._sum / len(self._deque)
84
85#---------------------------------------------------------------------------------------
86
87class SimBriefData(object):
88 """Data to be used when creating SimBrief briefings."""
89 def __init__(self, climbProfiles, cruiseProfiles, descentProfiles,
90 cruiseParameters = {}):
91 """Construct the SimBrief data with the given profiles.
92
93 cruiseParameters is a dictionary keyed by the cruise profile index. It
94 contains a tuple of:
95 - a boolean indicating if the parameter is mandatory,
96 - the name of the parameter for the SimBrief API
97 """
98 self.climbProfiles = climbProfiles
99 self.cruiseProfiles = cruiseProfiles
100 self.descentProfiles = descentProfiles
101 self.cruiseParameters = cruiseParameters
102
103#---------------------------------------------------------------------------------------
104
105class Aircraft(object):
106 """Base class for aircraft."""
107 @staticmethod
108 def create(flight, bookedFlight):
109 """Create an aircraft instance for the type in the given flight."""
110 acft = _classes[flight.aircraftType](flight)
111 acft.setBookedFlight(bookedFlight)
112 return acft
113
114 def __init__(self, flight, minLandingFuel = None,
115 recommendedLandingFuel = None):
116 """Construct the aircraft for the given type."""
117 self._flight = flight
118 self._minLandingFuel = minLandingFuel
119 self._recommendedLandingFuel = recommendedLandingFuel
120
121 self._name = None
122 self._modelName = None
123
124 self._aircraftState = None
125
126 self._maxVS = -10000.0
127 self._minVS = 10000.0
128
129 self._v1r2LineIndex = None
130 self._derateLineID = None
131 self._takeoffAntiIceLineID = None
132 self._vrefLineIndex = None
133 self._landingAntiIceLineID = None
134
135 self.humanWeight = 82.0
136
137 self.initialClimbSpeedAltitude = 1500
138 self.reverseMinSpeed = 50
139
140 self.hasStrobeLight = True
141
142 self.maxTakeOffPitch = 15.0
143 self.maxTouchDownPitch = 15.0
144 self.brakeCoolTime = 10.0
145
146 self.simBriefData = None
147
148 self._checkers = []
149
150 config = flight.config
151 # Loggers
152
153 self._checkers.append(checks.StageChecker())
154 self._checkers.append(checks.TakeOffLogger())
155
156 self._checkers.append(checks.AltimeterLogger())
157
158 self._ilsLogger = checks.ILSLogger()
159 self._checkers.append(self._ilsLogger)
160 self._nav1Logger = checks.NAV1Logger()
161 self._checkers.append(self._nav1Logger)
162 self._nav2Logger = checks.NAV2Logger()
163 self._checkers.append(self._nav2Logger)
164
165 self._adf1Logger = checks.ADF1Logger()
166 self._checkers.append(self._adf1Logger)
167 self._adf2Logger = checks.ADF2Logger()
168 self._checkers.append(self._adf2Logger)
169
170 self._checkers.append(checks.SquawkLogger())
171
172 self._appendLightsLoggers()
173
174 self._checkers.append(checks.FlapsLogger())
175
176 self._checkers.append(checks.GearsLogger())
177 self._checkers.append(checks.InitialClimbSpeedLogger())
178 self._checkers.append(checks.CruiseSpeedLogger())
179 self._checkers.append(checks.SpoilerLogger())
180 self._checkers.append(checks.APLogger())
181
182 if config.isMessageTypeFS(const.MESSAGETYPE_VISIBILITY):
183 self._checkers.append(checks.VisibilityChecker())
184
185 # FIXME: we should have a central data model object, and not collect
186 # the data from the GUI. However, some pieces of data (e.g. V-speeds,
187 # etc. that is entered into the GUI) *should* be a part of the GUI and
188 # queried from it, so the model should have a reference to the GUI as
189 # well and access such data via the GUI!
190 if config.onlineACARS and flight.loggedIn and not flight.entranceExam:
191 self._checkers.append(checks.ACARSSender(flight._gui))
192
193 # Fault checkers
194
195 self._appendLightsCheckers()
196
197 self._checkers.append(checks.TransponderChecker())
198
199 self._checkers.append(checks.BankChecker())
200
201 self._checkers.append(checks.FlapsRetractChecker())
202 self._checkers.append(checks.FlapsSpeedLimitChecker())
203
204 self._checkers.append(checks.GearsDownChecker())
205 self._checkers.append(checks.GearSpeedLimitChecker())
206
207 self._checkers.append(checks.GLoadChecker())
208
209 self._checkers.append(checks.MLWChecker())
210 self._checkers.append(checks.MTOWChecker())
211 self._checkers.append(checks.MZFWChecker())
212 self._checkers.append(checks.PayloadChecker())
213
214 self._checkers.append(checks.SpeedChecker())
215 self._checkers.append(checks.VSChecker())
216
217 timeout = 30.0 + config.realIASSmoothingLength - 1
218 self._checkers.append(checks.OverspeedChecker(faultTimeout = timeout))
219
220 self._checkers.append(checks.StallChecker())
221
222 self._checkers.append(checks.PitotChecker())
223
224 self._checkers.append(checks.ReverserLogger())
225 self._checkers.append(checks.ReverserChecker())
226
227 if flight.aircraftType is not None and config.enableApproachCallouts:
228 approachCallouts = flight.config.getApproachCallouts(flight.aircraftType)
229 if approachCallouts:
230 self._checkers.append(checks.ApproachCalloutsPlayer(approachCallouts))
231
232 self._smoothedIAS = SmoothedValue()
233 self._smoothedVS = SmoothedValue()
234
235 @property
236 def type(self):
237 """Get the type of the aircraft."""
238 return self._flight.aircraftType
239
240 @property
241 def flight(self):
242 """Get the flight the aircraft belongs to."""
243 return self._flight
244
245 @property
246 def minLandingFuel(self):
247 """Get the minimum acceptable amount of the landing fuel."""
248 return self._minLandingFuel
249
250 @property
251 def recommendedLandingFuel(self):
252 """Get the recommended amount of the landing fuel."""
253 return self._recommendedLandingFuel
254
255 @property
256 def logger(self):
257 """Get the logger to use for the aircraft."""
258 return self._flight.logger
259
260 @property
261 def state(self):
262 """Get the current aircraft state."""
263 return self._aircraftState
264
265 @property
266 def speedInKnots(self):
267 """Indicate if the speed is in knots.
268
269 This default implementation returns True."""
270 return True
271
272 @property
273 def aglInFeet(self):
274 """Indicate if AGL altitudes are to be logged in feet.
275
276 This default implementation returns True."""
277 return True
278
279 @property
280 def timestamp(self):
281 """Get the timestamp of the current state."""
282 return None if self._aircraftState is None \
283 else self._aircraftState.timestamp
284
285 @property
286 def derateType(self):
287 """Get the derate type for this aircraft.
288
289 This default implementation returns DERATE_NONE."""
290 return DERATE_NONE
291
292 def setBookedFlight(self, bookedFlight):
293 """Update the aircraft based on the booked flight data (e.g. tail number).
294
295 This default implementation does nothing."""
296
297 def getDerateLine(self, value):
298 """Get the log line for the given derate value.
299
300 It uses the the derate type and produces the standard message for
301 each. This children need not override it, although they can."""
302 dt = self.derateType
303
304 if dt==DERATE_BOEING:
305 return "Derate calculated by the pilot: %s %%" % \
306 ("-" if value is None else value,)
307 elif dt==DERATE_EPR:
308 return "EPR calculated by the pilot: %s" % \
309 ("-" if value is None else value,)
310 elif dt==DERATE_TUPOLEV:
311 return "Thrust setting calculated by the pilot: %s" % \
312 ("-" if value is None else
313 "nominal" if value==DERATE_TUPOLEV_NOMINAL else "takeoff",)
314 elif dt==DERATE_B462:
315 return "Derate setting: %s" % \
316 ("-" if value is None else "enabled" if value else "disabled",)
317 elif dt!=DERATE_NONE:
318 print("mlx.acft.getDerateLine: invalid derate type: " + dt)
319
320 return None
321
322 def getFlapsSpeedLimit(self, flaps):
323 """Get the speed limit for the given flaps setting."""
324 return self.flapSpeedLimits[flaps] if flaps in self.flapSpeedLimits \
325 else None
326
327 def modelChanged(self, timestamp, aircraftName, modelName):
328 """Called when the simulator's aircraft changes."""
329 self._name = aircraftName
330 self._modelName = modelName
331 if self._flight.stage is not None:
332 self._logNameAndModel(timestamp)
333
334 def handleState(self, aircraftState):
335 """Called when the state of the aircraft changes.
336
337 This is the function that the simulator calls directly with the new
338 state."""
339 try:
340 config = self._flight.config
341
342 self._smoothedIAS.add(config.realIASSmoothingLength, aircraftState.ias)
343 aircraftState.smoothedIAS = self._smoothedIAS.get()
344
345 self._smoothedVS.add(config.realVSSmoothingLength, aircraftState.vs)
346 aircraftState.smoothedVS = self._smoothedVS.get()
347
348 for checker in self._checkers:
349 try:
350 checker.check(self._flight, self, self._flight.logger,
351 self._aircraftState, aircraftState)
352 except:
353 print("Checker", checker, "failed", file=sys.stderr)
354 traceback.print_exc()
355
356 self._flight.handleState(self._aircraftState, aircraftState)
357
358 self._maxVS = max(self._maxVS, aircraftState.vs)
359 self._minVS = min(self._minVS, aircraftState.vs)
360 except:
361 print("Failed to handle the state", file=sys.stderr)
362 traceback.print_exc()
363 finally:
364 self._aircraftState = aircraftState
365
366 def setStage(self, aircraftState, newStage):
367 """Set the given stage as the new one and do whatever should be
368 done."""
369 if newStage==const.STAGE_BOARDING:
370 self._logNameAndModel(aircraftState.timestamp)
371
372 oldStage = self._flight.stage
373
374 if self._flight.setStage(aircraftState.timestamp, newStage):
375 if newStage==const.STAGE_PUSHANDTAXI:
376 self.logger.message(aircraftState.timestamp, "Block time start")
377 self._flight.logFuel(aircraftState)
378 self.logger.message(aircraftState.timestamp,
379 "ZFW: %.2f kg" % (aircraftState.zfw))
380 flight = self._flight
381 if flight.v1 is None or flight.vr is None or flight.v2 is None:
382 fs.sendMessage(const.MESSAGETYPE_HELP,
383 "Don't forget to set the takeoff V-speeds!",
384 5)
385 elif newStage==const.STAGE_TAKEOFF:
386 if oldStage == const.STAGE_RTO and self._flight.hasRTO:
387 rtoState = self._flight.rtoState
388 if (aircraftState.timestamp - rtoState.timestamp) < \
389 (self.brakeCoolTime * 60.0):
390 self.logger.fault("brakeCoolTime",
391 aircraftState.timestamp,
392 "Did not cool the brakes for at least %.f minutes after the RTO" % (self.brakeCoolTime,),
393 15.0)
394 self.logger.message(aircraftState.timestamp,
395 "Flight time start")
396 self.logger.message(aircraftState.timestamp,
397 "Takeoff weight: %.0f kg, MTOW: %.0f kg" % \
398 (aircraftState.grossWeight, self.mtow))
399 self._logQNH(aircraftState)
400 self.logger.message(aircraftState.timestamp,
401 "Wind %03.0f/%.0f" % \
402 (aircraftState.windDirection,
403 aircraftState.windSpeed))
404 self._logRadios(aircraftState)
405 self._logV1R2(aircraftState)
406 self._logDerate(aircraftState)
407 self._logTakeoffAntiIce(aircraftState)
408 elif newStage==const.STAGE_DESCENT or newStage==const.STAGE_LANDING:
409 self._logRadios(aircraftState)
410 if newStage==const.STAGE_LANDING:
411 self._logQNH(aircraftState)
412 elif newStage==const.STAGE_GOAROUND:
413 from .logger import Logger
414 self._flight.handleFault("goaround",
415 aircraftState.timestamp,
416 "Go-around detected, please, explain!",
417 Logger.NO_SCORE)
418 elif newStage==const.STAGE_TAXIAFTERLAND:
419 flight = self._flight
420 bookedFlight = flight.bookedFlight
421 config = flight.config
422 if config.onlineGateSystem and \
423 flight.loggedIn and \
424 not flight.entranceExam and \
425 bookedFlight.arrivalICAO=="LHBP" and \
426 config.isMessageTypeFS(const.MESSAGETYPE_GATE_SYSTEM):
427 self._flight.getFleet(callback = self._fleetRetrieved,
428 force = True)
429 self.logger.message(aircraftState.timestamp, "Flight time end")
430 self._flight.logFuel(aircraftState)
431 if self._minLandingFuel is not None and \
432 aircraftState.totalFuel<self._minLandingFuel:
433 self._flight.handleFault(self.__class__,
434 aircraftState.timestamp,
435 "The amount of the landing fuel is less than the minimum for this type: %ukgs (possible NO GO!)" %
436 (self._minLandingFuel,), 0)
437 self.logger.message(aircraftState.timestamp,
438 "Landing weight: %.0f kg, MLW: %.0f" % \
439 (aircraftState.grossWeight, self.mlw))
440 self.logger.message(aircraftState.timestamp,
441 "Vertical speed range: %.0f..%.0f feet/min" % \
442 (self._minVS, self._maxVS))
443 # elif newStage==const.STAGE_PARKING:
444 # self.logger.message(aircraftState.timestamp, "Block time end")
445 elif newStage==const.STAGE_END:
446 flightLength = self._flight.flightTimeEnd - self._flight.flightTimeStart
447 self.logger.message(aircraftState.timestamp, "Block time end")
448 self.logger.message(aircraftState.timestamp,
449 "Flight time: " +
450 util.getTimeIntervalString(flightLength))
451 self.logger.message(aircraftState.timestamp,
452 "Flown distance: %.2f NM" % \
453 (self._flight.flownDistance,))
454 blockLength = self._flight.blockTimeEnd - self._flight.blockTimeStart
455 self.logger.message(aircraftState.timestamp,
456 "Block time: " +
457 util.getTimeIntervalString(blockLength))
458
459 def prepareFlare(self):
460 """Called when it is detected that we will soon flare.
461
462 On the first call, it should start monitoring some parameters more
463 closely to determine flare time."""
464 self.flight.simulator.startFlare()
465
466 def flareStarted(self, windSpeed, windDirection, visibility,
467 flareStart, flareStartFS):
468 """Called when the flare has started."""
469 self.logger.message(self._aircraftState.timestamp, "The flare has begun")
470 self.logger.message(self._aircraftState.timestamp,
471 "Wind %03.0f/%.0f" % \
472 (windDirection, windSpeed))
473 self.logger.message(self._aircraftState.timestamp,
474 "Visibility: %.0f metres" % (visibility,))
475 self._logQNH(self._aircraftState)
476 self._logVRef()
477 self._logLandingAntiIce(self._aircraftState)
478 self.flight.flareStarted(flareStart, flareStartFS)
479 fs.sendMessage(const.MESSAGETYPE_INFORMATION, "Flare-time", 3)
480
481 def flareFinished(self, flareEnd, flareEndFS, tdRate, tdRateCalculatedByFS,
482 ias, pitch, bank, heading, gLoad):
483 """Called when the flare has finished."""
484 (flareTimeFromFS, flareTime) = self.flight.flareFinished(flareEnd,
485 flareEndFS,
486 tdRate)
487 self.logger.message(self._aircraftState.timestamp,
488 "Flaretime: %.3f (from %s)" % \
489 (flareTime,
490 "the simulator" if flareTimeFromFS else "real time",))
491 self.logger.message(self._aircraftState.timestamp,
492 "Touchdown rate: %.0f feet/min" % (tdRate,))
493 self.logger.message(self._aircraftState.timestamp,
494 "Touchdown rate was calculated by the %s" % \
495 ("simulator" if tdRateCalculatedByFS else "logger",))
496 self.logger.message(self._aircraftState.timestamp,
497 "Touchdown G-load: %.2f" % (gLoad,))
498 flight = self._flight
499 self.logger.message(self._aircraftState.timestamp,
500 "Touchdown speed: %.0f %s" % \
501 (flight.speedFromKnots(ias),
502 flight.getEnglishSpeedUnit()))
503 self.logger.message(self._aircraftState.timestamp,
504 "Touchdown pitch: %.1f degrees" % (pitch,))
505 self.logger.message(self._aircraftState.timestamp,
506 "Touchdown bank: %.1f degrees" % (bank,))
507 self.logger.message(self._aircraftState.timestamp,
508 "Touchdown heading: %03.0f degrees" % (heading,))
509 self.logger.message(self._aircraftState.timestamp,
510 "CG: %.1f%%" % \
511 (self._aircraftState.cog*100.0,))
512
513 if abs(pitch)>self.maxTouchDownPitch:
514 self._flight.handleNoGo("TDPitch", self._aircraftState.timestamp,
515 "Touchdown pitch higher than aircraft maximum (%.2f)" % \
516 (self.maxTouchDownPitch,),
517 "TD TAILSTRIKE NO GO")
518
519 def cancelFlare(self):
520 """Cancel flare, if it has started."""
521 self.flight.simulator.cancelFlare()
522
523 def checkFlightEnd(self, aircraftState):
524 """Check if the end of the flight has arrived.
525
526 This default implementation checks the N1 values, but for
527 piston-powered aircraft you need to check the RPMs."""
528 if aircraftState.n1 is not None:
529 for n1 in aircraftState.n1:
530 if n1 is not None and n1>=0.5: return False
531 return True
532
533 def updateV1R2(self):
534 """Update the V1, Vr and V2 values from the flight, if the these values
535 have already been logged."""
536 if self._v1r2LineIndex is not None:
537 self._logV1R2()
538
539 def updateDerate(self):
540 """Update the derate value from the flight, if the these values
541 have already been logged."""
542 if self._derateLineID is not None:
543 self._logDerate()
544
545 def updateTakeoffAntiIce(self):
546 """Update the take-off anti-ice setting."""
547 if self._takeoffAntiIceLineID is not None:
548 self._logTakeoffAntiIce()
549
550 def _appendLightsLoggers(self):
551 """Append the loggers needed for the lights.
552
553 This default implementation adds the loggers for the anti-collision
554 lights, the landing lights, the strobe lights and the NAV lights."""
555 self._checkers.append(checks.AnticollisionLightsLogger())
556 self._checkers.append(checks.LandingLightsLogger())
557 self._checkers.append(checks.StrobeLightsLogger())
558 self._checkers.append(checks.NavLightsLogger())
559
560 def _appendLightsCheckers(self):
561 """Append the checkers needed for the lights.
562
563 This default implementation adds the checkers for the anti-collision
564 lights, the landing lights, the strobe lights and the NAV lights."""
565 self._checkers.append(checks.AntiCollisionLightsChecker())
566 self._checkers.append(checks.LandingLightsChecker())
567 self._checkers.append(checks.NavLightsChecker())
568 self._checkers.append(checks.StrobeLightsChecker())
569
570 def _speedToLog(self, speed):
571 """Convert the given speed (being either None or expressed in the
572 flight's speed unit into a string."""
573 if speed is None:
574 return "-"
575 else:
576 return str(speed) + " " + self._flight.getEnglishSpeedUnit()
577
578 def _logV1R2(self, state = None):
579 """Log the V1, Vr and V2 value either newly, or by updating the
580 corresponding line."""
581 message = "Calc. TO speeds: V1: %s, VR: %s, V2: %s" % \
582 (self._speedToLog(self._flight.v1),
583 self._speedToLog(self._flight.vr),
584 self._speedToLog(self._flight.v2))
585
586 if self._v1r2LineIndex is None:
587 if state is None:
588 state = self._aircraftState
589 self._v1r2LineIndex = \
590 self.logger.message(state.timestamp, message)
591 else:
592 self.logger.updateLine(self._v1r2LineIndex, message)
593
594 def _logDerate(self, state = None):
595 """Log the derate values either newly or by updating the corresponding
596 line."""
597 dt = self.derateType
598 if dt==DERATE_NONE:
599 return
600
601 message = self.getDerateLine(self._flight.derate)
602 if message is not None:
603 if self._derateLineID is None:
604 if state is None:
605 state = self._aircraftState
606 self._derateLineID = \
607 self.logger.message(state.timestamp, message)
608 else:
609 self.logger.updateLine(self._derateLineID, message)
610
611 def _logTakeoffAntiIce(self, state = None):
612 """Log the take-off anti-ice setting either newly or by updating the
613 corresponding line."""
614 antiIceOn = self._flight.takeoffAntiIceOn
615 if state is not None:
616 antiIceOn = antiIceOn or state.antiIceOn is True
617 self._flight.takeoffAntiIceOn = antiIceOn
618
619 message = "Anti-ice was turned %s" % \
620 ("ON" if antiIceOn else "OFF")
621
622 if self._takeoffAntiIceLineID is None:
623 if state is None:
624 state = self._aircraftState
625 self._takeoffAntiIceLineID = \
626 self.logger.message(state.timestamp, message)
627 else:
628 self.logger.updateLine(self._takeoffAntiIceLineID, message)
629
630 def updateVRef(self):
631 """Update the Vref value from the flight, if the Vref value has already
632 been logged."""
633 if self._vrefLineIndex is not None:
634 self._logVRef()
635
636 def _logVRef(self):
637 """Log the Vref value either newly, or by updating the corresponding
638 line."""
639 message = "VRef speed calculated by the pilot: %s" % \
640 (self._speedToLog(self._flight.vref),)
641 if self._vrefLineIndex is None:
642 self._vrefLineIndex = \
643 self.logger.message(self._aircraftState.timestamp, message)
644 else:
645 self.logger.updateLine(self._vrefLineIndex, message)
646
647 def updateLandingAntiIce(self):
648 """Update the landing anti-ice setting."""
649 if self._landingAntiIceLineID is not None:
650 self._logLandingAntiIce()
651
652 def _logLandingAntiIce(self, state = None):
653 """Log the landing anti-ice setting either newly or by updating the
654 corresponding line."""
655 antiIceOn = self._flight.landingAntiIceOn
656 if state is not None:
657 antiIceOn = antiIceOn or state.antiIceOn is True
658 self._flight.landingAntiIceOn = antiIceOn
659
660 message = "Anti-ice was turned %s" % \
661 ("ON" if antiIceOn else "OFF")
662
663 if self._landingAntiIceLineID is None:
664 if state is None:
665 state = self._aircraftState
666 self._landingAntiIceLineID = \
667 self.logger.message(state.timestamp, message)
668 else:
669 self.logger.updateLine(self._landingAntiIceLineID, message)
670
671 def _fleetRetrieved(self, fleet):
672 """Callback for the fleet retrieval result."""
673 if fleet is not None:
674 gateList = ""
675 occupiedGateNumbers = fleet.getOccupiedGateNumbers()
676 for gate in gates.lhbpGates.gates:
677 if gate.isAvailable(gates.lhbpGates, occupiedGateNumbers):
678 if gateList: gateList += ", "
679 gateList += gate.number
680 fs.sendMessage(const.MESSAGETYPE_GATE_SYSTEM,
681 "Free gates: " + gateList, 20)
682
683
684 def _logRadios(self, aircraftState):
685 """Log the radios from the given aircraft state."""
686 flight = self._flight
687 logger = flight.logger
688
689 self._ilsLogger.forceLog(flight, logger, aircraftState)
690 self._nav1Logger.forceLog(flight, logger, aircraftState)
691 self._nav2Logger.forceLog(flight, logger, aircraftState)
692 self._adf1Logger.forceLog(flight, logger, aircraftState)
693 self._adf2Logger.forceLog(flight, logger, aircraftState)
694
695 def _logQNH(self, aircraftState):
696 """Log the current QNH along with the altimeter setting."""
697 self.logger.message(aircraftState.timestamp,
698 "QNH: %.2f hPa, altimeter: %.2f hPa" % \
699 (aircraftState.qnh, aircraftState.altimeter))
700
701 def _logNameAndModel(self, timestamp):
702 """Log the aircraft's name and model with taking the timestamp from the
703 given state."""
704 self._flight.logger.message(timestamp,
705 "Aircraft: name='%s', model='%s'" % \
706 (self._name, self._modelName))
707
708#---------------------------------------------------------------------------------------
709
710class Boeing737(Aircraft):
711 """Base class for the various aircraft in the Boeing 737 family.
712
713 The aircraft type-specific values in the aircraft state have the following
714 structure:
715 - fuel: left, centre, right
716 - n1: left, right
717 - reverser: left, right"""
718 def __init__(self, flight, minLandingFuel = 2500,
719 recommendedLandingFuel = 3500):
720 super(Boeing737, self).__init__(flight,
721 minLandingFuel = minLandingFuel,
722 recommendedLandingFuel =
723 recommendedLandingFuel)
724
725 self.gearSpeedLimit = 270
726 self.flapSpeedLimits = { 1 : 260,
727 2 : 260,
728 5 : 250,
729 10 : 210,
730 15 : 200,
731 25 : 190,
732 30 : 175,
733 40 : 162 }
734
735 @property
736 def derateType(self):
737 """Get the derate type for this type."""
738 return DERATE_BOEING
739
740#---------------------------------------------------------------------------------------
741
742class B736(Boeing737):
743 """Boeing 737-600 aircraft."""
744 dow = 38307
745
746 def __init__(self, flight):
747 super(B736, self).__init__(flight)
748 self.mtow = 58328
749 self.mlw = 54657
750 self.mzfw = 51482
751 self.maxTakeOffPitch = 16.2
752 self.maxTouchDownPitch = 14.7
753 self.simBriefData = SimBriefData(["250/280/78"],
754 ["CI", "M75", "M78", "M79", "M80", "LRC"],
755 ["78/280/250"],
756 cruiseParameters = {0: (False, "civalue")})
757
758#---------------------------------------------------------------------------------------
759
760class B737(Boeing737):
761 """Boeing 737-700 aircraft."""
762 dow = 39250
763
764 def __init__(self, flight):
765 super(B737, self).__init__(flight)
766 self.mtow = 61410
767 self.mlw = 58059
768 self.mzfw = 54657
769 self.maxTakeOffPitch = 14.7
770 self.maxTouchDownPitch = 13.2
771 self.simBriefData = SimBriefData(["250/280/78"],
772 ["CI", "M75", "M78", "M79", "M80", "LRC"],
773 ["78/280/250", "78/250/250"],
774 cruiseParameters = {0: (False, "civalue")})
775
776#---------------------------------------------------------------------------------------
777
778class B738(Boeing737):
779 """Boeing 737-800 aircraft."""
780 dow = 42690
781
782 def __init__(self, flight):
783 super(B738, self).__init__(flight)
784 self.mtow = 71791
785 self.mlw = 65317
786 self.mzfw = 61688
787 self.maxTakeOffPitch = 11
788 self.maxTouchDownPitch = 9.5
789 self.simBriefData = SimBriefData(["250/280/78"],
790 ["CI", "M76", "M78", "M79", "M80", "LRC"],
791 ["78/280/250", "78/250/250"],
792 cruiseParameters = {0: (False, "civalue")})
793
794#---------------------------------------------------------------------------------------
795
796class B738Charter(B738):
797 """Boeing 737-800 aircraft used for charters."""
798 def __init__(self, flight):
799 super(B738Charter, self).__init__(flight)
800 self.mtow = 77791
801 self.simBriefData = SimBriefData(["AUTO"],
802 ["280/M74"],
803 ["AUTO"])
804
805#---------------------------------------------------------------------------------------
806
807class Boeing737CL(Boeing737):
808 """Base class for the various aircraft in the Boeing 737 Classic family."""
809 def __init__(self, flight):
810 super(Boeing737CL, self).__init__(flight, minLandingFuel = 3500,
811 recommendedLandingFuel = None)
812
813#---------------------------------------------------------------------------------------
814
815class B732(Boeing737CL):
816 """Boeing 737-200 aircraft."""
817 dow = 27646
818
819 def __init__(self, flight):
820 super(B732, self).__init__(flight)
821 self.mtow = 52390
822 self.mlw = 46720
823 self.mzfw = 43091
824 self.maxTakeOffPitch = 15.5
825 self.maxTouchDownPitch = 15.5
826 self.simBriefData = SimBriefData(["250/280/70"],
827 ["LRC", "M72", "M73", "M74"],
828 ["74/320/250"])
829
830#---------------------------------------------------------------------------------------
831
832class B733(Boeing737CL):
833 """Boeing 737-300 aircraft."""
834 dow = 32900
835
836 def __init__(self, flight):
837 super(B733, self).__init__(flight)
838 self.mtow = 56472
839 self.mlw = 51710
840 self.mzfw = 47625
841 self.maxTakeOffPitch = 13.4
842 self.maxTouchDownPitch = 12.0
843 self.simBriefData = SimBriefData(["250/280/74"],
844 ["CI", "M74", "M76", "M78", "LRC"],
845 ["74/280/250"],
846 cruiseParameters = {0: (False, "civalue")})
847
848#---------------------------------------------------------------------------------------
849
850class B734(Boeing737CL):
851 """Boeing 737-400 aircraft."""
852 def __init__(self, flight):
853 super(B734, self).__init__(flight)
854 self.dow = 35100
855 self.mtow = 62822
856 self.mlw = 54885
857 self.mzfw = 51256
858 self.maxTakeOffPitch = 11.4
859 self.maxTouchDownPitch = 10
860 self.simBriefData = SimBriefData(["250/280/74"],
861 ["CI", "M74", "M76", "M78", "LRC"],
862 ["74/280/250"],
863 cruiseParameters = {0: (False, "civalue")})
864
865#---------------------------------------------------------------------------------------
866
867class B735(Boeing737CL):
868 """Boeing 737-500 aircraft."""
869 dow = 31900
870
871 def __init__(self, flight):
872 super(B735, self).__init__(flight)
873 self.mtow = 62823
874 self.mlw = 49895
875 self.mzfw = 46720
876 self.maxTakeOffPitch = 14.7
877 self.maxTouchDownPitch = 13.2
878 self.simBriefData = SimBriefData(["250/280/74"],
879 ["CI", "M74", "M76", "M78", "LRC"],
880 ["74/280/250"],
881 cruiseParameters = {0: (False, "civalue")})
882
883#---------------------------------------------------------------------------------------
884
885class DH8D(Aircraft):
886 """Bombardier Dash-8 Q400 aircraft.
887
888 The aircraft type-specific values in the aircraft state have the following
889 structure:
890 - fuel: left, right
891 - n1: left, right
892 - reverser: left, right."""
893 dow = 18508
894
895 def __init__(self, flight):
896 super(DH8D, self).__init__(flight, minLandingFuel = 2000)
897 self.mtow = 29574
898 self.mlw = 28123
899 self.mzfw = 26308
900 self.gearSpeedLimit = 215
901 self.flapSpeedLimits = { 5 : 200,
902 10 : 181,
903 15 : 172,
904 35 : 158 }
905 self.maxTakeOffPitch = 8.0
906 self.maxTouchDownPitch = 7.0
907 self.simBriefData = SimBriefData(["I-900", "II-900", "III-900",
908 "I-850", "II-850", "III-850"],
909 ["MCR", "ISC", "LRC", "HSC"],
910 ["I-850", "II-850", "III-850"])
911
912#---------------------------------------------------------------------------------------
913
914class Boeing767(Aircraft):
915 """Base class for the various aircraft in the Boeing 767 family.
916
917 The aircraft type-specific values in the aircraft state have the following
918 structure:
919 - fuel: left, centre, right
920 - n1: left, right
921 - reverser: left, right"""
922
923 def __init__(self, flight, minLandingFuel = 7000):
924 super(Boeing767, self).__init__(flight, minLandingFuel = minLandingFuel)
925 self.gearSpeedLimit = 270
926 self.flapSpeedLimits = { 1 : 255,
927 5 : 235,
928 10 : 215,
929 20 : 215,
930 25 : 185,
931 30 : 175 }
932
933 @property
934 def derateType(self):
935 """Get the derate type for this type."""
936 return DERATE_BOEING
937
938#---------------------------------------------------------------------------------------
939
940class B762(Boeing767):
941 """Boeing 767-200 aircraft."""
942 dow = 84507
943
944 def __init__(self, flight):
945 super(B762, self).__init__(flight)
946 self.mtow = 159210
947 self.mlw = 126098
948 self.mzfw = 114758
949 self.maxTakeOffPitch = 13.1
950 self.maxTouchDownPitch = 11.6
951 self.simBriefData = SimBriefData(["250/290/78"],
952 ["CI", "M76", "M78", "M80", "M82",
953 "M84", "M85", "LRC"],
954 ["78/290/250"],
955 cruiseParameters = {0: (False, "civalue")})
956
957#---------------------------------------------------------------------------------------
958
959class B763(Boeing767):
960 """Boeing 767-300 aircraft."""
961 dow = 91311
962
963 def __init__(self, flight):
964 super(B763, self).__init__(flight)
965 self.mtow = 181436
966 self.mlw = 137892
967 self.mzfw = 114758
968 self.maxTakeOffPitch = 9.6
969 self.maxTouchDownPitch = 8.1
970 self.simBriefData = SimBriefData(["250/290/78"],
971 ["CI", "M76", "M78", "M80", "M82", "M84", "LRC"],
972 ["78/290/250"],
973 cruiseParameters = {0: (False, "civalue")})
974
975 def setBookedFlight(self, bookedFlight):
976 """Update the aircraft based on the booked flight data (e.g. tail number)."""
977 if bookedFlight.tailNumber=="HA-LHD":
978 self.mtow = 159210
979 self.mlw = 126098
980
981#---------------------------------------------------------------------------------------
982
983class CRJ2(Aircraft):
984 """Bombardier CRJ-200 aircraft.
985
986 The aircraft type-specific values in the aircraft state have the following
987 structure:
988 - fuel: left, centre, right
989 - n1: left, right
990 - reverser: left, right."""
991 dow = 14549
992
993 def __init__(self, flight):
994 super(CRJ2, self).__init__(flight, minLandingFuel = 1000)
995 self.mtow = 22995
996 self.mlw = 21319
997 self.mzfw = 19958
998 self.gearSpeedLimit = 240
999 self.flapSpeedLimits = { 8 : 260,
1000 20 : 220,
1001 30 : 190,
1002 45 : 175 }
1003 self.maxTakeOffPitch = 18.0
1004 self.maxTouchDownPitch = 18.0
1005 self.simBriefData = SimBriefData(["250/70", "290/74"],
1006 ["CI", "LRC", "M70", "M72", "M74", "M77", "M80"],
1007 ["74/290/250", "77/320/250"],
1008 cruiseParameters = {0: (False, "civalue")})
1009
1010#---------------------------------------------------------------------------------------
1011
1012class F70(Aircraft):
1013 """Fokker 70 aircraft.
1014
1015 The aircraft type-specific values in the aircraft state have the following
1016 structure:
1017 - fuel: left, centre, right
1018 - n1: left, right
1019 - reverser: left, right."""
1020 dow = 24283
1021
1022 def __init__(self, flight):
1023 super(F70, self).__init__(flight, minLandingFuel = 1900)
1024 self.mtow = 38100 # FIXME: differentiate by registration number,
1025 # MTOW of HA-LMF: 41955
1026 self.mlw = 36740
1027 self.mzfw = 32655
1028 self.gearSpeedLimit = 200
1029 self.flapSpeedLimits = { 8 : 250,
1030 15 : 220,
1031 25 : 220,
1032 42 : 180 }
1033 self.reverseMinSpeed = 50
1034 self.maxTakeOffPitch = 16.0
1035 self.maxTouchDownPitch = 16.0
1036 self.simBriefData = SimBriefData(["250/280/70"],
1037 ["M70", "LRC"],
1038 ["70/280/250"])
1039
1040 @property
1041 def derateType(self):
1042 """Get the derate type for this type."""
1043 return DERATE_EPR
1044
1045#---------------------------------------------------------------------------------------
1046
1047class DC3(Aircraft):
1048 """Lisunov Li-2 (DC-3) aircraft.
1049
1050 The aircraft type-specific values in the aircraft state have the following
1051 structure:
1052 - fuel: left aux, left, right, right aux
1053 - rpm: left, right
1054 - reverser: left, right."""
1055 dow = 8627
1056
1057 def __init__(self, flight):
1058 super(DC3, self).__init__(flight)
1059 self.mtow = 11884
1060 self.mlw = 11793
1061 self.mzfw = 11780
1062 self.gearSpeedLimit = 148
1063 self.flapSpeedLimits = { 15 : 135,
1064 30 : 99,
1065 45 : 97 }
1066
1067 def _checkFlightEnd(self, aircraftState):
1068 """Check if the end of the flight has arrived.
1069
1070 This implementation checks the RPM values to be 0."""
1071 for rpm in aircraftState.rpm:
1072 if rpm>0: return False
1073 return True
1074
1075#---------------------------------------------------------------------------------------
1076
1077class T134(Aircraft):
1078 """Tupolev Tu-134 aircraft.
1079
1080 The aircraft type-specific values in the aircraft state have the following
1081 structure:
1082 - fuel: left tip, left aux, centre, right aux, right tip, external 1,
1083 external 2
1084 - n1: left, right
1085 - reverser: left, right."""
1086 dow = 29500
1087
1088 def __init__(self, flight):
1089 super(T134, self).__init__(flight, minLandingFuel = 3000)
1090 self.mtow = 49000
1091 self.mlw = 43000
1092 self.mzfw = 38500
1093 self.gearSpeedLimit = 216
1094 self.flapSpeedLimits = { 10 : 450,
1095 20 : 400,
1096 30 : 300 }
1097 self.reverseMinSpeed = 50
1098
1099 self.hasStrobeLight = False
1100
1101 self.maxTakeOffPitch = 16.0
1102 self.maxTouchDownPitch = 16.0
1103
1104 @property
1105 def derateType(self):
1106 """Get the derate type for this type."""
1107 return DERATE_TUPOLEV
1108
1109 @property
1110 def speedInKnots(self):
1111 """Indicate if the speed is in knots."""
1112 return False
1113
1114 @property
1115 def aglInFeet(self):
1116 """Indicate if AGL altituedes are in feet."""
1117 return False
1118
1119 def _appendLightsLoggers(self):
1120 """Append the loggers needed for the lights."""
1121 self._checkers.append(checks.AnticollisionLightsLogger())
1122 self._checkers.append(checks.LandingLightsLogger())
1123 self._checkers.append(checks.NavLightsLogger())
1124
1125 def _appendLightsCheckers(self):
1126 """Append the checkers needed for the lights."""
1127 self._checkers.append(checks.TupolevAntiCollisionLightsChecker())
1128 self._checkers.append(checks.TupolevLandingLightsChecker())
1129 self._checkers.append(checks.LandingLightsChecker())
1130 self._checkers.append(checks.NavLightsChecker())
1131
1132#---------------------------------------------------------------------------------------
1133
1134class T154(Aircraft):
1135 """Tupolev Tu-154 aircraft.
1136
1137 The aircraft type-specific values in the aircraft state have the following
1138 structure:
1139 - fuel: left aux, left, centre, centre 2, right, right aux
1140 - n1: left, centre, right
1141 - reverser: left, right"""
1142 dow = 53259
1143
1144 def __init__(self, flight):
1145 super(T154, self).__init__(flight, minLandingFuel = 5000)
1146 self.mtow = 98000
1147 self.mlw = 78000
1148 self.mzfw = 72000
1149 self.gearSpeedLimit = 216
1150 self.flapSpeedLimits = { 15 : 227,
1151 28 : 194,
1152 45 : 162 }
1153 self.reverseMinSpeed = 50
1154
1155 self.hasStrobeLight = False
1156
1157 self.maxTakeOffPitch = 16.0
1158 self.maxTouchDownPitch = 16.0
1159 self.simBriefData = SimBriefData(["AUTO"],
1160 ["AUTO"],
1161 ["AUTO"])
1162
1163 @property
1164 def speedInKnots(self):
1165 """Indicate if the speed is in knots."""
1166 return False
1167
1168 @property
1169 def aglInFeet(self):
1170 """Indicate if AGL altituedes are in feet."""
1171 return False
1172
1173 @property
1174 def derateType(self):
1175 """Get the derate type for this type."""
1176 return DERATE_TUPOLEV
1177
1178 def setBookedFlight(self, bookedFlight):
1179 """Update the aircraft based on the booked flight data (e.g. tail number)."""
1180 if bookedFlight.tailNumber in ["HA-LCM", "HA-LCN", "HA-LCO", "HA-LCP",
1181 "HA-LCR", "HA-LCU", "HA-LCV"]:
1182 self.mtow = 100000
1183 self.mlw = 80000
1184 elif bookedFlight.tailNumber=="HA-LCX":
1185 self.mtow = 100000
1186 self.mlw = 80000
1187 self.mzfw = 74000
1188
1189 self.flapSpeedLimits = { 15 : 227,
1190 28 : 194,
1191 36 : 178,
1192 45 : 162 }
1193
1194 def _appendLightsLoggers(self):
1195 """Append the loggers needed for the lights."""
1196 self._checkers.append(checks.AnticollisionLightsLogger())
1197 self._checkers.append(checks.LandingLightsLogger())
1198 self._checkers.append(checks.NavLightsLogger())
1199
1200 def _appendLightsCheckers(self):
1201 """Append the checkers needed for the lights."""
1202 self._checkers.append(checks.AntiCollisionLightsChecker())
1203 self._checkers.append(checks.TupolevLandingLightsChecker())
1204 self._checkers.append(checks.LandingLightsChecker())
1205 self._checkers.append(checks.NavLightsChecker())
1206
1207#---------------------------------------------------------------------------------------
1208
1209class YK40(Aircraft):
1210 """Yakovlev Yak-40 aircraft.
1211
1212 The aircraft type-specific values in the aircraft state have the following
1213 structure:
1214 - fuel: left, right
1215 - n1: left, right
1216 - reverser: left, right"""
1217 dow = 9400
1218
1219 def __init__(self, flight):
1220 super(YK40, self).__init__(flight)
1221 self.mtow = 17200
1222 self.mlw = 16800
1223 self.mzfw = 12100
1224 self.gearSpeedLimit = 165
1225 self.flapSpeedLimits = { 20 : 165,
1226 35 : 135 }
1227
1228 self.hasStrobeLight = False
1229
1230 @property
1231 def speedInKnots(self):
1232 """Indicate if the speed is in knots."""
1233 return False
1234
1235 @property
1236 def aglInFeet(self):
1237 """Indicate if AGL altituedes are in feet."""
1238 return False
1239
1240 @property
1241 def derateType(self):
1242 """Get the derate type for this type."""
1243 return DERATE_TUPOLEV
1244
1245 def _appendLightsLoggers(self):
1246 """Append the loggers needed for the lights."""
1247 self._checkers.append(checks.AnticollisionLightsLogger())
1248 self._checkers.append(checks.LandingLightsLogger())
1249 self._checkers.append(checks.NavLightsLogger())
1250
1251 def _appendLightsCheckers(self):
1252 """Append the checkers needed for the lights."""
1253 self._checkers.append(checks.AntiCollisionLightsChecker())
1254 self._checkers.append(checks.LandingLightsChecker())
1255 self._checkers.append(checks.NavLightsChecker())
1256
1257#---------------------------------------------------------------------------------------
1258
1259class B462(Aircraft):
1260 """British Aerospace BAe-146 aircraft.
1261
1262 The aircraft type-specific values in the aircraft state have the following
1263 structure:
1264 - fuel: left, centre, right
1265 - n1: left outer, left inner, right inner, right outer
1266 - reverser: empty (the plane has no reversers)"""
1267 dow = 25706
1268
1269 def __init__(self, flight):
1270 super(B462, self).__init__(flight)
1271 self.mtow = 43998
1272 self.mlw = 38599
1273 self.mzfw = 33792
1274 self.gearSpeedLimit = 210
1275 self.flapSpeedLimits = { 18 : 217,
1276 24 : 180,
1277 30 : 170,
1278 33 : 150 }
1279 self.simBriefData = SimBriefData(["HighSpeed", "LongRange"],
1280 ["M70", "MCR", "LRC"],
1281 ["HighSpeed", "LongRange"])
1282 @property
1283 def derateType(self):
1284 """Get the derate type for this type."""
1285 return DERATE_B462
1286
1287#---------------------------------------------------------------------------------------
1288
1289mostFuelTanks = [const.FUELTANK_LEFT_TIP, const.FUELTANK_EXTERNAL1,
1290 const.FUELTANK_LEFT_AUX,
1291 const.FUELTANK_CENTRE,
1292 const.FUELTANK_RIGHT_AUX,
1293 const.FUELTANK_EXTERNAL2, const.FUELTANK_RIGHT_TIP]
1294
1295#---------------------------------------------------------------------------------------
1296
1297_classes = { const.AIRCRAFT_B736 : B736,
1298 const.AIRCRAFT_B737 : B737,
1299 const.AIRCRAFT_B738 : B738,
1300 const.AIRCRAFT_B738C : B738Charter,
1301 const.AIRCRAFT_B732 : B732,
1302 const.AIRCRAFT_B733 : B733,
1303 const.AIRCRAFT_B734 : B734,
1304 const.AIRCRAFT_B735 : B735,
1305 const.AIRCRAFT_DH8D : DH8D,
1306 const.AIRCRAFT_B762 : B762,
1307 const.AIRCRAFT_B763 : B763,
1308 const.AIRCRAFT_CRJ2 : CRJ2,
1309 const.AIRCRAFT_F70 : F70,
1310 const.AIRCRAFT_DC3 : DC3,
1311 const.AIRCRAFT_T134 : T134,
1312 const.AIRCRAFT_T154 : T154,
1313 const.AIRCRAFT_YK40 : YK40,
1314 const.AIRCRAFT_B462 : B462 }
1315
1316#---------------------------------------------------------------------------------------
1317
1318def getClass(aircraftType):
1319 """Get the class representing the given aircraft types"""
1320 return _classes[aircraftType]
1321
1322#---------------------------------------------------------------------------------------
1323
1324if __name__ == "__main__":
1325 value = SmoothedValue()
1326
1327 print("Adding 1, 12.0")
1328 value.add(1, 12.0)
1329 print(value.get())
1330
1331 print("Adding 1, 15.0")
1332 value.add(1, 15.0)
1333 print(value.get())
1334
1335 print("Adding 2, 18.0")
1336 value.add(2, 18.0)
1337 print(value.get())
1338
1339 print("Adding 2, 20.0")
1340 value.add(2, 20.0)
1341 print(value.get())
1342
1343 print("Adding 5, 22.0")
1344 value.add(5, 22.0)
1345 print(value.get())
1346
1347 print("Adding 5, 25.0")
1348 value.add(5, 25.0)
1349 print(value.get())
1350
1351 print("Adding 5, 29.0")
1352 value.add(5, 29.0)
1353 print(value.get())
1354
1355 print("Adding 5, 21.0")
1356 value.add(5, 21.0)
1357 print(value.get())
1358
1359 print("Adding 5, 26.0")
1360 value.add(5, 26.0)
1361 print(value.get())
1362
1363 print("Adding 2, 30.0")
1364 value.add(2, 30.0)
1365 print(value.get())
1366
1367 print("Adding 2, 55.0")
1368 value.add(2, 55.0)
1369 print(value.get())
1370
1371#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.