U
    
W[                     @   sN  d Z ddlmZmZ ddlZddlmZ ddlmZ ddl	m
Z
mZ ddlmZmZmZ ddlmZ dd	lmZ dd
lmZ dZdZdZdZdZdZdZdZd  Z\Z Z!Z"eej#G dd de$Z%G dd deZ&G dd deZ'G dd deZ(G dd deZ)G dd de$Z*G d d! d!e*eZ+G d"d# d#e*eZ,G d$d% d%e*eZ-G d&d' d'e*eZ.G d(d) d)eZ/G d*d+ d+e$Z0G d,d- d-e0eZ1G d.d/ d/e0eZ2d0d1 Z3d2d3 Z4d4d5 Z5G d6d7 d7e0eZ6G d8d9 d9e0eZ7G d:d; d;e0eZ8G d<d= d=e0eZ9G d>d? d?e0eZ:G d@dA dAe0eZ;G dBdC dCe0eZ<G dDdE dEeZ=dS )Fz&
Test cases for using NMEA sentences.
    )absolute_importdivisionN)
attrgetter)implementer)	iteritems
intToBytes)basenmeaipositioning)MockPositioningReceiver)TestCase)AnglessA   $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47sD   $GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6As5   $GPGSA,A,3,19,28,14,18,27,22,31,39,,,,,1.7,1.0,1.3*34s   $GPHDT,038.005,T*3Bs'   $GPGLL,4916.45,N,12311.12,W,225444,A*31s   $GPGLL,3751.65,S,14507.36,E*77s;   $GPGSV,1,1,11,03,03,111,00,04,15,270,00,06,01,010,00,,,,*4bs2   $GPGSV,1,1,11,03,03,111,00,,,,,,,,,13,06,292,00*75s   
$GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,00,13,06,292,00*74
$GPGSV,3,2,11,14,25,170,00,16,57,208,39,18,67,296,40,19,40,246,00*74
$GPGSV,3,3,11,22,42,067,42,24,14,311,43,27,05,244,00,,,,*4D
c                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )	NMEATestReceiverzY
    An NMEA receiver for testing.

    Remembers the last sentence it has received.
    c                 C   s   |    d S N)clearself r   D/usr/lib/python3/dist-packages/twisted/positioning/test/test_nmea.py__init__-   s    zNMEATestReceiver.__init__c                 C   s
   d| _ dS )zq
        Forgets the received sentence (if any), by setting
        C{self.receivedSentence} to L{None}.
        NreceivedSentencer   r   r   r   r   1   s    zNMEATestReceiver.clearc                 C   s
   || _ d S r   r   r   sentencer   r   r   sentenceReceived9   s    z!NMEATestReceiver.sentenceReceivedN)__name__
__module____qualname____doc__r   r   r   r   r   r   r   r   &   s   r   c                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )	CallbackTestsaO  
    Tests if the NMEA protocol correctly calls its sentence callback.

    @ivar protocol: The NMEA protocol under test.
    @type protocol: L{nmea.NMEAProtocol}
    @ivar sentenceTypes: The set of sentence types of all sentences the test's
        sentence callback function has been called with.
    @type sentenceTypes: C{set}
    c                 C   s"   t  }t|| j| _t | _d S r   )r   r	   NMEAProtocol_sentenceCallbackprotocolsetsentenceTypesr   receiverr   r   r   setUpH   s    zCallbackTests.setUpc                 C   s   | j |j dS )zC
        Remembers that a sentence of this type was fired.
        N)r$   addtyper   r   r   r   r!   N   s    zCallbackTests._sentenceCallbackc                 C   sh   dgdgdgdgdgdgd}t |D ]<\}}|D ].}| j| | | jt|g | j  q2q&dS )	zI
        The correct callbacks fire, and that *only* those fire.
        	   $GPGGA*56s	   $GPGLL*50s	   $GPGSA*42s	   $GPGSV*55s	   $GPHDT*4fs	   $GPRMC*4b)GPGGAGPGLLGPGSAGPGSVGPHDTGPRMCN)r   r"   lineReceivedassertEqualr$   r#   r   )r   ZsentencesByTypeZsentenceType	sentencesr   r   r   r   test_callbacksCalledU   s    	z"CallbackTests.test_callbacksCalledN)r   r   r   r   r'   r!   r4   r   r   r   r   r   >   s   	r   c                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )	BrokenSentenceCallbackTestsz3
    Tests for broken NMEA sentence callbacks.
    c                 C   s   t  }t|| j| _d S r   )r   r	   r    r!   r"   r%   r   r   r   r'   n   s    z!BrokenSentenceCallbackTests.setUpc                 C   s   t ddS )z+
        Raises C{AttributeError}.
        zERROR!!!N)AttributeErrorr   r   r   r   r!   s   s    z-BrokenSentenceCallbackTests._sentenceCallbackc                 C   s   | j j}| t|d dS )zt
        An C{AttributeError} in the sentence callback of an C{NMEAProtocol}
        doesn't get swallowed.
        r*   N)r"   r1   assertRaisesr6   )r   r1   r   r   r   "test_dontSwallowCallbackExceptionsz   s    z>BrokenSentenceCallbackTests.test_dontSwallowCallbackExceptionsN)r   r   r   r   r'   r!   r8   r   r   r   r   r5   j   s   r5   c                   @   s    e Zd ZdZdd Zdd ZdS )
