All pastes #1485822 Raw Edit

Someone

public python v1 · immutable
#1485822 ·published 2009-07-06 16:21 UTC
rendered paste body
# -*- encoding: utf-8 -*-# -*- test-case-name: twisted.test.test_positioning -*-# Copyright (c) 2009 Twisted Matrix Laboratories.# See LICENSE for details."""Classes for using NMEA sentences."""import functoolsfrom twisted.protocols.positioning.base import Sentence, SentenceReceiverSENTENCE_CONTENTS = {    'GPGGA': [        ('timestamp', fixTimestamp),        ('latitudeFloat', fixLatitudeHemisphere),        ('latitudeHemisphere', fixLatitudeHemisphere),        ('longitudeFloat', fixLongitudeHemisphere),        ('longitudeHemisphere', fixLongitudeHemisphere),        ('validGGA', validateGGA),        ('numberOfSatellites', fixNumberOfSatellites),        ('horizontalDilutionOfPosition', fixHDOP),        ('altitude', fixAltitude),        ('altitudeUnits', fixAltitudeUnits),        ('heightAboveGeoid', fixHeightAboveGeoidUnits),        ('heightAboveGeoidUnits', fixHeightAboveGeoidUnits),         ]}class NMEASentence(Sentence):    def isUsable():        """        Checks if the current sentence is usable for positioning information.        """        return self.validateChecksum() and self.isReliableFix()    def validateChecksum(self):        """        Validates the checksum of an NMEA sentence.        Sentences with a valid checksum or missing checksum can be used to        instantiate NMEAParser objects:        >>> sentence = '''        ... $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47        ... '''        >>> s = NMEASentence(sentence)        Same thing with missing checksum:                >>> sentence = '''        ... $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*        ... '''        >>> s = NMEASentence(sentence)                Instantiating with a sentence that has a bad checksum will raise an        exception.                 >>> sentence = '''        ... $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*46        ... '''        >>> s = NMEASentence(sentence)        Traceback (most recent call last):            ...        InvalidChecksum: 71 != 70        """        if self.sentence[-3] == '*': # sentence has a checksum            reference, source = int(self.sentence[-2:], 16), self.sentence[1:-3]            computed = reduce(operator.xor, (ord(x) for x in source))            if computed != reference:                raise InvalidChecksum("%s != %s" % (computed, reference))        else:            log.msg("sentence %s missing checksum" % self.sentence)    def isReliableFix(self):        """        Checks if the fix is reliable.        Returns False if and only if the information was present and it showed        that the fix is *un*reliable. This method is optimistic in the sense        that it will return True if no information is known about the quality        of the fix. This is usually acceptable, since most GPS devices will not        send any sentences (or send empty ones) until the fix is reliable.        """        if self.sentenceData.get('validGGA') == GGA_INVALID:            return False        if self.sentenceData.get('validRMC') == RMC_INVALID:            return False                return Truedef fixTimestamp(sentenceData, oldTimestamp):    """    Turns the NMEA timestamp notation into a datetime.time object.    The time in this object is expressed as Zulu time.    """    hoursMinutesSeconds = oldTimestamp.split('.')[0]    datetimeObject = datetime.datetime.strptime(hoursMinutesSeconds, '%H%M%S')    sentenceData['timestamp'] = datetimeObject.time()    # TODO: Add tzinfo (this is Zulu time!)    def fixCoordinateFloat(sentenceData, value, coordinate):    currentValue = sentenceData.get(coordinate, 1)    left, right = str(value).split('.')    degrees = int(left[:-2])    minutes = float("%s.%s" % (left[-2:], right))    magnitude = degrees + minutes/60        self.sentenceData[coordinate] = currentValue * magnitudefixLatitudeFloat = functools.partial(fixCoordinateFloat,                                     coordinate='latitude')fixLongitudeFloat = functools.partial(fixCoordinateFloat,                                      coordinate='longitude')def fixHemisphereSign(sentenceData, hemisphere, coordinate):    """    Fixes the sign for a hemisphere.    @param sentenceData: partial sentence data    @type sentenceData: C{dict}    @param hemisphere: Single letter hemisphere representation (N,S,E,W).    @type hemisphere: C{str}    @param coordinate: Coordinate type (latitude or longitude).    @type coordinate: C{str}    Coordinates in the northern and eastern hemispheres will be preserved.    >>> sentenceData = {'latitude': '12.34', 'latitudeHemisphere': 'N'}    >>> sentenceData = {'longitude': '12.34', 'longitudeHemisphere': 'E'}    """    sign = (-1 if hemisphere.upper() in ('S', 'W') else 1)            currentValue = self.sentenceData.get(coordinate, 1)    self.sentenceData[coordinate] = sign * currentValuefixLatitudeHemisphere = functools.partial(fixHemisphereSign,                                          coordinate='latitude')fixLongitudeHemisphere = functools.partial(fixHemisphereSign,                                           coordinate='longitude')def simpleConverter(sentenceData, value, key=None, converter=float):    sentenceData[key] = converter(value)fixAltitude = functools.partial(simpleconverter, key="altitude")fixAboveGeoidConverter = functools.partial(simpleConverter,                                           key='heightAboveGeoid')fixHDOP = functools.partial(fixUnits, converter=int,,                            unitsFor='horizontalDilutionOfPosition')fixNumberOfSatellites = functools.partial(fixUnits, converter=int,                                          unitsFor='numberOfSatellites')acceptableUnits = frozenset(['M'])unitConverters = {    'knots': lambda inKnots: inKnots * 0.51444444444444444,}def fixUnits(sentenceData, unit, unitsFor):    """    Changes the units (if necessary) to more sensible SI units.    """    if unit in acceptableUnits:        return    currentValue = sentenceData.get(unitsFor, 1)    sentenceData[unitsFor] = unitConverters[unit](currentValue)fixAltitudeUnits = functools.partial(fixUnits, unitsFor='altitude')fixHeightAboveGeoidUnits = functools.partial(fixUnits, unitsFor='heightAboveGeoid')def validateGGA(sentenceData, value):    """    Checks if a GGA sentence is valid.    """    currentValue = sentenceData.get('valid', True)    sentenceData['valid'] = currentValue and (value != GGA_INVALID)def validateRMC(sentenceData, value):    """    Checks if an RMC sentence is valid.    """    currentValue = sentenceData.get('valid', True)    sentenceData['valid'] = currentValue and (value != RMC_INVALID)