From c6b104270138ebd2fa8a23c936bc15928396e89c Mon Sep 17 00:00:00 2001 From: Rossen Georgiev Date: Wed, 20 Jan 2016 09:25:34 +0000 Subject: [PATCH] added doc source files (sphinx) [ci skip] --- README.rst | 148 ++------------------ docs/.gitignore | 3 + docs/Makefile | 216 +++++++++++++++++++++++++++++ docs/conf.py | 289 +++++++++++++++++++++++++++++++++++++++ docs/examples.rst | 130 ++++++++++++++++++ docs/index.rst | 49 +++++++ docs/parse_formats.rst | 298 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 998 insertions(+), 135 deletions(-) create mode 100644 docs/.gitignore create mode 100644 docs/Makefile create mode 100644 docs/conf.py create mode 100644 docs/examples.rst create mode 100644 docs/index.rst create mode 100644 docs/parse_formats.rst diff --git a/README.rst b/README.rst index f004e2b..07c13e2 100644 --- a/README.rst +++ b/README.rst @@ -3,152 +3,30 @@ APRS library for Python |pypi| |coverage| |master_build| -A tiny library for dealing with APRS. It can be used to connect and listen to the APRS-IS feed as well as upload. -Parsing of packets is also possible, but the entire spec is not fully implemented yet. -The following is supported: +A python library for dealing with APRS. +It can be used to interact with APRS-IS servers, sending and receiving. +Parsing functionally is also included, but currently doesn't implement the full spec. -- normal/compressed position reports -- objects -- mic-e position report -- messages (inc. telemetry, bulletins, etc) -- base91 comment telemetry extension -- altitude extension -- beacons +See `full documentation `_. -Packets can often contain characters outside of 7-bit ASCII. -``aprslib.parse()`` will attempt to guess the charset and return ``unicode`` strings using these steps and in that order: +Installation +============ -1. Attempt to decode string as ``utf-8`` -2. Attempt to guess the charset using ``chardet`` module (if installed), decode if confidence factor is sufficient -3. Finally, decode as ``latin-1`` - -Install ------------ - -You can grab the latest release from https://pypi.python.org/pypi/aprslib or via ``pip`` - -.. code:: bash +To install the latest release from ``pypi``:: pip install aprslib -Examples ------------ +To install the latest dev version from the `Github repo `_:: -Parsing -^^^^^^^ - -.. code:: python - - import aprslib - packet = aprslib.parse("M0XER-4>APRS64,TF3RPF,WIDE2*,qAR,TF3SUT-2:!/.(M4I^C,O `DXa/A=040849|#B>@\"v90!+|") - -.. code:: python - - {'altitude': 12450.7752, - 'comment': 'Xa', - 'format': 'compressed', - 'from': 'M0XER-4', - 'gpsfixstatus': 1, - 'latitude': 64.11987367625208, - 'longitude': -19.070654142799384, - 'messagecapable': False, - 'path': ['TF3RPF', 'WIDE2*', 'qAR', 'TF3SUT-2'], - 'raw': 'M0XER-4>APRS64,TF3RPF,WIDE2*,qAR,TF3SUT-2:!/.(M4I^C,O `DXa/A=040849|#B>@"v90!+|', - 'symbol': 'O', - 'symbol_table': '/', - 'telemetry': {'bits': '00000000', - 'seq': 215, - 'vals': [2670, 176, 2199, 10, 0]}, - 'to': 'APRS64', - 'via': 'TF3SUT-2'} - -Keep in mind that this function raises exceptions if the packet format is invalid or not supported. - -.. code:: python - - try: - packet = aprslib.parse("M0XER-4>APRS64,TF3RPF,WIDE2*,qAR,TF3SUT-2:!/.(M4I^C,O `DXa/A=040849|#B>@\"v90!+|") - except (aprslib.ParseError, aprslib.UnknownFormat) as exp: - pass + pip install git+https://github.com/rossengeorgiev/aprs-python -APRS-IS -^^^^^^^ +Contribution +============ -.. code:: python +| Suggestions, issues and pull requests are welcome. +| Just visit the repository at https://github.com/rossengeorgiev/aprs-python - import aprslib - - def callback(packet): - print packet - - AIS = aprslib.IS("N0CALL") - AIS.connect() - # by default `raw` is False, then each line is ran through aprslib.parse() - AIS.consumer(callback, raw=True) - -.. code:: text - - VK2TRL>APU25N,qAR,VK3KAW:;AWARC *270052z3602.24S/14656.26E-Albury/Wodonga A.R.C. see www.awarc.org - DL1TMF-1>APRS,TCPIP*,qAS,DL1TMF:!5022.38N/01146.58E- http://www.dl1tmf.de - KF4HFE-1>S3SX9S,K4TQR-1,WIDE1,AB4KN-2*,WIDE2,qAR,W4GR-10:`r,^l\Lk/"5h} - ... - -The ``IS`` class makes use of the ``logging`` module. -There are various levels of verbosity available for ``IS``. -The only non-standard levels are 9 (unknown format errors) and 11 (parse errors). -Here is a simple example: - -.. code:: python - - import aprslib - import logging - - logging.basicConfig(level=logging.DEBUG) # level=10 - - AIS = aprslib.IS("N0CALL") - AIS.connect() - AIS.consumer(lambda x: None, raw=True) - -.. code:: text - - INFO:aprslib.IS:Attempting connection to rotate.aprs.net:10152 - INFO:aprslib.IS:Connected to 205.233.35.52:10152 - DEBUG:aprslib.IS:Banner: # aprsc 2.0.14-g28c5a6a - INFO:aprslib.IS:Sending login information - DEBUG:aprslib.IS:Server: # logresp N0CALL unverified, server EIGHTH - INFO:aprslib.IS:Login successful (receive only) - DEBUG:aprslib.parse:Parsing: PY4MM-15>Q8U11W,PU4YRM-15*,WIDE3-2,qAR,PP2MD-1:'L.Kl #/"=h}APRS DIGI - Uberlandia - MG - DEBUG:aprslib.parse:Attempting to parse as mic-e packet - DEBUG:aprslib.parse:Parsed ok. - ... - -Uploading packets to APRS-IS is possible through the ``sendall()`` method in ``IS``. -The method assumes a single line/packet per call. The parameters may end with ``\r\n``, but it's not required. - -.. code:: python - - import aprslib - - # a valid passcode for the callsign is required in order to send - AIS = aprslib.IS("N0CALL", passcode="123456", port=14580) - AIS.connect() - # send a single status message - AIS.sendall("N0CALL>APRS,TCPIP*:>status text") - -A passcode generation function is also provided. - -CHANGES -^^^^^^^ - -You can find the latest changes between versions in the CHANGES file. - -Docs -^^^^ - -.. code:: bash - - $ python -m pydoc aprslib .. |pypi| image:: https://img.shields.io/pypi/v/aprslib.svg?style=flat&label=latest%20version :target: https://pypi.python.org/pypi/aprslib diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..fd39b78 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,3 @@ +_build +_static +_templates diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..88bd7cc --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,216 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) + $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don\'t have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +.PHONY: clean +clean: + rm -rf $(BUILDDIR)/* + +.PHONY: html +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +.PHONY: dirhtml +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +.PHONY: singlehtml +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +.PHONY: pickle +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +.PHONY: json +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +.PHONY: htmlhelp +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +.PHONY: qthelp +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/aprslib.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/aprslib.qhc" + +.PHONY: applehelp +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +.PHONY: devhelp +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/aprslib" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/aprslib" + @echo "# devhelp" + +.PHONY: epub +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +.PHONY: latex +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +.PHONY: latexpdf +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: latexpdfja +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: text +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +.PHONY: man +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +.PHONY: texinfo +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +.PHONY: info +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +.PHONY: gettext +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +.PHONY: changes +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +.PHONY: linkcheck +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +.PHONY: doctest +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +.PHONY: coverage +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +.PHONY: xml +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +.PHONY: pseudoxml +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..dca4357 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,289 @@ +# -*- coding: utf-8 -*- +# +# aprslib documentation build configuration file, created by +# sphinx-quickstart on Wed Jan 20 07:25:05 2016. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath('..')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', + 'sphinx.ext.githubpages', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'aprslib' +copyright = u'2016, Rossen Georgiev' +author = u'Rossen Georgiev' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'0.6.42' +# The full version, including alpha/beta/rc tags. +release = u'0.6.42' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'classic' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. +# " v documentation" by default. +#html_title = u'aprslib v0.6.42' + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not None, a 'Last updated on:' timestamp is inserted at every page +# bottom, using the given strftime format. +# The empty string is equivalent to '%b %d, %Y'. +#html_last_updated_fmt = None + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# 'ja' uses this config value. +# 'zh' user can custom change `jieba` dictionary path. +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'aprslibdoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'aprslib.tex', u'aprslib Documentation', + u'Rossen Georgiev', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'aprslib', u'aprslib Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'aprslib', u'aprslib Documentation', + author, 'aprslib', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100644 index 0000000..df51cf5 --- /dev/null +++ b/docs/examples.rst @@ -0,0 +1,130 @@ +Example usage +************* + +This section includes examples of basic usage + + +Parsing a packet +================ + +.. code:: python + + >>> import aprslib + >>> aprslib.parse("M0XER-4>APRS64,TF3RPF,WIDE2*,qAR,TF3SUT-2:!/.(M4I^C,O `DXa/A=040849|#B>@\"v90!+|") + + {'altitude': 12450.7752, + 'comment': 'Xa', + 'format': 'compressed', + 'from': 'M0XER-4', + 'gpsfixstatus': 1, + 'latitude': 64.11987367625208, + 'longitude': -19.070654142799384, + 'messagecapable': False, + 'path': ['TF3RPF', 'WIDE2*', 'qAR', 'TF3SUT-2'], + 'raw': 'M0XER-4>APRS64,TF3RPF,WIDE2*,qAR,TF3SUT-2:!/.(M4I^C,O `DXa/A=040849|#B>@"v90!+|', + 'symbol': 'O', + 'symbol_table': '/', + 'telemetry': {'bits': '00000000', + 'seq': 215, + 'vals': [2670, 176, 2199, 10, 0]}, + 'to': 'APRS64', + 'via': 'TF3SUT-2'} + +.. note:: + Keep in mind that this function raises exceptions + if the packet format is invalid or not supported. + +.. code:: python + + try: + packet = aprslib.parse("M0XER-4>APRS64,TF3RPF,WIDE2*,qAR,TF3SUT-2:!/.(M4I^C,O `DXa/A=040849|#B>@\"v90!+|") + except (aprslib.ParseError, aprslib.UnknownFormat) as exp: + pass + + +APRS-IS +======= + +Connect to a feed +----------------- + +Connecting to APRS-IS is done using the :py:class:`aprslib.IS` module. + +.. code:: python + + import aprslib + + def callback(packet): + print packet + + AIS = aprslib.IS("N0CALL") + AIS.connect() + # by default `raw` is False, then each line is ran through aprslib.parse() + AIS.consumer(callback, raw=True) + +Program output: + +.. code:: text + + VK2TRL>APU25N,qAR,VK3KAW:;AWARC *270052z3602.24S/14656.26E-Albury/Wodonga A.R.C. see www.awarc.org + DL1TMF-1>APRS,TCPIP*,qAS,DL1TMF:!5022.38N/01146.58E- http://www.dl1tmf.de + KF4HFE-1>S3SX9S,K4TQR-1,WIDE1,AB4KN-2*,WIDE2,qAR,W4GR-10:`r,^l\Lk/"5h} + ... + + +Logging +------- + +The :py:class:`aprslib.IS` module makes use of the ``logging`` module. +There are various levels of verbosity available for ``IS``. +The only non-standard levels are 9 (unknown format errors) and 11 (parse errors). +Here is a simple example: + +.. code:: python + + import aprslib + import logging + + logging.basicConfig(level=logging.DEBUG) # level=10 + + AIS = aprslib.IS("N0CALL") + AIS.connect() + AIS.consumer(lambda x: None, raw=True) + +Program output: + +.. code:: text + + INFO:aprslib.IS:Attempting connection to rotate.aprs.net:10152 + INFO:aprslib.IS:Connected to 205.233.35.52:10152 + DEBUG:aprslib.IS:Banner: # aprsc 2.0.14-g28c5a6a + INFO:aprslib.IS:Sending login information + DEBUG:aprslib.IS:Server: # logresp N0CALL unverified, server EIGHTH + INFO:aprslib.IS:Login successful (receive only) + DEBUG:aprslib.parse:Parsing: PY4MM-15>Q8U11W,PU4YRM-15*,WIDE3-2,qAR,PP2MD-1:'L.Kl #/"=h}APRS DIGI - Uberlandia - MG + DEBUG:aprslib.parse:Attempting to parse as mic-e packet + DEBUG:aprslib.parse:Parsed ok. + ... + + +Sending a packet +---------------- + +Uploading packets to APRS-IS is possible through the ``sendall()`` method in ``IS``. +The method assumes a single line/packet per call. The parameters may end with ``\r\n``, but it's not required. + +.. code:: python + + import aprslib + + # a valid passcode for the callsign is required in order to send + AIS = aprslib.IS("N0CALL", passcode="123456", port=14580) + AIS.connect() + # send a single status message + AIS.sendall("N0CALL>APRS,TCPIP*:>status text") + +Passcodes +--------- + +In order for the server to accept your packets, you need to send a valid passcode. +See :py:func:`aprslib.passcode` diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..74aaa83 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,49 @@ +aprslib documentation +********************* + +|pypi| |coverage| + +A python library for dealing with APRS. +It can be used to interact with APRS-IS servers, sending and receiving. +Parsing functionally is also included, but currently doesn't implement the full spec. +See the section for :ref:`supported formats`. + + +Installation +============ + +To install the latest release from ``pypi``:: + + pip install aprslib + +To install the latest dev version from the `Github repo `_:: + + pip install git+https://github.com/rossengeorgiev/aprs-python + +Contents +======== + +| Version: |version| +| Generated on: |today| + +.. toctree:: + :maxdepth: 3 + + examples + parse_formats + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + +.. |pypi| image:: https://img.shields.io/pypi/v/aprslib.svg?style=flat&label=pypi%20version + :target: https://pypi.python.org/pypi/aprslib + :alt: Latest version released on PyPi + +.. |coverage| image:: https://img.shields.io/coveralls/rossengeorgiev/aprs-python/master.svg?style=flat + :target: https://coveralls.io/r/rossengeorgiev/aprs-python?branch=master + :alt: Test coverage diff --git a/docs/parse_formats.rst b/docs/parse_formats.rst new file mode 100644 index 0000000..abeb7d1 --- /dev/null +++ b/docs/parse_formats.rst @@ -0,0 +1,298 @@ +Packet parsing +************** + +An overview of the parsing functionality. + +APRS Reference +============== + +The implementation follows the APRS Protocol Reference + +* Version 1.0: http://www.aprs.org/doc/APRS101.PDF +* Version 1.1: http://www.aprs.org/aprs11.html +* Version 1.2: http://www.aprs.org/aprs12.html + + +Encodings +========= + +Packets can often contain characters outside of 7-bit ASCII. +:py:func:`aprslib.parse` will attempt to guess the charset and return ``unicode`` strings using these steps and in that order: + +1. Attempt to decode string as ``utf-8`` +2. Attempt to guess the charset using ``chardet`` module (if installed), decode if confidence factor is sufficient +3. Finally, decode as ``latin-1`` + + +.. _sup_formats: + +Supported formats +================= + +- normal/compressed position reports +- mic-e position reports +- objects reports +- weather reports +- status reports +- messages (inc. telemetry, bulletins, etc) +- base91 comment telemetry extension +- altitude extension +- beacons + + +Position reports +================ + +Normal +------ + +.. code:: python + + >>> aprslib.parse("FROMCALL>TOCALL:!4903.50N/07201.75W-Test /A=001234") + + {'altitude': 376.1232, + 'comment': u'Test', + 'format': 'uncompressed', + 'from': u'FROMCALL', + 'latitude': 49.05833333333333, + 'longitude': -72.02916666666667, + 'messagecapable': False, + 'path': [], + 'posambiguity': 0, + 'raw': u'FROMCALL>TOCALL:!4903.50N/07201.75W-Test /A=001234', + 'symbol': u'-', + 'symbol_table': u'/', + 'to': u'TOCALL', + 'via': ''} + + +Compressed +---------- + +.. code:: python + + >>> aprslib.parse("M0XER-4>APRS64,TF3RPF,WIDE2*,qAR,TF3SUT-2:!/.(M4I^C,O `DXa/A=040849|#B>@\"v90!+|") + + {'altitude': 12450.7752, + 'comment': u'Xa', + 'format': 'compressed', + 'from': u'M0XER-4', + 'gpsfixstatus': 1, + 'latitude': 64.11987367625208, + 'longitude': -19.070654142799384, + 'messagecapable': False, + 'path': [u'TF3RPF', u'WIDE2*', u'qAR', u'TF3SUT-2'], + 'raw': u'M0XER-4>APRS64,TF3RPF,WIDE2*,qAR,TF3SUT-2:!/.(M4I^C,O `DXa/A=040849|#B>@"v90!+|', + 'symbol': u'O', + 'symbol_table': u'/', + 'telemetry': {'bits': '00000000', + 'seq': 215, + 'vals': [2670, 176, 2199, 10, 0]}, + 'to': u'APRS64', + 'via': u'TF3SUT-2'} + +With timestamp: + +.. code:: python + + >>> aprslib.parse("FROMCALL>TOCALL:/092345z4903.50N/07201.75W>Test1234") + + {'comment': u'Test1234', + 'format': 'uncompressed', + 'from': u'FROMCALL', + 'latitude': 49.05833333333333, + 'longitude': -72.02916666666667, + 'messagecapable': False, + 'path': [], + 'posambiguity': 0, + 'raw': u'FROMCALL>TOCALL:/092345z4903.50N/07201.75W>Test1234', + 'raw_timestamp': u'092345z', + 'symbol': u'>', + 'symbol_table': u'/', + 'timestamp': 1452383100, + 'to': u'TOCALL', + 'via': ''} + +Mic-E +----- + +.. code:: python + + >>> aprslib.parse('FROMCALL>SUSUR1:`CF"l#![/`"3z}_ ') + + {'altitude': 8, + 'comment': u'`_', + 'course': 305, + 'format': 'mic-e', + 'from': u'FROMCALL', + 'latitude': 35.58683333333333, + 'longitude': 139.701, + 'mbits': u'111', + 'mtype': 'M0: Off Duty', + 'path': [], + 'posambiguity': 0, + 'raw': u'FROMCALL>SUSUR1:`CF"l#![/`"3z}_ ', + 'speed': 0.0, + 'symbol': u'[', + 'symbol_table': u'/', + 'to': u'SUSUR1', + 'via': ''} + +Objects +======= + +.. code:: python + + >>> aprslib.parse('FROMCALL>TOCALL:;LEADER *092345z4903.50N/07201.75W>088/036') + + {'alive': True, + 'comment': u'', + 'course': 88, + 'format': 'object', + 'from': u'FROMCALL', + 'latitude': 49.05833333333333, + 'longitude': -72.02916666666667, + 'object_format': 'uncompressed', + 'object_name': u'LEADER ', + 'path': [], + 'posambiguity': 0, + 'raw': u'FROMCALL>TOCALL:;LEADER *092345z4903.50N/07201.75W>088/036', + 'raw_timestamp': u'092345z', + 'speed': 66.672, + 'symbol': u'>', + 'symbol_table': u'/', + 'timestamp': 1452383100, + 'to': u'TOCALL', + 'via': ''} + +Weather +======= + +Positionless +------------ + +.. code:: python + + >>> aprslib.parse('FROMCALL>TOCALL:_10090556c220s004g005t077r000p000P000h50b09900wRSW') + + {'comment': u'wRSW', + 'format': 'wx', + 'from': u'FROMCALL', + 'path': [], + 'raw': u'FROMCALL>TOCALL:_10090556c220s004g005t077r000p000P000h50b09900wRSW', + 'to': u'TOCALL', + 'via': '', + 'weather': {'humidity': 50, + 'pressure': 990.0, + 'rain_1h': 0.0, + 'rain_24h': 0.0, + 'rain_since_midnight': 0.0, + 'temperature': 25.0, + 'wind_direction': 220, + 'wind_gust': 2.2352, + 'wind_speed': 1.78816}, + 'wx_raw_timestamp': u'10090556'} + +Comment field +------------- + +.. code:: python + + >>> aprslib.parse("FROMCALL>TOCALL:=4903.50N/07201.75W_225/000g000t050r000p001...h00b10138dU2k") + + {'comment': u'...dU2k', + 'format': 'uncompressed', + 'from': u'FROMCALL', + 'latitude': 49.05833333333333, + 'longitude': -72.02916666666667, + 'messagecapable': True, + 'path': [], + 'posambiguity': 0, + 'raw': u'FROMCALL>TOCALL:=4903.50N/07201.75W_225/000g000t050r000p001...h00b10138dU2k', + 'symbol': u'_', + 'symbol_table': u'/', + 'to': u'TOCALL', + 'via': '', + 'weather': {'humidity': 0, + 'pressure': 1013.8, + 'rain_1h': 0.0, + 'rain_24h': 0.254, + 'temperature': 10.0, + 'wind_direction': 225, + 'wind_gust': 0.0, + 'wind_speed': 0.0}} + +Status report +============= + +.. code:: python + + >>> aprslib.parse('FROMCALL>TOCALL:>status text') + + {'format': 'status', + 'from': u'FROMCALL', + 'path': [], + 'raw': u'FROMCALL>TOCALL:>status text', + 'status': u'status text', + 'to': u'TOCALL', + 'via': ''} + +Messages +======== + + +Regular +------- + +.. code:: python + + >>> aprslib.parse('FROMCALL>TOCALL::ADDRCALL :message text') + + {'addresse': u'ADDRCALL', + 'format': 'message', + 'from': u'FROMCALL', + 'message_text': u'message text', + 'path': [], + 'raw': u'FROMCALL>TOCALL::FROMCALL :message text', + 'to': u'TOCALL', + 'via': ''} + +Telemetry configuration +----------------------- + + +.. code:: python + + >>> aprslib.parse('FROMCALL>TOCALL::FROMCALL :PARM.Vin,Rx1h,Dg1h,Eff1h,A5,O1,O2,O3,O4,I1,I2,I3,I4') + + {'addresse': 'FROMCALL', + 'format': 'telemetry-message', + 'from': 'FROMCALL', + 'path': [], + 'raw': 'FROMCALL>TOCALL::FROMCALL :PARM.Vin,Rx1h,Dg1h,Eff1h,A5,O1,O2,O3,O4,I1,I2,I3,I4', + 'tPARM': ['Vin', 'Rx1h', 'Dg1h', 'Eff1h', 'A5', 'O1', 'O2', 'O3', 'O4', 'I1', 'I2', 'I3', 'I4'], + 'to': 'TOCALL', + 'via': ''} + + >>> aprslib.parse('FROMCALL>TOCALL::FROMCALL :UNIT.Volt,Pkt,Pkt,Pcnt,None,On,On,On,On,Hi,Hi,Hi,Hi') + + {'addresse': 'FROMCALL', + 'format': 'telemetry-message', + 'from': 'FROMCALL', + 'path': [], + 'raw': 'FROMCALL>TOCALL::FROMCALL :UNIT.Volt,Pkt,Pkt,Pcnt,None,On,On,On,On,Hi,Hi,Hi,Hi', + 'tUNIT': ['Volt', 'Pkt', 'Pkt', 'Pcnt', 'None', 'On', 'On', 'On', 'On', 'Hi', 'Hi', 'Hi', 'Hi'], + 'to': 'TOCALL', + 'via': ''} + + >>> aprslib.parse('FROMCALL>TOCALL::FROMCALL :EQNS.0,0.075,0,0,10,0,0,10,0,0,1,0,0,0,0') + + {'addresse': 'FROMCALL', + 'format': 'telemetry-message', + 'from': 'FROMCALL', + 'path': [], + 'raw': 'FROMCALL>TOCALL::FROMCALL :EQNS.0,0.075,0,0,10,0,0,10,0,0,1,0,0,0,0', + 'tEQNS': [[0, 0.075, 0], [0, 10, 0], [0, 10, 0], [0, 1, 0], [0, 0, 0]], + 'to': 'TOCALL', + 'via': ''} +