SplitTestsz-
    Checks splitting of NMEA sentences.
    c                 C   s    t d}| |dddg dS )zH
        An NMEA sentence with a checksum gets split correctly.
        s   $GPGGA,spam,eggs*00   GPGGA   spam   eggsNr	   Z_splitr2   r   ZsplitSentencer   r   r   test_withChecksum   s    
zSplitTests.test_withChecksumc                 C   s    t d}| |dddg dS )zK
        An NMEA sentence without a checksum gets split correctly.
        s   $GPGGA,spam,eggs*r:   r;   r<   Nr=   r>   r   r   r   test_noCheckum   s    
zSplitTests.test_noCheckumN)r   r   r   r   r?   r@   r   r   r   r   r9      s   r9   c                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )	ChecksumTestsz4
    NMEA sentence checksum verification tests.
    c                 C   s   t t dS )z?
        Sentences with valid checksums get validated.
        Nr	   _validateChecksumr+   r   r   r   r   
test_valid   s    zChecksumTests.test_validc                 C   s   t tdd  dS )zA
        Sentences with missing checksums get validated.
        NrB   r   r   r   r   test_missing   s    zChecksumTests.test_missingc                 C   sR   t j}td\}}tt|dd }|d | g}|D ]}| tj|| q8dS )zw
        Sentences with a bad checksum raise L{base.InvalidChecksum} when
        attempting to validate them.
           *      N)	r	   rC   r+   splitr   intr7   r   ZInvalidChecksum)r   ZvalidateZbareSentenceZchecksumZbadChecksumr3   sr   r   r   test_invalid   s    zChecksumTests.test_invalidN)r   r   r   r   rD   rF   rM   r   r   r   r   rA      s   rA   c                   @   s   e Zd ZdZdd ZdS )NMEAReceiverSetupaG  
    A mixin for tests that need an NMEA receiver (and a protocol attached to
    it).

    @ivar receiver: An NMEA receiver that remembers the last sentence.
    @type receiver: L{NMEATestReceiver}
    @ivar protocol: An NMEA protocol attached to the receiver.
    @type protocol: L{twisted.positioning.nmea.NMEAProtocol}
    c                 C   s   t  | _t| j| _dS )z+
        Sets up an NMEA receiver.
        N)r   r&   r	   r    r"   r   r   r   r   r'      s    zNMEAReceiverSetup.setUpN)r   r   r   r   r'   r   r   r   r   rN      s   	rN   c                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )	GSVSequenceTestsz8
    Tests for the interpretation of GSV sequences.
    c                 C   s4   | j t | jj}| |  | |  dS )zO
        The first sentence in a GSV sequence is correctly identified.
        N)	r"   r1   GPGSV_FIRSTr&   r   
assertTrue_isFirstGSVSentenceassertFalse_isLastGSVSentencer   r   r   r   test_firstSentence   s    z#GSVSequenceTests.test_firstSentencec                 C   s4   | j t | jj}| |  | |  dS )z
        A sentence in the middle of a GSV sequence is correctly
        identified (as being neither the last nor the first).
        N)r"   r1   GPGSV_MIDDLEr&   r   rS   rR   rT   r   r   r   r   test_middleSentence   s    z$GSVSequenceTests.test_middleSentencec                 C   s4   | j t | jj}| |  | |  dS )zN
        The last sentence in a GSV sequence is correctly identified.
        N)	r"   r1   
GPGSV_LASTr&   r   rS   rR   rQ   rT   r   r   r   r   test_lastSentence   s    z"GSVSequenceTests.test_lastSentenceN)r   r   r   r   rU   rW   rY   r   r   r   r   rO      s   rO   c                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )	BogusSentenceTestszK
    Tests for verifying predictable failure for bogus NMEA sentences.
    c                 C   s   |  || jj| dS )a=  
        Asserts that the protocol raises C{exceptionClass} when it receives
        C{sentence}.

        @param exceptionClass: The exception class expected to be raised.
        @type exceptionClass: C{Exception} subclass

        @param sentence: The (bogus) NMEA sentence.
        @type sentence: C{str}
        N)r7   r"   r1   )r   exceptionClassr   r   r   r   assertRaisesOnSentence   s    z)BogusSentenceTests.assertRaisesOnSentencec                 C   s   |  td dS )z`
        Receiving a well-formed sentence of unknown type raises
        C{ValueError}.
        s   $GPBOGUS*5bN)r\   
