{"id":23683921,"url":"https://github.com/semuconsulting/pyrtcm","last_synced_at":"2025-05-16T16:01:46.352Z","repository":{"id":40442633,"uuid":"459291983","full_name":"semuconsulting/pyrtcm","owner":"semuconsulting","description":"Python library for parsing RTCM 3 protocol messages. ","archived":false,"fork":false,"pushed_at":"2025-03-27T08:27:52.000Z","size":852,"stargazers_count":82,"open_issues_count":0,"forks_count":25,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-03T20:09:16.579Z","etag":null,"topics":["gnss","gps","ntrip","rtcm","rtcm3"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/semuconsulting.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":"CITATION.bib","codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"buy_me_a_coffee":"semuconsulting"}},"created_at":"2022-02-14T19:09:48.000Z","updated_at":"2025-03-27T08:25:45.000Z","dependencies_parsed_at":"2023-12-21T13:01:47.200Z","dependency_job_id":"ae30c6fb-8331-4b98-b327-fdb1de2ef583","html_url":"https://github.com/semuconsulting/pyrtcm","commit_stats":{"total_commits":169,"total_committers":3,"mean_commits":"56.333333333333336","dds":"0.029585798816568087","last_synced_commit":"bf9273fa770c63ab5e020d8cd612c5cfd1118d17"},"previous_names":[],"tags_count":47,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semuconsulting%2Fpyrtcm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semuconsulting%2Fpyrtcm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semuconsulting%2Fpyrtcm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/semuconsulting%2Fpyrtcm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/semuconsulting","download_url":"https://codeload.github.com/semuconsulting/pyrtcm/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248578875,"owners_count":21127714,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gnss","gps","ntrip","rtcm","rtcm3"],"created_at":"2024-12-29T20:29:57.920Z","updated_at":"2025-04-12T14:19:51.536Z","avatar_url":"https://github.com/semuconsulting.png","language":"Python","funding_links":["https://buymeacoffee.com/semuconsulting"],"categories":[],"sub_categories":[],"readme":"# pyrtcm\r\n\r\n[Current Status](#currentstatus) |\r\n[Installation](#installation) |\r\n[Reading](#reading) |\r\n[Parsing](#parsing) |\r\n[Generating](#generating) |\r\n[Serializing](#serializing) |\r\n[Examples](#examples) |\r\n[Extensibility](#extensibility) |\r\n[Command Line Utility](#cli) |\r\n[Graphical Client](#gui) |\r\n[Author \u0026 License](#author)\r\n\r\n`pyrtcm` is an original Python 3 parser for the RTCM3 \u0026copy; GPS/GNSS protocol. RTCM3 is a proprietary GPS/GNSS [differential correction or DGPS](https://en.wikipedia.org/wiki/Differential_GPS) protocol published by the Radio Technical Commission for Maritime Services.\r\n\r\n[RTCM STANDARD 10403.n DIFFERENTIAL GNSS (GLOBAL NAVIGATION SATELLITE SYSTEMS) SERVICES – VERSION 3](https://rtcm.myshopify.com/collections/differential-global-navigation-satellite-dgnss-standards/products/rtcm-10403-3-differential-gnss-global-navigation-satellite-systems-services-version-3-amendment-2-may-20-2021).\r\n\r\nThe `pyrtcm` homepage is located at [https://github.com/semuconsulting/pyrtcm](https://github.com/semuconsulting/pyrtcm).\r\n\r\nThis is an independent project and we have no affiliation whatsoever with the Radio Technical Commission for Maritime Services.\r\n\r\n**FYI** There are companion libraries which handle standard NMEA 0183 \u0026copy; and UBX \u0026copy; (u-blox) GNSS/GPS messages:\r\n- [pyubx2](http://github.com/semuconsulting/pyubx2)\r\n- [pynmeagps](http://github.com/semuconsulting/pynmeagps)\r\n\r\n## \u003ca name=\"currentstatus\"\u003eCurrent Status\u003c/a\u003e\r\n\r\n![Status](https://img.shields.io/pypi/status/pyrtcm)\r\n![Release](https://img.shields.io/github/v/release/semuconsulting/pyrtcm?include_prereleases)\r\n![Build](https://img.shields.io/github/actions/workflow/status/semuconsulting/pyrtcm/main.yml?branch=main)\r\n![Codecov](https://img.shields.io/codecov/c/github/semuconsulting/pyrtcm)\r\n![Release Date](https://img.shields.io/github/release-date-pre/semuconsulting/pyrtcm)\r\n![Last Commit](https://img.shields.io/github/last-commit/semuconsulting/pyrtcm)\r\n![Contributors](https://img.shields.io/github/contributors/semuconsulting/pyrtcm.svg)\r\n![Open Issues](https://img.shields.io/github/issues-raw/semuconsulting/pyrtcm)\r\n\r\nParses RTCM3 messages into their constituent data fields - `DF002`, `DF003`, etc. Refer to the `RTCM_MSGIDS` dictionary in [`rtcmtypes_core.py`](https://github.com/semuconsulting/pyrtcm/blob/main/src/pyrtcm/rtcmtypes_core.py#L695) for a list of message types currently implemented. Additional message types can be readily added - see [Extensibility](#extensibility).\r\n\r\nSphinx API Documentation in HTML format is available at [https://www.semuconsulting.com/pyrtcm/](https://www.semuconsulting.com/pyrtcm/)\r\n\r\nContributions welcome - please refer to [CONTRIBUTING.MD](https://github.com/semuconsulting/pyrtcm/blob/master/CONTRIBUTING.md).\r\n\r\n[Bug reports](https://github.com/semuconsulting/pyrtcm/blob/master/.github/ISSUE_TEMPLATE/bug_report.md) and [Feature requests](https://github.com/semuconsulting/pyrtcm/blob/master/.github/ISSUE_TEMPLATE/feature_request.md) - please use the templates provided. For general queries and advice, post a message to one of the [pyrtcm Discussions](https://github.com/semuconsulting/pyrtcm/discussions) channels.\r\n\r\n---\r\n## \u003ca name=\"installation\"\u003eInstallation\u003c/a\u003e\r\n\r\n![Python version](https://img.shields.io/pypi/pyversions/pyrtcm.svg?style=flat)\r\n[![PyPI version](https://img.shields.io/pypi/v/pyrtcm.svg?style=flat)](https://pypi.org/project/pyrtcm/)\r\n![PyPI downloads](https://img.shields.io/pypi/dm/pyrtcm.svg?style=flat)\r\n\r\n`pyrtcm` is compatible with Python 3.9 - 3.13. In the following, `python3` \u0026 `pip` refer to the Python 3 executables. You may need to substitute `python` for `python3`, depending on your particular environment (*on Windows it's generally `python`*).\r\n\r\nThe recommended way to install the latest version of `pyrtcm` is with [pip](http://pypi.python.org/pypi/pip/):\r\n\r\n```shell\r\npython3 -m pip install --upgrade pyrtcm\r\n```\r\n\r\nIf required, `pyrtcm` can also be installed into a virtual environment, e.g.:\r\n\r\n```shell\r\npython3 -m venv env\r\nsource env/bin/activate # (or env\\Scripts\\activate on Windows)\r\npython3 -m pip install --upgrade pyrtcm\r\n```\r\n\r\nFor [Conda](https://docs.conda.io/en/latest/) users, `pyrtcm` is also available from [conda-forge](https://github.com/conda-forge/pyrtcm-feedstock):\r\n\r\n[![Anaconda-Server Badge](https://anaconda.org/conda-forge/pyrtcm/badges/version.svg)](https://anaconda.org/conda-forge/pyrtcm)\r\n[![Anaconda-Server Badge](https://img.shields.io/conda/dn/conda-forge/pyrtcm)](https://anaconda.org/conda-forge/pyrtcm)\r\n\r\n```shell\r\nconda install -c conda-forge pyrtcm\r\n```\r\n\r\n---\r\n## \u003ca name=\"reading\"\u003eReading (Streaming)\u003c/a\u003e\r\n\r\n```\r\nclass pyrtcm.rtcmreader.RTCMReader(stream, **kwargs)\r\n```\r\n\r\nYou can create a `RTCMReader` object by calling the constructor with an active stream object. \r\nThe stream object can be any data stream which supports a `read(n) -\u003e bytes` method (e.g. File or Serial, with \r\nor without a buffer wrapper). `pyrtcm` implements an internal `SocketStream` class to allow sockets to be read in the same way as other streams (see example below).\r\n\r\nIndividual RTCM messages can then be read using the `RTCMReader.read()` function, which returns both the raw binary data (as bytes) and the parsed data (as a `RTCMMessage`, via the `parse()` method). The function is thread-safe in so far as the incoming data stream object is thread-safe. `RTCMReader` also implements an iterator.\r\n\r\nExample -  Serial input:\r\n```python\r\nfrom serial import Serial\r\nfrom pyrtcm import RTCMReader\r\nwith Serial('/dev/tty.usbmodem14101', 9600, timeout=3) as stream:\r\n  rtr = RTCMReader(stream)\r\n  raw_data, parsed_data = rtr.read()\r\n  if parsed_data is not None:\r\n    print(parsed_data)\r\n```\r\n```\r\n\"\u003cRTCM(1077, DF002=1077, DF003=0, DF004=204137001, DF393=1, DF409=0, DF001_7=0, ..., DF404_15=-9556, DF404_16=-2148, DF404_17=-2174)\u003e\",     \r\n```\r\n\r\nExample - File input (using iterator).\r\n```python\r\nfrom pyrtcm import RTCMReader\r\nwith open('rtcmdata.log', 'rb') as stream:\r\n  rtr = RTCMReader(stream)\r\n  for raw_data, parsed_data in rtr:\r\n    print(parsed_data)\r\n```\r\n\r\nExample - Socket input (using iterator):\r\n```python\r\nimport socket\r\nfrom pyrtcm import RTCMReader\r\nwith socket.socket(socket.AF_INET, socket.SOCK_STREAM) as stream:\r\n  stream.connect((\"localhost\", 50007))\r\n  rtr = RTCMReader(stream)\r\n  for raw_data, parsed_data in rtr:\r\n    print(parsed_data)\r\n```\r\n\r\n---\r\n## \u003ca name=\"parsing\"\u003eParsing\u003c/a\u003e\r\n\r\nYou can parse individual RTCM messages using the static `RTCMReader.parse(data)` function, which takes a bytes array containing a binary RTCM message and returns a `RTCMMessage` object.\r\n\r\n**NB:** Once instantiated, an `RTCMMessage` object is immutable.\r\n\r\nExample:\r\n```python\r\nfrom pyrtcm import RTCMReader\r\nmsg = RTCMReader.parse(b\"\\xd3\\x00\\x13\u003e\\xd0\\x00\\x03\\x8aX\\xd9I\u003c\\x87/4\\x10\\x9d\\x07\\xd6\\xafH Z\\xd7\\xf7\")\r\nprint(msg)\r\n```\r\n```\r\n\u003cRTCM(1005, DF002=1005, DF003=0, DF021=0, DF022=1, DF023=1, DF024=1, DF141=0, DF025=4444030.8028, DF142=1, DF001_1=0, DF026=3085671.2349, DF364=0, DF027=3366658.256)\u003e\r\n```\r\n\r\nThe `RTCMMessage` object exposes different public attributes depending on its message type or 'identity'. Attributes are defined as data fields (`DF002`, `DF003`, etc.) e.g. the `1097` multiple signal message (MSM) contains the following data fields:\r\n\r\n```python\r\nprint(msg)\r\nprint(msg.identity)\r\nprint(msg.DF248)\r\nprint(msg.DF404_07)\r\n```\r\n```\r\n\"\u003cRTCM(1097, DF002=1097, DF003=0, DF248=204137001, DF393=1, DF409=0, DF001_7=0, DF411=0, DF412=0, DF417=0, DF418=0, DF394=216181732825628672, NSat=5, DF395=1073872896, NSig=2, DF396=1023, NCell=10, PRN_01=007, PRN_02=008, PRN_03=021, PRN_04=027, ..., DF404_07=5534, DF404_08=5545, DF404_09=-7726, DF404_10=-7733)\u003e\",             \r\n'1097'\r\n204137001\r\n5534\r\n```\r\n\r\nAttributes within repeating groups are parsed with a two-digit suffix (`DF419_01`, `DF419_02`, etc. See [example below](#iterating) for an illustration of how to iterate through grouped attributes).\r\n\r\nHelper methods are available to interpret the individual datafields:\r\n\r\n```python\r\nfrom pyrtcm import RTCM_DATA_FIELDS, datadesc\r\ndfname = \"DF012\"\r\nprint(RTCM_DATA_FIELDS[dfname])\r\nprint(datadesc(dfname))\r\n```\r\n```\r\n(INT20, 0.0001, \"GPS L1 PhaseRange - L1 Pseudorange\")\r\n'GPS L1 PhaseRange - L1 Pseudorange'\r\n```\r\n\r\nThe `payload` attribute always contains the raw payload as bytes.\r\n\r\n#### \u003ca name=\"iterating\"\u003eIterating Through Group Attributes\u003c/a\u003e\r\n\r\nTo iterate through a group of one or more repeating attributes in a given `RTCMMessage` object, the following construct can be used (in this illustration, repeating attributes CELLPRN, CELLSIG, DF405, DF406, DF407, DF408, DF420 and DF404 are extracted from an MSM 1077 message `msg` and collated in the array `msmarray`):\r\n\r\n```python\r\nmsmarray = []\r\nfor i in range(msg.NCell): # msg = MSM 1077, number of cells = NCell\r\n  vals = []\r\n  for attr in (\"CELLPRN\", \"CELLSIG\", \"DF405\", \"DF406\", \"DF407\", \"DF408\", \"DF420\", \"DF404\"):\r\n    val = getattr(msg, f\"{attr}_{i+1:02d}\")\r\n    vals.append(val)\r\n  msmarray.append(vals)\r\nprint(msmarray)\r\n```\r\n```shell\r\n[['005', '1C', 0.00014309026300907135, 0.00014193402603268623, 341, 45.0, 0, -0.9231], ..., ['030', '2L', -0.00030865520238876343, -0.00030898721888661385, 341, 41.0, 0, -0.2174]]\r\n```\r\n\r\nThe following dedicated helper methods are available to parse selected RTCM3 message types into a series of iterable data arrays:\r\n- `parse_msm` - for MSM message types (e.g. 1077, 1125, etc.).\r\n- `parse_4076_201` - for 4076_201 SSR (harmonic coefficients) message types.\r\n\r\n---\r\n## \u003ca name=\"generating\"\u003eGenerating\u003c/a\u003e\r\n\r\n```\r\nclass pyrtcm.rtcmmessage.RTCMMessage(**kwargs)\r\n```\r\n\r\nYou can create an `RTCMMessage` object by calling the constructor with the following keyword arguments:\r\n1. payload as bytes\r\n\r\nExample:\r\n\r\n```python\r\nfrom pyrtcm import RTCMMessage\r\nmsg = RTCMMessage(payload=b\"\u003e\\xd0\\x00\\x03\\x8aX\\xd9I\u003c\\x87/4\\x10\\x9d\\x07\\xd6\\xafH \")\r\nprint(msg)\r\n```\r\n```\r\n\u003cRTCM(1005, DF002=1005, DF003=0, DF021=0, DF022=1, DF023=1, DF024=1, DF141=0, DF025=4444030.8028, DF142=1, DF001_1=0, DF026=3085671.2349, DF364=0, DF027=3366658.256)\u003e\r\n```\r\n\r\n---\r\n## \u003ca name=\"serializing\"\u003eSerializing\u003c/a\u003e\r\n\r\nThe `RTCMMessage` class implements a `serialize()` method to convert a `RTCMMessage` object to a bytes array suitable for writing to an output stream.\r\n\r\ne.g. to create and send a `1005` message type:\r\n\r\n```python\r\nfrom serial import Serial\r\nfrom pyrtcm import RTCMMessage\r\nserialOut = Serial('COM7', 38400, timeout=5)\r\nmsg = RTCMMessage(payload=b\"\u003e\\xd0\\x00\\x03\\x8aX\\xd9I\u003c\\x87/4\\x10\\x9d\\x07\\xd6\\xafH \")\r\nprint(msg)\r\noutput = msg.serialize()\r\nprint(output)\r\nserialOut.write(output)\r\n```\r\n```\r\n\u003cRTCM(1005, DF002=1005, DF003=0, DF021=0, DF022=1, DF023=1, DF024=1, DF141=0, DF025=4444030.8028, DF142=1, DF001_1=0, DF026=3085671.2349, DF364=0, DF027=3366658.256)\u003e\r\nb'\\xd3\\x00\\x13\u003e\\xd0\\x00\\x03\\x8aX\\xd9I\u003c\\x87/4\\x10\\x9d\\x07\\xd6\\xafH Z\\xd7\\xf7'\r\n```\r\n\r\n---\r\n## \u003ca name=\"examples\"\u003eExamples\u003c/a\u003e\r\n\r\nThe following examples are available in the /examples folder:\r\n\r\n1. `rtcmpoller.py` - illustrates how to read and display RTCM messages 'concurrently' with other tasks using threads and queues. This represents a useful generic pattern for many end user applications.\r\n1. `rtcmfile.py` - illustrates how to stream RTCM data from binary log file.\r\n1. `rtcmsocket.py` - illustrates how to implement a TCP Socket reader for RTCM messages using RTCMReader iterator functionality.\r\n1. `msmparser.py` - illustrates how to parse RTCM3 MSM (multiple signal messages) into a series of iterable data arrays keyed on satellite PRN and signal ID.\r\n1. `rtcm_ntrip_client.py` - illustrates a simple [NTRIP](https://en.wikipedia.org/wiki/Networked_Transport_of_RTCM_via_Internet_Protocol) client using pyrtcm to parse the RTCM3 output.\r\n\r\n---\r\n## \u003ca name=\"extensibility\"\u003eExtensibility\u003c/a\u003e\r\n\r\nThe RTCM protocol is principally defined in the modules `rtcmtypes_core.py` and `rtcmtypes_get.py` as a series of dictionaries. RTCM uses a series of pre-defined data fields (\"DF002\", DF003\" etc.), each of which has a designated data type (UINT32, etc.). Message payload definitions must conform to the following rules:\r\n\r\n```\r\n1. datafield names must be unique within each message class\r\n2. datafield types must be one of the valid data fields (\"DF026\", \"DF059\", etc.)\r\n3. repeating or bitfield groups must be defined as a tuple ('numr', {dict}), where:\r\n   'numr' is either:\r\n     a. an integer representing a fixed number of repeats e.g. 32\r\n     b. a string representing the name of a preceding attribute containing the number of repeats e.g. 'DF029'\r\n   {dict} is the nested dictionary of repeating items or bitfield group\r\n```\r\n\r\nRepeating attribute names are parsed with a two-digit suffix (\"DF030_01\", \"DF030_02\", etc.). Nested repeating groups are supported.\r\n\r\n---\r\n## \u003ca name=\"cli\"\u003eCommand Line Utility\u003c/a\u003e\r\n\r\nA command line utility `gnssstreamer` is available via the `pygnssutils` package. This is capable of reading and parsing NMEA, UBX and RTCM3 data from a variety of input sources (e.g. serial, socket and file) and outputting to a variety of media in a variety of formats. See https://github.com/semuconsulting/pygnssutils for further details.\r\n\r\nTo install `pygnssutils`:\r\n```\r\npython3 -m pip install --upgrade pygnssutils\r\n```\r\n\r\nFor help with the `gnssstreamer` utility, type:\r\n```\r\ngnssstreamer -h\r\n```\r\n\r\n---\r\n## \u003ca name=\"gui\"\u003eGraphical Client\u003c/a\u003e\r\n\r\nA python/tkinter graphical GPS client which supports NMEA, UBX, RTCM3, NTRIP and SPARTN protocols is available at: \r\n\r\n[https://github.com/semuconsulting/PyGPSClient](https://github.com/semuconsulting/PyGPSClient)\r\n\r\n---\r\n## \u003ca name=\"author\"\u003eAuthor \u0026 License Information\u003c/a\u003e\r\n\r\nsemuadmin@semuconsulting.com\r\n\r\n![License](https://img.shields.io/github/license/semuconsulting/pyrtcm.svg)\r\n\r\n`pyrtcm` is maintained entirely by unpaid volunteers. It receives no funding from advertising or corporate sponsorship. If you find the utility useful, please consider sponsoring the project with the price of a coffee...\r\n\r\n[![Sponsor](https://github.com/semuconsulting/pyubx2/blob/master/images/sponsor.png?raw=true)](https://buymeacoffee.com/semuconsulting)\r\n\r\n[![Freedom for Ukraine](https://github.com/semuadmin/sandpit/blob/main/src/sandpit/resources/ukraine200.jpg?raw=true)](https://u24.gov.ua/)\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsemuconsulting%2Fpyrtcm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsemuconsulting%2Fpyrtcm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsemuconsulting%2Fpyrtcm/lists"}