Example #1
0
 def resetData(self):
     # FPL.TIME_OF_DEP
     dep = self.fpl[FPL.TIME_OF_DEP]
     self.depTime_enable.setChecked(dep != None)
     if dep == None:
         dep = now()
     self.depTime_edit.setDateTime(
         QDateTime(dep.year, dep.month, dep.day, dep.hour, dep.minute))
     # FPL.EET
     eet = self.fpl[FPL.EET]
     self.EET_enable.setChecked(eet != None)
     if eet != None:
         minutes = int(eet.total_seconds() / 60 + .5)
         self.EETh_edit.setValue(minutes // 60)
         self.EETmin_edit.setValue(minutes % 60)
     #	ICAO_ALT
     self.altAirportPicker_widget.setEditText(
         some(self.fpl[FPL.ICAO_ALT], ''))
     #	SOULS
     souls = self.fpl[FPL.SOULS]
     self.soulsOnBoard_enable.setChecked(souls != None)
     if souls != None:
         self.soulsOnBoard_edit.setValue(souls)
     # ONLINE COMMENTS
     self.viewOnlineComments_button.setEnabled(
         self.fpl.onlineComments() != [])
     SharedDetailSheet.resetData(self)
Example #2
0
 def changeFplOnlineStatus(self, new_status):
     t = now()
     if new_status == FPL.OPEN:
         text = 'Do you want also to update departure time with the current date & time below?\n%s, %s' % (
             datestr(t), timestr(t))
         text += '\n\nWARNING: Answering "yes" or "no" will open the flight plan online.'
         button = QMessageBox.question(
             self,
             'Open FPL',
             text,
             buttons=(QMessageBox.Cancel | QMessageBox.No
                      | QMessageBox.Yes))
         if button == QMessageBox.Yes:
             self.depTime_edit.setDateTime(
                 QDateTime(t.year, t.month, t.day, t.hour, t.minute))
         ok = button != QMessageBox.Cancel
     elif new_status == FPL.CLOSED:
         ok = yesNo_question(self, 'Close FPL', 'Time is %s.' % timestr(t),
                             'This will close the flight plan online. OK?')
     if ok:
         try:
             settings.session_manager.changeFplStatus(self.fpl, new_status)
         except FplError as err:
             QMessageBox.critical(self, 'FPL open/close error', str(err))
         self.updateOnlineStatusBox()
Example #3
0
 def addNotification(self, t, msg, dbl_click_function):
     position = self.rowCount()
     self.beginInsertRows(QModelIndex(), position, position)
     self.history.insert(position,
                         Notification(t, now(), msg, dbl_click_function))
     self.endInsertRows()
     return True
Example #4
0
def auto_print_strip_reason(fpl):
    '''
	Returns reason to print if strip should be auto-printed from FPL; None otherwise
	'''
    if fpl.status() == FPL.CLOSED or fpl.strip_auto_printed \
    or settings.auto_print_strips_IFR_only and fpl[FPL.FLIGHT_RULES] != 'IFR' \
    or env.airport_data == None or env.linkedStrip(fpl) != None:
        return None
    present_time = now()
    ok_reason = None
    if settings.auto_print_strips_include_DEP:  # check DEP time
        dep = fpl[FPL.TIME_OF_DEP]
        if dep != None and fpl[
                FPL.
                ICAO_DEP] == env.airport_data.navpoint.code:  # we know: fpl.status() != FPL.CLOSED
            if dep - settings.auto_print_strips_anticipation <= present_time <= dep:
                ok_reason = 'departure due ' + rel_datetime_str(dep)
    if ok_reason == None and settings.auto_print_strips_include_ARR:  # check ARR time
        eta = fpl.ETA()
        if eta != None and fpl[
                FPL.ICAO_ARR] == env.airport_data.navpoint.code and fpl.status(
                ) == FPL.OPEN:
            if eta - settings.auto_print_strips_anticipation <= present_time <= eta:
                ok_reason = 'arrival due ' + rel_datetime_str(eta)
    return ok_reason
def get_declination(earth_location):
    today = now()
    try:
        q_items = {
            'startYear': today.year,
            'startMonth': today.month,
            'startDay': today.day,
            'resultFormat': 'xml',
            'lon1Hemisphere': 'EW'[earth_location.lon < 0],
            'lon1': abs(earth_location.lon),
            'lat1Hemisphere': 'NS'[earth_location.lat < 0],
            'lat1': abs(earth_location.lat),
            'browserRequest': 'false'
        }
        #DEBUG print('%s?%s' % (decl_base_location, urlencode(q_items)))
        response = open_URL('%s?%s' % (decl_base_location, urlencode(q_items)))
        xml = ElementTree.fromstring(response)
        if xml.tag == 'maggridresult':
            res_elt = xml.find('result')
            if res_elt != None:
                decl = float(res_elt.find('declination').text)
                return decl
    except URLError:
        print('Could not obtain declination from NOAA website.')
    except timeout:
        print('NOAA declination request timed out.')
    except ElementTree.ParseError:
        print('Parse error while reading NOAA data.')
	def checkAckTimeout(self):
		if settings.CPDLC_ACK_timeout != None and self.expecting:
			last_msg = self.lastMsg()
			if last_msg.isFromMe() and now() - last_msg.timeStamp() >= settings.CPDLC_ACK_timeout:
				self.timed_out = True
				self.statusChanged.emit()
				signals.cpdlcProblem.emit(self.acftCallsign(), 'Answer timed out')
 def resumeSession(self):
     if self.isRunning() and self.simulation_paused_at != None:
         pause_delay = now() - self.simulation_paused_at
         for acft in self.getAircraft():
             acft.moveHistoryTimesForward(pause_delay)
         self.session_ticker.start_stopOnZero(solo_ticker_interval)
         self.simulation_paused_at = None
         signals.sessionResumed.emit()
Example #8
0
def mkWeather(station, wind='00000KT', vis=9999, clouds='NSC', qnh=1013):
    metar = '%s %s %s' % (station, METAR_time_str(now()), wind)
    if vis >= 9999 and clouds == 'NSC':
        metar += ' CAVOK'
    else:
        metar += ' %04d %s' % (min(vis, 9999), clouds)
    metar += ' 15/05'
    metar += ' Q%d' % qnh
    return Weather(metar + '=')
Example #9
0
 def resumeSession(self):
     pause_delay = now() - self.simulation_paused_at
     for acft in self.aircraft_list:
         acft.moveHistoryTimesForward(pause_delay)
     self.simulation_paused_at = None
     self.session_ticker.start_stopOnZero(teacher_ticker_interval)
     signals.sessionResumed.emit()
     if self.studentConnected():
         self.student.sendMessage(TeachingMsg(TeachingMsg.SIM_RESUMED))
 def tickOnce(self):
     if not self.frozen:
         self.tick_interval = now() - self.lastLiveUpdateTime()
         hdg_before_tick = self.params.heading
         alt_before_tick = self.params.altitude
         self.doTick()
         self.hdg_tick_diff = self.params.heading.diff(hdg_before_tick)
         self.alt_tick_diff = self.params.altitude.diff(alt_before_tick)
     self.updateLiveStatus(self.params.position,
                           self.params.geometricAltitude(), self.xpdrData())
Example #11
0
 def __init__(self, sender, text, recipient=None, private=False):
     self.sent_by = sender
     self.known_recipient = recipient
     self.text = text
     self.private = private
     if recipient == None and private:
         self.private = False
         print(
             'WARNING: Cannot make message private without an identified recipient; made public.'
         )
     self.msg_time_stamp = now()
	def __init__(self, parent, atc_pov, acft_callsign, atc_callsign, xfr=None):
		QAbstractTableModel.__init__(self, parent)
		self.atc_pov = atc_pov
		self.acft_callsign = acft_callsign
		self.data_authority = atc_callsign
		self.initiator = xfr # Transferring ATC, or None if initiated by ACFT
		self.messages = []
		self.connect_time = now()
		self.disconnect_time = None
		self.transferred_to = None
		self.expecting = False # True if either party is expecting an answer
		self.timed_out = False # True if other party took too long to answer
		self.problems = {} # int msg index -> str problem to resolve
Example #13
0
 def flightIsInTimeWindow(self, half_width, ref=None, strict=False):
     dep = self.details[FPL.TIME_OF_DEP]
     if dep == None:
         return False
     if ref == None:
         ref = now()
     lo = ref - half_width
     hi = ref + half_width
     eta = some(self.ETA(), dep)
     if strict:
         return dep >= lo and eta <= hi
     else:
         return dep <= hi and eta >= lo
Example #14
0
 def addPhysicalRunway(self, width, surface, rwy1, rwy2):
     rwy1._opposite_runway = rwy2
     rwy2._opposite_runway = rwy1
     rwy1._orientation = rwy1.thr.headingTo(rwy2.thr)
     rwy2._orientation = rwy2.thr.headingTo(rwy1.thr)
     rwy1._physical_runway = rwy2._physical_runway = len(
         self.physical_runways)
     rwy = min(rwy1, rwy2, key=(lambda r: r.name))
     self.physical_runways.append((rwy, rwy.opposite(), width, surface))
     self.physical_RWY_box_WTC_timer.append(
         (now() - timedelta(days=1), None))
     self.directional_runways[rwy1.name] = rwy1
     self.directional_runways[rwy2.name] = rwy2
 def __init__(self, gui):
     QObject.__init__(self)
     self.ticker = Ticker(self.scan, parent=gui)
     self.last_sweep = now()  # to be updated with current time at each blip
     self.aircraft_list = []  # Aircraft list
     self.blips_invisible = {
     }  # str -> int; number of blips for which ACFT callsign has been invisible
     self.soft_links = []  # (Strip, Aircraft) pairs
     self.known_EMG_squawkers = set()  # str identifiers
     self.runway_occupation = {}  # int -> list of ACFT identifiers
     if env.airport_data != None:
         for i in range(env.airport_data.physicalRunwayCount()):
             self.runway_occupation[i] = []
	def terminate(self, xfr=None):
		if self.expecting: # should imply message count > 0
			problem_row = self.msgCount() - 1
			self.timed_out = False
			self.expecting = False
			self._markProblem(problem_row, 'Expected answer never received')
			self.dataChanged.emit(self.index(problem_row, 2), self.index(problem_row, 2))
		if self.isLive():
			self.disconnect_time = now()
			self.transferred_to = xfr
			self.statusChanged.emit()
		else:
			print('WARNING: CPDLC connection already terminated.')
	def __init__(self, identifier, acft_type, init_position, init_geom_alt):
		self.identifier = identifier # assumed unique
		self.aircraft_type = acft_type
		# "REAL TIME" VALUES
		self.live_update_time = now()
		self.live_position = init_position, init_geom_alt # EarthCoords, geom AMSL
		self.live_XPDR_data = {} # sqkey -> value mappings available from live update
		# UPDATED DATA
		init_snapshot = RadarSnapshot(self.live_update_time, init_position, init_geom_alt)
		self.radar_snapshots = [init_snapshot] # snapshot history; list must not be empty
		self.conflict = Conflict.NO_CONFLICT
		# USER OPTIONS
		self.individual_cheat = False
		self.ignored = False
		# FLAGS
		self.spawned = True
		self.frozen = False
Example #18
0
    def updateTimeStr(self):
        '''
		If METAR recent enough, returns a human-readable time string.
		Otherwise returns the 'Z'-suffixed METAR timestamp.
		'''
        match = update_time_regexp.search(self.METAR_string)
        if match:
            day = int(match.group(1))
            timestr_update = match.group(2)
            t = now()
            day_now = t.day
            timestr_now = '%02d%02d' % (t.hour, t.minute)
            if day == day_now and timestr_update <= timestr_now or day == day_now - 1 and timestr_update > timestr_now:
                return '%s:%s' % (timestr_update[:2], timestr_update[2:])
            else:
                return '%s%sZ' % (match.group(1), timestr_update)
        return None
 def __init__(self, parent=None):
     QDialog.__init__(self, parent)
     self.setupUi(self)
     t = now()
     qdt = QDateTime(t.year,
                     t.month,
                     t.day,
                     t.hour,
                     t.minute,
                     timeSpec=Qt.UTC)
     self.beginTime_edit.setDateTime(qdt)
     self.endTime_edit.setTime(QTime((t.hour + 2) % 24, 0))
     self.frequency_edit.addFrequencies([
         (frq, descr) for frq, descr, t in env.frequencies
     ])
     self.frequency_edit.clearEditText()
     self.announce_button.clicked.connect(self.announceSession)
     self.cancel_button.clicked.connect(self.reject)
Example #20
0
 def __init__(self, from_me, msg_type, contents=None):
     self.from_me = from_me  # True = sent by me to them; False otherwise
     self.msg_type = msg_type
     self.msg_contents = contents
     self.time_stamp = now()
	def data(self, index, role):
		fpl = self.FPL_list[index.row()]
		col = index.column()
		if role == Qt.DisplayRole:
			if col == 0:
				if fpl.existsOnline() and fpl.needsUpload(): # not in sync with online version
					return '!!'
			elif col == 1:
				return some(fpl[FPL.CALLSIGN], '?')
			elif col == 2:
				return fpl.shortDescr_AD()
			elif col == 3:
				return fpl.shortDescr_time()
		
		elif role == Qt.DecorationRole:
			if col == 0:
				if fpl.existsOnline():
					status = fpl.status()
					if status == FPL.FILED:
						dep = fpl[FPL.TIME_OF_DEP]
						if dep != None and now() > dep + FPL_outdated_delay: # outdated
							colour = settings.colour('FPL_filed_outdated')
						else:
							colour = settings.colour('FPL_filed')
					elif status == FPL.OPEN:
						eta = fpl.ETA()
						if eta == None: # warning
							colour = settings.colour('FPL_open_noETA')
						elif now() > eta: # overdue
							colour = settings.colour('FPL_open_overdue')
						else:
							colour = settings.colour('FPL_open')
					elif status == FPL.CLOSED:
						colour = settings.colour('FPL_closed')
					return coloured_square_icon(colour)
		
		elif role == Qt.ToolTipRole:
			if col == 0:
				if fpl.existsOnline():
					status = fpl.status()
					if status == FPL.FILED:
						dep = fpl[FPL.TIME_OF_DEP]
						txt = 'Outdated' if dep != None and now() > dep + FPL_outdated_delay else 'Filed'
					elif status == FPL.OPEN:
						eta = fpl.ETA()
						if eta == None: # warning
							txt = 'Open, ETA unknown'
						else:
							txt = 'Open'
							minutes_overtime = int(round((now() - eta).total_seconds())) // 60
							if minutes_overtime >= 1:
								txt += ', arrival overdue by %d h %02d min' % (minutes_overtime // 60, minutes_overtime % 60)
					elif status == FPL.CLOSED:
						txt = 'Closed'
					else:
						txt = 'Status N/A'
					if fpl.needsUpload():
						txt += '\n(local changes)'
					return txt
				else:
					return 'Not online'
Example #22
0
 def physicalRunway_restartWtcTimer(self, phyrwy_index, wtc):
     self.physical_RWY_box_WTC_timer[phyrwy_index] = now(), wtc
Example #23
0
 def physicalRunwayWtcTimer(self, phyrwy_index):
     t, wtc = self.physical_RWY_box_WTC_timer[phyrwy_index]
     return now() - t, wtc
 def pauseSession(self):
     if self.isRunning() and self.simulation_paused_at == None:
         self.simulation_paused_at = now()
         self.session_ticker.stop()
         signals.sessionPaused.emit()
Example #25
0
 def pauseSession(self):
     self.session_ticker.stop()
     self.simulation_paused_at = now()
     signals.sessionPaused.emit()
     if self.studentConnected():
         self.student.sendMessage(TeachingMsg(TeachingMsg.SIM_PAUSED))
Example #26
0
 def addStrip(self, strip):
     self.beginInsertRows(QModelIndex(), 0, 0)
     self.discarded_strips.insert(0, (strip, now()))
     self.endInsertRows()
    def scan(self):
        visible_aircraft = {
            a.identifier: a
            for a in settings.session_manager.getAircraft()
            if a.isRadarVisible()
        }

        ## UPDATE AIRCRAFT LIST
        lost_contacts = []
        for got_acft in self.aircraft_list:
            try:
                ignore = visible_aircraft.pop(got_acft.identifier)
                got_acft.saveRadarSnapshot()
                self.blips_invisible[got_acft.identifier] = 0
            except KeyError:
                count = self.blips_invisible[got_acft.identifier]
                if count < settings.invisible_blips_before_contact_lost:
                    self.blips_invisible[got_acft.identifier] = count + 1
                else:
                    lost_contacts.append(got_acft.identifier)
        # Remove lost aircraft
        for acft in pop_all(self.aircraft_list,
                            lambda acft: acft.identifier in lost_contacts):
            strip = env.linkedStrip(acft)
            del self.blips_invisible[acft.identifier]
            self.known_EMG_squawkers.discard(acft.identifier)
            self.lostContact.emit(acft)
            if strip != None:
                signals.controlledContactLost.emit(strip, acft.coords())
        # Add newly visible aircraft
        for new_acft in visible_aircraft.values():
            new_acft.saveRadarSnapshot()
            self.aircraft_list.append(new_acft)
            self.blips_invisible[new_acft.identifier] = 0
            self.newContact.emit(new_acft)

        ## CHECK FOR NEW EMERGENCIY SQUAWKS
        for acft in self.aircraft_list:
            if acft.xpdrCode() in XPDR_emergency_codes:
                if acft.identifier not in self.known_EMG_squawkers:
                    self.known_EMG_squawkers.add(acft.identifier)
                    self.emergencySquawk.emit(acft)
            else:
                self.known_EMG_squawkers.discard(acft.identifier)

        ## CHECK FOR NEW/LOST RADAR IDENTIFICATIONS
        if settings.traffic_identification_assistant:
            found_S_links = []
            found_A_links = []
            for strip in env.strips.listStrips(
                    lambda s: s.linkedAircraft() == None):
                mode_S_found = False
                # Try mode S identification
                if strip.lookup(FPL.CALLSIGN) != None:
                    scs = strip.lookup(FPL.CALLSIGN).upper()
                    if env.strips.count(
                            lambda s: s.lookup(FPL.CALLSIGN) != None and s.
                            lookup(FPL.CALLSIGN).upper() == scs) == 1:
                        candidates = [
                            acft for acft in self.aircraft_list
                            if acft.xpdrCallsign() != None
                            and acft.xpdrCallsign().upper() == scs
                        ]
                        if len(candidates) == 1:
                            found_S_links.append((strip, candidates[0]))
                            mode_S_found = True
                # Try mode A identification
                if not mode_S_found:
                    ssq = strip.lookup(assigned_SQ_detail)
                    if ssq != None and env.strips.count(lambda s: \
                      s.lookup(assigned_SQ_detail) == ssq and s.linkedAircraft() == None) == 1: # only one non-linked strip with this SQ
                        candidates = [acft for acft in self.aircraft_list if not any(a is acft for s, a in found_S_links) \
                         and acft.xpdrCode() == ssq and env.linkedStrip(acft) == None]
                        if len(candidates) == 1:  # only one aircraft matching
                            found_A_links.append((strip, candidates[0]))
            for s, a in pop_all(
                    self.soft_links, lambda sl: not any(sl[0] is s and sl[
                        1] is a for s, a in found_S_links + found_A_links)):
                s.writeDetail(soft_link_detail, None)
            for s, a, m in [(s, a, True) for s, a in found_S_links
                            ] + [(s, a, False) for s, a in found_A_links]:
                if not any(sl[0] is s and sl[1] is a
                           for sl in self.soft_links):  # new found soft link
                    if strip.lookup(
                            received_from_detail
                    ) != None and settings.strip_autolink_on_ident and (
                            m or settings.strip_autolink_include_modeC):
                        s.linkAircraft(a)
                    else:  # strip not automatically linked; notify of a new identification
                        self.soft_links.append((s, a))
                        s.writeDetail(soft_link_detail, a)
                        signals.aircraftIdentification.emit(s, a, m)

        ## UPDATE POSITION/ROUTE WARNINGS
        conflicts = {
            acft.identifier: Conflict.NO_CONFLICT
            for acft in self.aircraft_list
        }
        traffic_for_route_checks = []
        for strip in env.strips.listStrips(
        ):  # check for position conflicts and build list of traffic to check for routes later
            acft = strip.linkedAircraft()
            if acft != None and acft.identifier in conflicts:  # controlled traffic with radar contact
                for other in self.aircraft_list:
                    if other is not acft and position_conflict_test(
                            acft, other
                    ) == Conflict.NEAR_MISS:  # positive separation loss detected
                        conflicts[acft.identifier] = conflicts[
                            other.identifier] = Conflict.NEAR_MISS
                if not bypass_route_conflict_check(strip):
                    traffic_for_route_checks.append(acft)
        if settings.route_conflict_warnings:  # check for route conflicts
            while traffic_for_route_checks != []:  # progressively emptying the list
                acft = traffic_for_route_checks.pop()
                for other in traffic_for_route_checks:
                    c = path_conflict_test(acft, other)
                    conflicts[acft.identifier] = max(
                        conflicts[acft.identifier], c)
                    conflicts[other.identifier] = max(
                        conflicts[other.identifier], c)
        # now update aircraft conflicts and emit signals if any are new
        new_near_miss = new_path_conflict = False
        for contact in self.aircraft_list:
            new_conflict = conflicts[contact.identifier]
            if new_conflict > contact.conflict:
                new_near_miss |= new_conflict == Conflict.NEAR_MISS
                new_path_conflict |= new_conflict in [
                    Conflict.DEPENDS_ON_ALT, Conflict.PATH_CONFLICT
                ]
            contact.conflict = new_conflict
        if new_path_conflict:
            self.pathConflict.emit()
        if new_near_miss:
            self.nearMiss.emit()

        ## UPDATE RUNWAY OCCUPATION
        for phrwy in self.runway_occupation:
            new_occ = []
            if settings.monitor_runway_occupation:
                rwy1, rwy2 = env.airport_data.physicalRunway(phrwy)
                width_metres = env.airport_data.physicalRunwayData(phrwy)[0]
                thr1 = rwy1.threshold().toRadarCoords()
                thr2 = rwy2.threshold().toRadarCoords()
                w = m2NM * width_metres
                for acft in self.aircraft_list:
                    if acft.considerOnGround():
                        if acft.coords().toRadarCoords().isBetween(
                                thr1, thr2, w / 2):  # ACFT is on RWY
                            new_occ.append(acft)
                            if not any(
                                    a is acft
                                    for a in self.runway_occupation[phrwy]
                            ):  # just entered the RWY: check if alarm must sound
                                try:
                                    boxed_link = env.strips.findStrip(
                                        lambda strip: strip.lookup(
                                            runway_box_detail) == phrwy
                                    ).linkedAircraft()
                                except StopIteration:  # no strip boxed on this runway
                                    if rwy1.inUse() or rwy2.inUse(
                                    ):  # entering a non-reserved but active RWY
                                        self.runwayIncursion.emit(phrwy, acft)
                                else:  # RWY is reserved
                                    if boxed_link == None and env.linkedStrip(
                                            acft
                                    ) == None or boxed_link is acft:
                                        # entering ACFT is the one cleared to enter, or can be
                                        if self.runway_occupation[
                                                phrwy] != []:  # some ACFT was/were already on RWY
                                            call_guilty = acft if boxed_link == None else self.runway_occupation[
                                                phrwy][0]
                                            self.runwayIncursion.emit(
                                                phrwy, call_guilty)
                                    else:  # entering ACFT is known to be different from the one cleared to enter
                                        self.runwayIncursion.emit(phrwy, acft)
            self.runway_occupation[phrwy] = new_occ

        # Finished aircraft stuff
        self.last_sweep = now()
        self.blip.emit()
 def filterAcceptsRow(self, sourceRow, sourceParent):
     msg = self.sourceModel().messageOnRow(sourceRow)
     return msg.sender() not in self.hidden_senders \
      and (settings.text_chat_history_time == None or now() - msg.timeStamp() <= settings.text_chat_history_time)
Example #29
0
 def receiveMsgFromTeacher(self, msg):
     #DEBUG if msg.type != TeachingMsg.TRAFFIC:#
     #DEBUG 	print('=== STUDENT RECEIVES ===\n%s\n=== End ===' % msg.data)
     if msg.type == TeachingMsg.ACFT_KILLED:
         callsign = msg.strData()
         if env.cpdlc.isConnected(callsign):
             env.cpdlc.endDataLink(callsign)
         for acft in pop_all(self.traffic,
                             lambda a: a.identifier == callsign):
             signals.aircraftKilled.emit(acft)
     elif msg.type == TeachingMsg.TRAFFIC:  # traffic update; contains FGMS packet
         fgms_packet = msg.binData()
         update_FgmsAircraft_list(self.traffic, fgms_packet)
         send_packet_to_views(fgms_packet)
         self.teacher.sendMessage(TeachingMsg(TeachingMsg.TRAFFIC))
     elif msg.type == TeachingMsg.SIM_PAUSED:
         self.teacher_paused_at = now()
         signals.sessionPaused.emit()
     elif msg.type == TeachingMsg.SIM_RESUMED:
         pause_delay = now() - self.teacher_paused_at
         for acft in self.traffic:
             acft.moveHistoryTimesForward(pause_delay)
         self.teacher_paused_at = None
         signals.sessionResumed.emit()
     elif msg.type == TeachingMsg.ATC_TEXT_CHAT:
         lines = msg.strData().split('\n')
         if len(lines) == 2:
             signals.incomingAtcTextMsg.emit(
                 ChatMessage(lines[0],
                             lines[1],
                             recipient=student_callsign,
                             private=True))
         else:
             print(
                 'ERROR: Invalid format in received ATC text chat from teacher.'
             )
     elif msg.type == TeachingMsg.STRIP_EXCHANGE:
         line_sep = msg.strData().split('\n', maxsplit=1)
         fromATC = line_sep[0]
         strip = Strip.fromEncodedDetails(
             '' if len(line_sep) < 2 else line_sep[1])
         strip.writeDetail(received_from_detail, fromATC)
         signals.receiveStrip.emit(strip)
     elif msg.type == TeachingMsg.SX_LIST:
         to_remove = set(env.ATCs.knownATCs())
         to_remove.discard(teacher_callsign)
         for line in msg.strData().split('\n'):
             if line != '':  # last line is empty
                 lst = line.rsplit('\t', maxsplit=1)
                 try:
                     frq = CommFrequency(lst[1]) if len(lst) == 2 else None
                 except ValueError:
                     frq = None
                 env.ATCs.updateATC(lst[0], None, None, frq)
                 to_remove.discard(lst[0])
         for atc in to_remove:
             env.ATCs.removeATC(atc)
         env.ATCs.refreshViews()
     elif msg.type == TeachingMsg.WEATHER:
         metar = msg.strData()
         station = metar.split(' ', maxsplit=1)[0]
         if station == settings.primary_METAR_station and metar != self.known_METAR:
             self.known_METAR = metar
             signals.newWeather.emit(station, Weather(metar))
     elif msg.type == TeachingMsg.PTT:  # msg format: "b acft" where b is '1' or '0' for PTT on/off; acft is caller's identifier
         line_sep = msg.strData().split(' ', maxsplit=1)
         try:
             ptt = bool(int(line_sep[0]))
             caller = next(acft for acft in self.getAircraft()
                           if acft.identifier == line_sep[1])
             if ptt:
                 env.rdf.receiveSignal(caller.identifier,
                                       lambda acft=caller: acft.coords())
             else:
                 env.rdf.dieSignal(caller.identifier)
         except StopIteration:
             print('Ignored PTT message from teacher (unknown ACFT %s).' %
                   line_sep[1])
         except (ValueError, IndexError):
             print('Error decoding PTT message value from teacher')
     elif msg.type == TeachingMsg.CPDLC:
         try:
             acft_callsign, line2 = msg.strData().split('\n', maxsplit=1)
             if line2 == '0':  # ACFT is disconnecting
                 env.cpdlc.endDataLink(acft_callsign)
             elif line2 == '1':  # ACFT is connecting (CAUTION: teacher needs confirmation of accepted connection)
                 if settings.controller_pilot_data_link:
                     env.cpdlc.beginDataLink(acft_callsign,
                                             student_callsign)
                 self.teacher.sendMessage(
                     TeachingMsg(
                         TeachingMsg.CPDLC,
                         data=('%s\n%d' %
                               (acft_callsign,
                                settings.controller_pilot_data_link))))
             elif line2.startswith(CPDLC_transfer_cmd_prefix
                                   ):  # ACFT being transferred to me
                 if settings.controller_pilot_data_link:  # CAUTION: teacher needs confirmation of accepted connection
                     xfr_auth = line2[len(CPDLC_transfer_cmd_prefix):]
                     env.cpdlc.beginDataLink(acft_callsign,
                                             student_callsign,
                                             transferFrom=xfr_auth)
                     self.teacher.sendMessage(TeachingMsg(TeachingMsg.CPDLC, \
                       data=('%s\n%s%s' % (acft_callsign, CPDLC_transfer_cmd_prefix, xfr_auth))))
                 else:
                     self.teacher.sendMessage(
                         TeachingMsg(TeachingMsg.CPDLC,
                                     data=('%s\n0' % acft_callsign)))
             elif line2.startswith(
                     CPDLC_message_cmd_prefix):  # ACFT sending a message
                 encoded_msg = line2[len(CPDLC_message_cmd_prefix):]
                 link = env.cpdlc.currentDataLink(acft_callsign)
                 if link == None:
                     print(
                         'Ignored CPDLC message sent from %s while not connected.'
                         % acft_callsign)
                 else:
                     link.appendMessage(
                         CpdlcMessage.fromText(False, encoded_msg))
             else:
                 print('Error decoding CPDLC command from teacher:', line2)
         except (IndexError, ValueError):
             print('Error decoding CPDLC message value from teacher')
     else:
         print('Unhandled message type from teacher: %s' % msg.type)
	def updateLiveStatus(self, pos, geom_alt, xpdr_data):
		self.live_update_time = now()
		self.live_position = pos, geom_alt
		self.live_XPDR_data = xpdr_data