ValueErrorr   r   r   r   test_raiseOnUnknownSentenceType  s    z2BogusSentenceTests.test_raiseOnUnknownSentenceTypec                 C   s   |  tjd dS )zP
        Receiving a malformed sentence raises L{base.InvalidSentence}.
        ZGPBOGUSN)r\   r   ZInvalidSentencer   r   r   r   test_raiseOnMalformedSentences  s    z1BogusSentenceTests.test_raiseOnMalformedSentencesN)r   r   r   r   r\   r^   r_   r   r   r   r   rZ      s   rZ   c                   @   s   e Zd ZdZdd ZdS )NMEASentenceTestsz1
    Tests for L{nmea.NMEASentence} objects.
    c                 C   s@   t dfg}|D ],\}}| j| | jj}| t|| qdS )zI
        The C{repr} of L{nmea.NMEASentence} objects is correct.
        aX  <NMEASentence (GPGSA) {dataMode: A, fixType: 3, horizontalDilutionOfPrecision: 1.0, positionDilutionOfPrecision: 1.7, usedSatellitePRN_0: 19, usedSatellitePRN_1: 28, usedSatellitePRN_2: 14, usedSatellitePRN_3: 18, usedSatellitePRN_4: 27, usedSatellitePRN_5: 22, usedSatellitePRN_6: 31, usedSatellitePRN_7: 39, verticalDilutionOfPrecision: 1.3}>N)r-   r"   r1   r&   r   r2   repr)r   ZsentencesWithExpectedReprr   ZexpectedReprreceivedr   r   r   	test_repr  s    zNMEASentenceTests.test_reprN)r   r   r   r   rc   r   r   r   r   r`     s   r`   c                   @   sX   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Zdd Z	dd Z
dd Zdd ZdS )ParsingTestsz
    Tests if raw NMEA sentences get parsed correctly.

    This doesn't really involve any interpretation, just turning ugly raw NMEA
    representations into objects that are more pleasant to work with.
    c                 C   s&   | j | | jj}| ||j dS )z
        Passes a sentence to the protocol and gets the parsed sentence from
        the receiver. Then verifies that the parsed sentence contains the
        expected data.
        N)r"   r1   r&   r   r2   _sentenceData)r   r   expectedrb   r   r   r   _parserTest@  s    zParsingTests._parserTestc                 C   s.   ddddddddd	d
ddd}|  t| dS )z:
        A full RMC sentence is correctly parsed.
        r0   4807.038N	01131.000Ez003.1Wz022.4123519Z230394z084.4A)r)   latitudeFloatlatitudeHemispherelongitudeFloatlongitudeHemispheremagneticVariationmagneticVariationDirectionspeedInKnots	timestamp	datestamptrueHeadingdataModeN)rg   r0   r   rf   r   r   r   test_fullRMCK  s    zParsingTests.test_fullRMCc                 C   s0   dddddddddd	d
ddd}|  t| dS )z:
        A full GGA sentence is correctly parsed.
        r+   545.4M46.9z0.9rh   ri   rj   rk   Z08rm   1)r)   altitudeZaltitudeUnitsheightOfGeoidAboveWGS84ZheightOfGeoidAboveWGS84UnitshorizontalDilutionOfPrecisionro   rp   rq   rr   numberOfSatellitesSeenrv   
