Example #1
0
class RTL433MsgGroup(ExportedState):
    implements(ITelemetryObject)

    def __init__(self, object_id):
        """Implements ITelemetryObject."""
        self.__cells = {}
        self.__last_heard_time = None

    def state_is_dynamic(self):
        """Overrides ExportedState."""
        return True

    def state_def(self, callback):
        """Overrides ExportedState."""
        super(RTL433MsgGroup, self).state_def(callback)
        for cell in self.__cells.itervalues():
            callback(cell)

    # not exported
    def receive(self, message_wrapper):
        """Implements ITelemetryObject."""
        self.__last_heard_time = message_wrapper.receive_time
        shape_changed = False
        for k, v in message_wrapper.message.iteritems():
            if _message_field_is_id.get(k, False) or k == u'time':
                continue
            if k not in self.__cells:
                shape_changed = True
                self.__cells[k] = LooseCell(key=k,
                                            value=None,
                                            type=object,
                                            writable=False,
                                            persists=False,
                                            label=k)
            self.__cells[k].set_internal(v)
        self.state_changed()
        if shape_changed:
            self.state_shape_changed()

    def is_interesting(self):
        """Implements ITelemetryObject."""
        return True

    def get_object_expiry(self):
        """implement ITelemetryObject"""
        return self.__last_heard_time + drop_unheard_timeout_seconds

    @exported_value(type=Timestamp(), changes='explicit', label='Last heard')
    def get_last_heard_time(self):
        return self.__last_heard_time
Example #2
0
class APRSStation(ExportedState):
    implements(IAPRSStation, ITelemetryObject)

    def __init__(self, object_id):
        self.__last_heard_time = None
        self.__address = object_id
        self.__track = empty_track
        self.__status = u''
        self.__symbol = None
        self.__last_comment = u''
        self.__last_parse_error = u''

    def receive(self, message):
        """implement ITelemetryObject"""
        self.__last_heard_time = message.receive_time
        for fact in message.facts:
            if isinstance(fact, KillObject):
                # Kill by pretending the object is ancient.
                self.__last_heard_time = 0
            if isinstance(fact, Position):
                self.__track = self.__track._replace(
                    latitude=TelemetryItem(fact.latitude,
                                           message.receive_time),
                    longitude=TelemetryItem(fact.longitude,
                                            message.receive_time),
                )
            if isinstance(fact, Altitude):
                conversion = _FEET_TO_METERS if fact.feet_not_meters else 1
                self.__track = self.__track._replace(altitude=TelemetryItem(
                    fact.value * conversion, message.receive_time), )
            if isinstance(fact, Velocity):
                self.__track = self.__track._replace(
                    h_speed=TelemetryItem(
                        fact.speed_knots * _KNOTS_TO_METERS_PER_SECOND,
                        message.receive_time),
                    track_angle=TelemetryItem(fact.course_degrees,
                                              message.receive_time),
                )
            elif isinstance(fact, Status):
                # TODO: Empirically, not always ASCII. Move this implicit decoding off into parse stages.
                self.__status = unicode(fact.text)
            elif isinstance(fact, Symbol):
                self.__symbol = unicode(fact.id)
            else:
                # TODO: Warn somewhere in this case (recognized by parser but not here)
                pass
        self.__last_comment = unicode(message.comment)
        if len(message.errors) > 0:
            self.__last_parse_error = '; '.join(message.errors)

    def is_interesting(self):
        """implement ITelemetryObject"""
        return True

    def get_object_expiry(self):
        """implement ITelemetryObject"""
        return self.__last_heard_time + drop_unheard_timeout_seconds

    @exported_value(type=Timestamp())
    def get_last_heard_time(self):
        return self.__last_heard_time

    @exported_value(type=unicode)
    def get_address(self):
        return self.__address

    @exported_value(type=Track)
    def get_track(self):
        return self.__track

    @exported_value(type=unicode)
    def get_symbol(self):
        """APRS symbol table identifier and symbol."""
        return self.__symbol

    @exported_value(type=unicode)
    def get_status(self):
        """String status text."""
        return self.__status

    @exported_value(type=unicode)
    def get_last_comment(self):
        return self.__last_comment

    @exported_value(type=Notice(always_visible=False))
    def get_last_parse_error(self):
        return self.__last_parse_error
Example #3
0
    # TODO: We should probably take larger-than-current day numbers as the previous month, and similar for hours just before midnight in 'h' format
    try:
        if kind == 'h':
            absolute_time = datetime.utcfromtimestamp(receive_time).replace(
                hour=n1, minute=n2, second=n3)
        elif kind == 'z':
            absolute_time = datetime.utcfromtimestamp(receive_time).replace(
                day=n1, hour=n2, minute=n3, second=0, microsecond=0)
        else:  # kind == '/'
            absolute_time = datetime.fromtimestamp(receive_time).replace(
                day=n1, hour=n2, minute=n3, second=0, microsecond=0)
    except ValueError, e:
        errors.append('DHM/HMS timestamp invalid: %s' % (e.message, ))
        return

    facts.append(Timestamp(absolute_time))


