diff --git a/python/setup.py b/python/setup.py index 01ed48b9..0849d596 100755 --- a/python/setup.py +++ b/python/setup.py @@ -2,21 +2,22 @@ from version import get_git_version setup_args = dict( - name = 'swiftnav', - version = get_git_version(), - description = 'Python bindings to the libswiftnav library.', - license = 'LGPLv3', - url = 'http://www.swiftnav.com', - author = 'Swift Navigation Inc.', - author_email = 'dev@swiftnav.com', - maintainer = 'Swift Navigation', - maintainer_email = 'dev@swiftnav.com', - packages = ['swiftnav'], + name='swiftnav', + version=get_git_version(), + description='Python bindings to the libswiftnav library.', + license='LGPLv3', + url='http://www.swiftnav.com', + author='Swift Navigation Inc.', + author_email='dev@swiftnav.com', + maintainer='Swift Navigation', + maintainer_email='dev@swiftnav.com', + packages=['swiftnav'], ) if __name__ == "__main__": import numpy as np - import os, sys + import os + import sys from setuptools import setup, Extension try: from Cython.Distutils import build_ext @@ -47,15 +48,16 @@ include_dirs.append('../include/') include_dirs.append('../libfec/include/') include_dirs.append('../tests/data/l2cbitstream/') + def make_extension(ext_name): ext_path = ext_name.replace('.', os.path.sep) + '.pyx' return Extension( ext_name, [ext_path], - include_dirs = include_dirs, - extra_compile_args = ['-O0', '-g'], - extra_link_args = ['-g'], - libraries = ['m', 'swiftnav', 'l2cbitstream'], - library_dirs = library_dirs, + include_dirs=include_dirs, + extra_compile_args=['-O0', '-g'], + extra_link_args=['-g'], + libraries=['m', 'swiftnav', 'l2cbitstream'], + library_dirs=library_dirs, ) ext_names = [ 'swiftnav.edc', @@ -64,6 +66,7 @@ def make_extension(ext_name): 'swiftnav.constants', 'swiftnav.cnav_msg', 'swiftnav.nav_msg', + 'swiftnav.nav_msg_glo', 'swiftnav.pvt', 'swiftnav.correlate', 'swiftnav.track', diff --git a/python/swiftnav/ephemeris.pyx b/python/swiftnav/ephemeris.pyx index f9c971ec..ab79c516 100644 --- a/python/swiftnav/ephemeris.pyx +++ b/python/swiftnav/ephemeris.pyx @@ -1,4 +1,4 @@ -# Copyright (C) 2015 Swift Navigation Inc. +# Copyright (C) 2015-2016 Swift Navigation Inc. # # This source is subject to the license found in the file 'LICENSE' which must # be be distributed together with this source. All other rights reserved. diff --git a/python/swiftnav/nav_msg_glo.pxd b/python/swiftnav/nav_msg_glo.pxd new file mode 100644 index 00000000..a8e4de6b --- /dev/null +++ b/python/swiftnav/nav_msg_glo.pxd @@ -0,0 +1,49 @@ +# Copyright (C) 2016 Swift Navigation Inc. +# +# This source is subject to the license found in the file 'LICENSE' which must +# be be distributed together with this source. All other rights reserved. +# +# THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +# EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. + +# cython: embedsignature=True + +from common cimport * +from ephemeris cimport * +from libcpp cimport bool +from signal cimport gnss_signal_t + +cdef extern from "libswiftnav/nav_msg_glo.h": + enum: + NAV_MSG_GLO_STRING_BITS_LEN + GLO_TM + GLO_TM_LEN + + ctypedef enum glo_receive_machine: + INVALID + SYNC_TM + GET_DATA_BIT + + ctypedef struct nav_msg_glo_t: + u32 string_bits[NAV_MSG_GLO_STRING_BITS_LEN] + u16 current_head_bit_index + u16 nt + u8 next_string_id + u8 n4 + u8 hrs + u8 min + u8 sec + glo_receive_machine state + u8 meander_bits_cnt + u8 manchester + u8 decode_done + + void nav_msg_init_glo(nav_msg_glo_t *n) + s8 process_string_glo(nav_msg_glo_t *n, ephemeris_t *e) + s8 nav_msg_update_glo(nav_msg_glo_t *n, bool bit_val) + double nav_msg_get_tow_glo(nav_msg_glo_t *n) + s8 error_detection_glo(nav_msg_glo_t *n) + +cdef class NavMsgGlo: + cdef nav_msg_glo_t _thisptr diff --git a/python/swiftnav/nav_msg_glo.pyx b/python/swiftnav/nav_msg_glo.pyx new file mode 100644 index 00000000..234647d6 --- /dev/null +++ b/python/swiftnav/nav_msg_glo.pyx @@ -0,0 +1,108 @@ +# Copyright (C) 2016 Swift Navigation Inc. +# +# This source is subject to the license found in the file 'LICENSE' which must +# be be distributed together with this source. All other rights reserved. +# +# THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +# EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. + +# cython: embedsignature=True + +from ephemeris cimport ephemeris_t +from fmt_utils import fmt_repr +from time cimport gps_time_t +from signal cimport gnss_signal_t +from swiftnav.ephemeris import Ephemeris + + +cdef class NavMsgGlo: + + def __cinit__(self): + nav_msg_init_glo(&self._thisptr) + + def __getattr__(self, k): + return self._thisptr.get(k) + + def __repr__(self): + return fmt_repr(self) + + def to_dict(self): + return self._thisptr + + def from_dict(self, d): + self._thisptr = d + + def __reduce__(self): + return (rebuild_NavMsgGlo, tuple(self.to_dict().items())) + + def updateEphemeris(self, Ephemeris e): + return process_string_glo(&self._thisptr, &e._thisptr) + + def update(self, bit_val): + return nav_msg_update_glo(&self._thisptr, bit_val) + + def getTow(self): + return nav_msg_get_tow_glo(&self._thisptr) + + def detectError(self): + return error_detection_glo(&self._thisptr) + + def isDecodeDone(self): + return self._thisptr.decode_done != 0 + + def __richcmp__(self, other, op): + """ + Weird Cython comparison method. See + http://docs.cython.org/src/userguide/special_methods.html. + """ + if op == 2: + return self._equal(other) + elif op == 3: + return not self._equal(other) + else: + raise NotImplementedError + + def _equal(self, other): + """ + Compare equality between self and another :class:`NavMsg` object. + + Parameters + ---------- + other : :class:`NavMsgGlo` object + The :class:`NavMsgGlo` to test equality against. + + Return + ------ + out : bool + True if the passed :class:`NavMsg` object is identical. + + """ + if self.to_dict().keys() != other.to_dict().keys(): + return False + + for k in self.to_dict().keys(): + if self.to_dict()[k] != other.to_dict()[k]: + return False + + return True + + +def rebuild_NavMsgGlo(*reduced): + """ + Rebuild NavMsg for unpickling. + + Parameters + ---------- + reduced: tuple + Tuple of dict of NavMsg nav_msg_t struct fields + + Returns + ------- + out: :class:`NavMsg` instance + Rebuilt :class:`NavMsg` instance + """ + nm = NavMsgGlo() + nm.from_dict(dict(reduced)) + return nm + diff --git a/python/tests/test_nav_msg_glo.py b/python/tests/test_nav_msg_glo.py new file mode 100644 index 00000000..904fdcd4 --- /dev/null +++ b/python/tests/test_nav_msg_glo.py @@ -0,0 +1,86 @@ + + +from swiftnav.nav_msg_glo import NavMsgGlo +from swiftnav.ephemeris import Ephemeris +import numpy + + +def test_imports(): + """Verify that distributed packages survive setuptools installation. + + """ + pass + +GLO_STRINGS = numpy.asanyarray([[1, 1, 1], # dummy words + # 01074396999b05c3a850b5 + [0x010743, 0x96999b05, 0xc3a850b5], + # 021760a5256204d9c15f66 + [0x021760, 0xa5256204, 0xd9c15f66], + # 0380269d60899a6d0e3123 + [0x038026, 0x6d0e3123, 0x9d60899a], + # 04865d1cc0000000344918 + [0x04865d, 0x00344918, 0x1cc00000], + # 050d100000000340000895 + [0x050d10, 0x3, 0x40000895] + ], + dtype=numpy.dtype('>u4')) +GLO_STRING_BITS = [numpy.unpackbits(s.view(numpy.uint8))[-85:] + for s in GLO_STRINGS] + +GLO_TM = numpy.asanyarray([0x3E375096], dtype=numpy.dtype('>u4')) +GLO_TM_BITS = numpy.unpackbits(GLO_TM.view(numpy.uint8))[-30:] + + +def test_init(): + ''' + Object construction test + ''' + msg = NavMsgGlo() + assert msg.isDecodeDone() == False + assert msg.getTow() == -1. + + +def test_update(): + ''' + Object update test + ''' + msg = NavMsgGlo() + assert msg.update(0) == -1 + assert msg.update(0) == -1 + assert msg.update(0) == -1 + assert msg.update(1) == -1 + + assert msg.isDecodeDone() == False + assert msg.getTow() == -1. + + +def test_decode(): + ''' + Ephemeris and ToW decoding test. + ''' + msg = NavMsgGlo() + n_strings = 0 + n_tow = 0 + tow = -1 + e = Ephemeris() + print GLO_STRING_BITS + for bit_string in GLO_STRING_BITS: + print bit_string + for bit in bit_string: + assert msg.update(bit ^ 1) == -1 + if msg.update(bit) == 1: + n_strings += 1 + if msg.updateEphemeris(e) == 1: + n_tow += 1 + tow = msg.getTow() + + # now pass time mark bit by bit to receiver (MSB first), no line code + # needed + for bit in GLO_TM_BITS: + assert msg.update(bit) == -1 + + assert n_strings == 5 + assert n_tow == 1 + assert isinstance(tow, float) + assert tow == 262707.0 + assert e.toe['tow'] == 262707.0 - 10.