fixQualityN)rg   r+   rz   r   r   r   test_fullGGA`  s    zParsingTests.test_fullGGAc                 C   s$   dddddddd}|  t| d	S )
z:
        A full GLL sentence is correctly parsed.
        r,   z4916.45ri   z12311.12rl   Z225444rn   )r)   ro   rp   rq   rr   rv   ry   N)rg   r,   rz   r   r   r   test_fullGLLz  s    zParsingTests.test_fullGLLc                 C   s    dddddd}|  t| dS )z=
        A partial GLL sentence is correctly parsed.
        r,   z3751.65Sz14507.36rk   )r)   ro   rp   rq   rr   N)rg   GPGLL_PARTIALrz   r   r   r   test_partialGLL  s    zParsingTests.test_partialGLLc                 C   s>   ddddddddd	d
ddd	dddddddd}|  t| dS )z:
        A full GSV sentence is correctly parsed.
        r.   r   311Z111Z270Z010Z292Z03Z15Z01Z06Z04Z1300)r)   GSVSentenceIndexnumberOfGSVSentencesr   	azimuth_0	azimuth_1	azimuth_2Z	azimuth_3elevation_0elevation_1elevation_2Zelevation_3satellitePRN_0satellitePRN_1satellitePRN_2ZsatellitePRN_3signalToNoiseRatio_0signalToNoiseRatio_1signalToNoiseRatio_2ZsignalToNoiseRatio_3N)rg   rP   rz   r   r   r   test_fullGSV  s,    zParsingTests.test_fullGSVc                 C   s6   dddddddddd	d
dddddd}|  t| dS )z=
        A partial GSV sentence is correctly parsed.
        r.   r   r   Z067Z311Z244Z4214Z0522Z2427Z43r   )r)   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   N)rg   rX   rz   r   r   r   test_partialGSV  s$    zParsingTests.test_partialGSVc                 C   s   ddd}|  t| dS )z:
        A full HDT sentence is correctly parsed.
        r/   z038.005)r)   rx   N)rg   r/   rz   r   r   r   test_fullHDT  s    zParsingTests.test_fullHDTc                 C   s2   ddddddddd	d
ddddd}|  t| dS )z=
        A typical GSA sentence is correctly parsed.
        r-   rn   r   Z19Z28r   Z18r   r   Z31Z39z1.7z1.0z1.3)r)   ry   fixTypeZusedSatellitePRN_0ZusedSatellitePRN_1ZusedSatellitePRN_2ZusedSatellitePRN_3ZusedSatellitePRN_4ZusedSatellitePRN_5ZusedSatellitePRN_6ZusedSatellitePRN_7positionDilutionOfPrecisionr   verticalDilutionOfPrecisionN)rg   r-   rz   r   r   r   test_typicalGSA  s     zParsingTests.test_typicalGSAN)r   r   r   r   rg   r{   r   r   r   r   r   r   r   r   r   r   r   rd   9  s   !rd   c                   @   s0   e Zd ZdZdd Zdd Zdd Zdd	 Zd
S )FixUnitsTestsz
    Tests for the generic unit fixing method, L{nmea.NMEAAdapter._fixUnits}.

    @ivar adapter: The NMEA adapter.
    @type adapter: L{nmea.NMEAAdapter}
    c                 C   s   t t | _d S r   r	   NMEAAdapterr   ZBasePositioningReceiveradapterr   r   r   r   r'     s    zFixUnitsTests.setUpc                 C   sB   G dd dt }| | j_| jjddd | | jjd d dS )	zv
        Tests that when no C{valueKey} is provided, C{unitKey} is used, minus
        C{"Units"} at the end.
        c                   @   s   e Zd ZdZdd ZdS )z3FixUnitsTests.test_noValueKey.<locals>.FakeSentencezJ
            A fake sentence that just has a "foo" attribute.
            c                 S   s
   d| _ d S )NrI   )foor   r   r   r   r     s    z<FixUnitsTests.test_noValueKey.<locals>.FakeSentence.__init__Nr   r   r   r   r   r   r   r   r   FakeSentence  s   r   fooUnitsri   )unitKeyunitr   rI   Nobjectr   ZcurrentSentence	_fixUnitsassertNotEqualre   r   r   r   r   r   test_noValueKey
  s    
zFixUnitsTests.test_noValueKeyc                 C   s@   G dd dt }| | j_| jjdd | | jjd d dS )z
        Tests that if a unit key is provided but the unit isn't, the unit is
        automatically determined from the unit key.
        c                   @   s   e Zd ZdZdd ZdS )z9FixUnitsTests.test_unitKeyButNoUnit.<locals>.FakeSentencezX
            A fake sentence that just has "foo" and "fooUnits" attributes.
            c                 S   s   d| _ d| _d S )NrI   ri   )r   r   r   r   r   r   r   $  s    zBFixUnitsTests.test_unitKeyButNoUnit.<locals>.FakeSentence.__init__Nr   r   r   r   r   r      s   r   r   )r   r   rI   Nr   r   r   r   r   test_unitKeyButNoUnit  s    
z#FixUnitsTests.test_unitKeyButNoUnitc                 C   s   | j t| jjdd dS )z
        Tests that when a unit is specified but neither C{valueKey} nor
        C{unitKey} is provided, C{ValueError} is raised.
        K)r   N)r7   r]   r   r   r   r   r   r   test_noValueKeyAndNoUnitKey-  s    z)FixUnitsTests.test_noValueKeyAndNoUnitKeyN)r   r   r   r   r'   r   r   r   r   r   r   r   r     s
   r   c                   @   s"   e Zd ZdZdd ZdddZdS )FixerTestMixinz
    Mixin for tests for the fixers on L{nmea.NMEAAdapter} that adapt
    from NMEA-specific notations to generic Python objects.

    @ivar adapter: The NMEA adapter.
    @type adapter: L{nmea.NMEAAdapter}
    c                 C   s   t t | _d S r   r   r   r   r   r   r'   >  s    zFixerTestMixin.setUpNc                    sR   t | fdd}|dkr8|    jj| n ||  j  dS )a  
        A generic adapter fixer test.

        Creates a sentence from the C{sentenceData} and sends that to the
        adapter. If C{exceptionClass} is not passed, this is assumed to work,
        and C{expected} is compared with the adapter's internal state.
        Otherwise, passing the sentence to the adapter is checked to raise
        C{exceptionClass}.

        @param sentenceData: Raw sentence content.
        @type sentenceData: C{dict} mapping C{str} to C{str}

        @param expected: The expected state of the adapter.
        @type expected: C{dict} or L{None}

        @param exceptionClass: The exception to be raised by the adapter.
        @type exceptionClass: subclass of C{Exception}
        c                      s    j  d S r   )r   r   r   r   r   r   receiveSentenceV  s    z2FixerTestMixin._fixerTest.<locals>.receiveSentenceN)r	   ZNMEASentencer2   r   _stater7   r   )r   sentenceDatarf   r[   r   r   r   r   
_fixerTestB  s    
zFixerTestMixin._fixerTest)NN)r   r   r   r   r'   r   r   r   r   r   r   6  s   r   c                   @   s    e Zd ZdZdd Zdd ZdS )TimestampFixerTestszL
    Tests conversion from NMEA timestamps to C{datetime.time} objects.
    c                 C   s*   ddi}dt dddi}| || dS )z<
        A simple timestamp is converted correctly.
        rv   Z123456Z_time   "   8   N)datetimetimer   )r   datarf   r   r   r   test_simpleg  s    zTimestampFixerTests.test_simplec                 C   s$   d}|D ]}| j d|itd qdS )z:
        A broken timestamp raises C{ValueError}.
        )Z993456Z129956Z123499rv   r[   Nr   r]   )r   ZbadTimestampstr   r   r   test_brokenp  s    zTimestampFixerTests.test_brokenN)r   r   r   r   r   r   r   r   r   r   r   c  s   	r   c                   @   s4   e Zd Zdd Zdd Zdd Zdd Zd	d
 ZdS )DatestampFixerTestsc                 C   s   |  | jjd dS )z5
        The default year threshold is 1980.
        i  N)r2   r   ZyearThresholdr   r   r   r   test_defaultYearThreshold|  s    z-DatestampFixerTests.test_defaultYearThresholdc                 C   s,   dt ddd }}| d|id|i dS )z
        Dates before the threshold are interpreted as being in the century
        after the threshold. (Since the threshold is the earliest possible
        date.)
        Z010115i  rI   rw   _dateNr   dater   r   Z
datestringr   r   r   r   test_beforeThreshold  s    z(DatestampFixerTests.test_beforeThresholdc                 C   s,   dt ddd }}| d|id|i dS )zr
        Dates after the threshold are interpreted as being in the same century
        as the threshold.
        Z010195i  rI   rw   r   Nr   r   r   r   r   test_afterThreshold  s    z'DatestampFixerTests.test_afterThresholdc                 C   s   | j dditd dS )zQ
        A datestring with an invalid month (> 12) raises C{ValueError}.
        rw   Z011301r   Nr   r   r   r   r   test_invalidMonth  s    z%DatestampFixerTests.test_invalidMonthc                 C   s(   | j dditd | j dditd dS )zy
        A datestring with an invalid day (more days than there are in that
        month) raises C{ValueError}.
        rw   Z320101r   Z300201Nr   r   r   r   r   test_invalidDay  s    z#DatestampFixerTests.test_invalidDayN)r   r   r   r   r   r   r   r   r   r   r   r   r   {  s
   
	r   c                 C   s   d| |f S )a^  
    Builds an NMEA float representation for a given angle in degrees and
    decimal minutes.

    @param degrees: The integer degrees for this angle.
    @type degrees: C{int}
    @param minutes: The decimal minutes value for this angle.
    @type minutes: C{float}
    @return: The NMEA float representation for this angle.
    @rtype: C{str}
    z%i%0.3fr   )ZdegreesZminutesr   r   r   
_nmeaFloat  s    r   c                 C   s   | dkrdS dS )a8  
    Return the sign of a coordinate.

    This is C{1} if the coordinate is in the northern or eastern hemispheres,
    C{-1} otherwise.

    @param hemisphere: NMEA shorthand for the hemisphere. One of "NESW".
    @type hemisphere: C{str}

    @return: The sign of the coordinate value.
    @rtype: C{int}
    ZNErI   r   Z
hemispherer   r   r   _coordinateSign  s    r   c                 C   s   | dkrt jS t jS )ao  
    Return the type of a coordinate.

    This is L{Angles.LATITUDE} if the coordinate is in the northern or
    southern hemispheres, L{Angles.LONGITUDE} otherwise.

    @param hemisphere: NMEA shorthand for the hemisphere. One of "NESW".
    @type hemisphere: C{str}

    @return: The type of the coordinate (L{Angles.LATITUDE} or
        L{Angles.LONGITUDE})
    ZNS)r   LATITUDE	LONGITUDEr   r   r   r   _coordinateType  s    r   c                   @   s@   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Zdd Z	dS )CoordinateFixerTestszO
    Tests turning NMEA coordinate notations into something more pleasant.
    c                 C   s,   ddd}dt dtji}| || dS )zg
        NMEA coordinate representations in the northern hemisphere
        convert correctly.
        1030.000ri   ro   rp   latitude      %@Nr   
Coordinater   r   r   r   r   stater   r   r   
test_north  s    
zCoordinateFixerTests.test_northc                 C   s,   ddd}dt dtji}| || dS )zg
        NMEA coordinate representations in the southern hemisphere
        convert correctly.
        r   r   r   r         %Nr   r   r   r   r   
test_south  s    
zCoordinateFixerTests.test_southc                 C   s,   ddd}dt dtji}| || dS )zf
        NMEA coordinate representations in the eastern hemisphere
        convert correctly.
        r   rk   rq   rr   	longituder   Nr   r   r   r   r   r   r   r   r   	test_east  s    
zCoordinateFixerTests.test_eastc                 C   s,   ddd}dt dtji}| || dS )zf
        NMEA coordinate representations in the western hemisphere
        convert correctly.
        r   rl   r   r   r   Nr   r   r   r   r   	test_west  s    
zCoordinateFixerTests.test_westc                 C   s   ddi}| j |td dS )z
        NMEA coordinate representations for nonexistent hemispheres
        raise C{ValueError} when you attempt to parse them.
        rr   Qr   Nr   r   r   r   r   r   test_badHemisphere  s    z'CoordinateFixerTests.test_badHemispherec                    s    fdd}  t| dS )z
        NMEA coordinate repesentation parsing fails predictably
        when you pass nonexistent coordinate types (not latitude or
        longitude).
        c                      s    j dS )NZBOGUS_VALUE)r   Z_getHemisphereSignr   r   r   r   <lambda>      z=CoordinateFixerTests.test_badHemisphereSign.<locals>.<lambda>N)r7   r]   )r   ZgetSignr   r   r   test_badHemisphereSign  s    z+CoordinateFixerTests.test_badHemisphereSignN)
r   r   r   r   r   r   r   r   r   r   r   r   r   r   r     s   



	r   c                   @   s    e Zd ZdZdd Zdd ZdS )AltitudeFixerTestszO
    Tests that NMEA representations of altitudes are correctly converted.
    c                 C   s.   d\}}t t|}| ||i||i dS )zo
        The NMEA representation of an altitude (above mean sea level)
        is correctly converted.
        )r   r|   Nr   Altitudefloatr   r   keyvaluer   r   r   r   test_fixAltitude  s    z#AltitudeFixerTests.test_fixAltitudec                 C   s.   d\}}t t|}| ||i||i dS )z
        The NMEA representation of an altitude of the geoid (above the
        WGS84 reference level) is correctly converted.
        )r   r~   Nr   r   r   r   r   test_heightOfGeoidAboveWGS84%  s    z/AltitudeFixerTests.test_heightOfGeoidAboveWGS84N)r   r   r   r   r   r   r   r   r   r   r     s   
r   c                   @   s   e Zd ZdZdd ZdS )SpeedFixerTestszL
    Tests that NMEA representations of speeds are correctly converted.
    c                 C   s6   d\}}}t t|t j }| ||i||i dS )z`
        Speeds reported in knots correctly get converted to meters per
        second.
        )ru   Z10speedN)r   ZSpeedr   ZMPS_PER_KNOTr   )r   r   r   Z	targetKeyr   r   r   r   test_speedInKnots4  s    