def _parse_angle(angle_str):
    # TODO return imprecision information
    # TODO old notes say "." is allowed as imprecision, check
    match = re.match(r'^(\d{1,3})([\d ]{2}\.[\d ]{2})([NESW])$', angle_str)
    if not match:
        return None
    else:
        degrees, minutes, direction = match.groups()
        minutes = minutes.replace(' ', '0')
        if direction == 'S' or direction == 'W':
            sign = -1
        else:
            sign = 1
Example #4
0
class Aircraft(ExportedState):
    implements(IAircraft, ITelemetryObject)

    def __init__(self, object_id):
        """Implements ITelemetryObject. object_id is the hex formatted address."""
        self.__last_heard_time = None
        self.__track = empty_track
        self.__call = None
        self.__ident = None
        self.__aircraft_type = None

    # not exported
    def receive(self, message_wrapper):
        message = message_wrapper.message
        cpr_decoder = message_wrapper.cpr_decoder
        receive_time = message_wrapper.receive_time
        self.__last_heard_time = receive_time
        # Unfortunately, gr-air-modes doesn't provide a function to implement this gunk -- imitating its output_flightgear code which
        data = message.data
        t = data.get_type()
        if t == 0:
            self.__track = self.__track._replace(altitude=TelemetryItem(
                air_modes.decode_alt(data['ac'], True) *
                _METERS_PER_FEET, receive_time))
            # TODO more info available here
        elif t == 4:
            self.__track = self.__track._replace(altitude=TelemetryItem(
                air_modes.decode_alt(data['ac'], True) *
                _METERS_PER_FEET, receive_time))
            # TODO more info available here
        elif t == 5:
            self.__ident = air_modes.decode_id(data['id'])
            # TODO more info available here
        elif t == 17:  # ADS-B
            bdsreg = data['me'].get_type()
            if bdsreg == 0x05:
                # TODO use unused info
                (altitude_feet, latitude, longitude, _range,
                 _bearing) = air_modes.parseBDS05(data, cpr_decoder)
                self.__track = self.__track._replace(
                    altitude=TelemetryItem(altitude_feet * _METERS_PER_FEET,
                                           receive_time),
                    latitude=TelemetryItem(latitude, receive_time),
                    longitude=TelemetryItem(longitude, receive_time),
                )
            elif bdsreg == 0x06:
                # TODO use unused info
                (_ground_track, latitude, longitude, _range,
                 _bearing) = air_modes.parseBDS06(data, cpr_decoder)
                self.__track = self.__track._replace(
                    latitude=TelemetryItem(latitude, receive_time),
                    longitude=TelemetryItem(longitude, receive_time),
                )
            elif bdsreg == 0x08:
                (self.__call,
                 self.__aircraft_type) = air_modes.parseBDS08(data)
            elif bdsreg == 0x09:
                subtype = data['bds09'].get_type()
                if subtype == 0:
                    (velocity, heading, vertical_speed,
                     _turn_rate) = air_modes.parseBDS09_0(data)
                    # TODO: note we're stuffing the heading in as track angle. Is there something better to do?
                    self.__track = self.__track._replace(
                        h_speed=TelemetryItem(
                            velocity * _KNOTS_TO_METERS_PER_SECOND,
                            receive_time),
                        heading=TelemetryItem(heading, receive_time),
                        track_angle=TelemetryItem(heading, receive_time),
                        v_speed=TelemetryItem(vertical_speed, receive_time),
                        # TODO add turn rate
                    )
                elif subtype == 1:
                    (velocity, heading,
                     vertical_speed) = air_modes.parseBDS09_1(data)
                    self.__track = self.__track._replace(
                        h_speed=TelemetryItem(
                            velocity * _KNOTS_TO_METERS_PER_SECOND,
                            receive_time),
                        heading=TelemetryItem(heading, receive_time),
                        track_angle=TelemetryItem(heading, receive_time),
                        v_speed=TelemetryItem(vertical_speed, receive_time),
                        # TODO reset turn rate?
                    )
                else:
                    # TODO report
                    pass
            else:
                # TODO report
                pass
        else:
            # TODO report
            pass
        self.state_changed()

    def is_interesting(self):
        """
        Implements ITelemetryObject. Does this aircraft have enough information to be worth mentioning?
        """
        # TODO: Loosen this rule once we have more efficient state transfer (no polling) and better UI for viewing them on the client.
        return \
            self.__track.latitude.value is not None or \
            self.__track.longitude.value is not None or \
            self.__call is not None or \
            self.__aircraft_type is not None

    def get_object_expiry(self):
        """implement ITelemetryObject"""
        return self.__last_heard_time + drop_unheard_timeout_seconds

    @exported_value(type=Timestamp(),
                    changes='explicit',
                    sort_key='100',
                    label='Last heard')
    def get_last_heard_time(self):
        return self.__last_heard_time

    @exported_value(type=unicode,
                    changes='explicit',
                    sort_key='020',
                    label='Call')  # TODO naming may be wrong
    def get_call(self):
        return self.__call

    @exported_value(type=int,
                    changes='explicit',
                    sort_key='050',
                    label='Ident')  # TODO naming may be wrong
    def get_ident(self):
        return self.__ident

    @exported_value(type=unicode,
                    changes='explicit',
                    sort_key='020',
                    label='Aircraft type')
    def get_aircraft_type(self):
        return self.__aircraft_type

    @exported_value(type=Track, changes='explicit', sort_key='010', label='')
    def get_track(self):
        return self.__track