z!SpeedFixerTests.test_speedInKnotsN)r   r   r   r   r   r   r   r   r   r   0  s   r   c                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )	VariationFixerTestsz
    Tests if the absolute values of magnetic variations on the heading
    and their sign get combined correctly, and if that value gets
    combined with a heading correctly.
    c                 C   s<   d\}}t jjdt| d}||d}| |d|i dS )z?
        Tests westward (negative) magnetic variation.
        )1.34rl   r   ZvariationValuers   rt   headingNr   ZHeadingZ
fromFloatsr   r   r   	variation	directionr  r   r   r   r   r   E  s    zVariationFixerTests.test_westc                 C   s8   d\}}t jjt|d}||d}| |d|i dS )z?
        Tests eastward (positive) magnetic variation.
        )r   rk   r   r   r  Nr  r  r   r   r   r   Q  s    zVariationFixerTests.test_eastc                 C   sB   d\}}}|||d}t jjt|t|d}| |d|i dS )zH
        Variation values get combined with headings correctly.
        )z123.12r   rk   )rx   rs   rt   r   r  Nr  )r   rx   r  r  r   r  r   r   r   test_withHeading]  s    
z$VariationFixerTests.test_withHeadingN)r   r   r   r   r   r   r  r   r   r   r   r   ?  s   r   c                   @   s    e Zd ZdZdd Zdd ZdS )PositionErrorFixerTestsa  
    Position errors in NMEA are passed as dilutions of precision (DOP). This
    is a measure relative to some specified value of the GPS device as its
    "reference" precision. Unfortunately, there are very few ways of figuring
    this out from just the device (sans manual).

    There are two basic DOP values: vertical and horizontal. HDOP tells you
    how precise your location is on the face of the earth (pretending it's
    flat, at least locally). VDOP tells you how precise your altitude is
    known. PDOP (position DOP) is a dependent value defined as the Euclidean
    norm of those two, and gives you a more generic "goodness of fix" value.
    c                 C   s    |  ddidtjddi d S )Nr   r   positionErrorg      &@)hdop)r   r   PositionErrorr   r   r   r   r   x  s    z#PositionErrorFixerTests.test_simplec                 C   sF   d\}}}t jt|t|t|d}|||d}| |d|i d S )N)r   r   r   )pdopr	  vdop)r   r   r   r  )r   r
  r   r   )r   r  r	  r  r  r   r   r   r   test_mixing~  s    

z#PositionErrorFixerTests.test_mixingN)r   r   r   r   r   r  r   r   r   r   r  k  s   r  c                   @   s    e Zd ZdZdd Zdd ZdS )ValidFixTestsz<
    Tests that data reported from a valid fix is used.
    c                 C   s.   ddt jjd}dtdi}| || dS )z4
        GGA data with a valid fix is used.
        r+   r|   r)   r   r   r   33333@N)r	   GPGGAFixQualitiesZGPS_FIXr   r   r   r   r   ZexpectedStater   r   r   test_GGA  s    zValidFixTests.test_GGAc                 C   s.   ddt jjd}dtdi}| || dS )z:
        GLL data with a valid data mode is used.
        r,   r|   r)   r   ry   r   r  N)r	   GPGLLGPRMCFixQualitiesACTIVEr   r   r   r  r   r   r   test_GLL  s    zValidFixTests.test_GLLN)r   r   r   r   r  r  r   r   r   r   r    s   r  c                   @   s@   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Zdd Z	dS )InvalidFixTestsa:  
    Tests that data being reported from a bad or incomplete fix isn't
    used. Although the specification dictates that GPSes shouldn't produce
    NMEA sentences with real-looking values for altitude or position in them
    unless they have at least some semblance of a GPS fix, this is widely
    ignored.
    c                 C   s   |  |i  dS )zu
        Sentences with an invalid fix or data mode result in empty
        state (ie, the data isn't used).
        N)r   r   r   r   r   _invalidFixTest  s    zInvalidFixTests._invalidFixTestc                 C   s   ddt jjd}| | dS )zC
        GGA sentence data is unused when there is no fix.
        r+   r|   r  N)r	   r  ZINVALID_FIXr  r   r   r   r   r    s
    zInvalidFixTests.test_GGAc                 C   s   ddt jjd}| | dS )zO
        GLL sentence data is unused when the data is flagged as void.
        r,   r|   r  N)r	   r  VOIDr  r   r   r   r   r    s
    zInvalidFixTests.test_GLLc                 C   s$   ddt jjt jjd}| | dS )z
        GSA sentence data is not used when there is no GPS fix, but
        the data mode claims the data is "active". Some GPSes do
        this, unfortunately, and that means you shouldn't use the
        data.
        r-   r|   r)   r   ry   r   N)r	   r  r  GPGSAFixTypes
GSA_NO_FIXr  r   r   r   r   test_badGSADataMode  s    z#InvalidFixTests.test_badGSADataModec                 C   s$   ddt jjt jjd}| | dS )z
        GSA sentence data is not used when the fix claims to be valid
        (albeit only 2D), but the data mode says the data is void.
        Some GPSes do this, unfortunately, and that means you
        shouldn't use the data.
        r-   r|   r  N)r	   r  r  r  Z
GSA_2D_FIXr  r   r   r   r   test_badGSAFixType  s    z"InvalidFixTests.test_badGSAFixTypec                 C   s$   ddt jjt jjd}| | dS )zj
        GSA sentence data is not use when neither the fix nor the data
        mode is any good.
        r-   r|   r  N)r	   r  r  r  r  r  r   r   r   r   test_badGSADataModeAndFixType  s    z-InvalidFixTests.test_badGSADataModeAndFixTypeN)
r   r   r   r   r  r  r  r  r  r   r   r   r   r   r    s   r  c                   @   s   e Zd ZdZdd Zdd Zd$dd	Zd
d Zdd Zdd Z	dd Z
dd Zdd Zdd Zdd Zdd Zdd Zdd Zd d! Zd"d# ZdS )%NMEAReceiverTestsz&
    Tests for the NMEA receiver.
    c                 C   s(   t  | _t| j| _t| j| _d S r   )r   r&   r	   r   r   r    r"   r   r   r   r   r'     s    zNMEAReceiverTests.setUpc                 C   s~   | j t tdddg}| t| jj | | j  | 	| j
ji  | j t tdg}| t| jj | dS )z
        If the current sentence does not contain any new fields for a
        particular callback, that callback is not called; even if all
        necessary information is still in the state from one or more
        previous messages.
        positionReceivedpositionErrorReceivedaltitudeReceivedheadingReceivedN)r"   r1   r+   r#   r2   r&   calledkeysr   r   r   r   r/   )r   ZgpggaCallbacksZgphdtCallbacksr   r   r   1test_onlyFireWhenCurrentSentenceHasNewInformation  s    

zCNMEAReceiverTests.test_onlyFireWhenCurrentSentenceHasNewInformationr   Nc                 C   s\   |D ]}| j | q| jj }| t|t| |dk	rD|  | j  | j  dS )a  
        A generic test for NMEA receiver behavior.

        @param sentences: The sequence of sentences to simulate receiving.
        @type sentences: iterable of C{str}
        @param expectedFired: The names of the callbacks expected to fire.
        @type expectedFired: iterable of C{str}
        @param extraTest: An optional extra test hook.
        @type extraTest: nullary callable
        N)	r"   r1   r&   r&  r'  r2   r#   r   r   )r   r3   ZexpectedFiredZ	extraTestr   ZactuallyFiredr   r   r   _receiverTest  s    
zNMEAReceiverTests._receiverTestc                    s:   t gt }ddg}dd   fdd}||| dS )zJ
        The positioning error is updated across multiple states.
        r#  beaconInformationReceivedc                 S   s   t ttd| S )N
identifier)sortedmapr   )Zbeaconsr   r   r   _getIdentifiers9  s    zONMEAReceiverTests.test_positionErrorUpdateAcrossStates.<locals>._getIdentifiersc                     s`   j jd }  | j}dddddddd	d
ddg}||  | j}|ddd	d
dg d S )NbeaconInformation               rH                  )r   r   seenBeaconsr2   ZusedBeacons)r/  ZseenIdentifiersrf   ZusedIdentifiersr.  r   r   r   checkBeaconInformation<  s    

zVNMEAReceiverTests.test_positionErrorUpdateAcrossStates.<locals>.checkBeaconInformationN)r-   	GPGSV_SEQr)  r   r3   callbacksFiredr<  r   r;  r   $test_positionErrorUpdateAcrossStates2  s
    
z6NMEAReceiverTests.test_positionErrorUpdateAcrossStatesc                    s*   t g}dg} fdd} ||| dS )z
        A GSV sentence with empty entries in any position does not mean that
        entries in subsequent positions of the same GSV sentence are ignored.
        r*  c                     s<    j jd } | j} t|d  ddd |D  d S )Nr/     r3  c                 S   s   g | ]
}|j qS r   )r+  ).0br   r   r   
<listcomp>Z  s     zYNMEAReceiverTests.test_emptyMiddleGSV.<locals>.checkBeaconInformation.<locals>.<listcomp>)r   r   r:  r2   lenZassertIn)r/  r:  r   r   r   r<  U  s    zENMEAReceiverTests.test_emptyMiddleGSV.<locals>.checkBeaconInformationNGPGSV_EMPTY_MIDDLEr)  r>  r   r   r   test_emptyMiddleGSVM  s    z%NMEAReceiverTests.test_emptyMiddleGSVc                 C   s    t g}dddg}| || dS )z
        A sequence of GGA sentences fires C{positionReceived},
        C{positionErrorReceived} and C{altitudeReceived}.
        r"  r#  r$  N)r+   r)  r   r3   r?  r   r   r   test_GGASentences_  s    z#NMEAReceiverTests.test_GGASentencesc                 C   s8   t ddd| jjd< tg}ddddg}| || dS )	z
        When receiving a GPGGA sentence and a date was already in the
        state, the new time (from the GPGGA sentence) is combined with
        that date.
        i  rI   r   r"  r#  r$  timeReceivedN)r   r   r   r   r+   r)  rI  r   r   r   test_GGAWithDateInStatel  s    z)NMEAReceiverTests.test_GGAWithDateInStatec                 C   s"   t g}ddddg}| || dS )z
        A sequence of RMC sentences fires C{positionReceived},
        C{speedReceived}, C{headingReceived} and C{timeReceived}.
        r%  speedReceivedr"  rK  N)r0   r)  rI  r   r   r   test_RMCSentences}  s    z#NMEAReceiverTests.test_RMCSentencesc                    s.   t ttg}dg} fdd} ||| dS )zb
        A complete sequence of GSV sentences fires
        C{beaconInformationReceived}.
        r*  c                      s     d jj d S )NZ_partialBeaconInformation)ZassertNotInr   r   r   r   r   r   checkPartialInformation  s    zDNMEAReceiverTests.test_GSVSentences.<locals>.checkPartialInformationN)rP   rV   rX   r)  )r   r3   r?  rO  r   r   r   test_GSVSentences  s    
z#NMEAReceiverTests.test_GSVSentencesc                 C   s   t g}| |dg dS )z
        A complete sequence of GSV sentences with empty entries in the
        middle still fires C{beaconInformationReceived}.
        r*  NrF  r   r3   r   r   r   "test_emptyMiddleEntriesGSVSequence  s    z4NMEAReceiverTests.test_emptyMiddleEntriesGSVSequencec                 C   s   t g}| | dS )zV
        An incomplete sequence of GSV sentences does not fire any callbacks.
        N)rP   r)  rQ  r   r   r   test_incompleteGSVSequence  s    z,NMEAReceiverTests.test_incompleteGSVSequencec                 C   s   t g}| |dg dS )z
        The parser does not fail badly when the sequence consists of
        only one sentence (but is otherwise complete).
        r*  N)GPGSV_SINGLEr)  rQ  r   r   r   test_singleSentenceGSVSequence  s    z0NMEAReceiverTests.test_singleSentenceGSVSequencec                 C   s   t tg}| |dg dS )z9
        GLL sentences fire C{positionReceived}.
        r"  N)r   r,   r)  rQ  r   r   r   test_GLLSentences  s    z#NMEAReceiverTests.test_GLLSentencesc                 C   s   t g}| |dg dS )z8
        HDT sentences fire C{headingReceived}.
        r%  N)r/   r)  rQ  r   r   r   test_HDTSentences  s    z#NMEAReceiverTests.test_HDTSentencesc                    s6   t tg}ddddddg} fdd} ||| d	S )
zA
        A mix of sentences fires the correct callbacks.
        r$  rM  r"  r#  rK  r%  c                     s,   t  dddddd}   jjd |  d S )Ni  r0     r   #   r6  r   )r   r2   r   r   )ZexpectedDateTimer   r   r   	checkTime  s    z8NMEAReceiverTests.test_mixedSentences.<locals>.checkTimeN)r0   r+   r)  )r   r3   r?  rZ  r   r   r   test_mixedSentences  s    z%NMEAReceiverTests.test_mixedSentencesc                 C   s6   t gt tttg }dddddddg}| || dS )	a#  
        Sends an entire gamut of sentences and verifies the
        appropriate callbacks fire. These are more than you'd expect
        from your average consumer GPS device. They have most of the
        important information, including beacon information and
        visibility.
        r%  r*  rM  r"  rK  r$  r#  N)r-   r=  r0   r+   r,   r)  rI  r   r   r   test_lotsOfMixedSentences  s    z+NMEAReceiverTests.test_lotsOfMixedSentences)r   N)r   r   r   r   r'   r(  r)  r@  rH  rJ  rL  rN  rP  rR  rS  rU  rV  rW  r[  r\  r   r   r   r   r!    s"   
		r!  )>r   Z
__future__r   r   r   operatorr   Zzope.interfacer   Ztwisted.python.compatr   r   Ztwisted.positioningr   r	   r
   Z!twisted.positioning.test.receiverr   Ztwisted.trial.unittestr   Ztwisted.positioning.baser   r+   r0   r-   r/   r,   r   rT  rG  rJ   r=  rP   rV   rX   ZINMEAReceiverr   r   r   r5   r9   rA   rN   rO   rZ   r`   rd   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r!  r   r   r   r   <module>   sZ   ,"'"" G7-,@,Q