- libsbp Development Procedures
This document summarizes some practices around contributions to this library. These instructions don't come with a warranty yet, so please feel free to update it to mirror reality.
Adding new SBP messages is currently a very organic, social process. This is likely to change in the future.
-
Read, understand, and imitate the current SBP definition syntax by looking at the annotated
exampleandexistingmessages. -
Add a message definition to the approprate package, or create a new one if needed. Read the Message Guidelines below.
-
Increment
number_of_messagesinpython/tests/sbp/test_table.pyby the corresponding number of new messages. -
If adding a new "group" of messages (adding a new YAML file to
spec/yaml/swiftnav/sbp), add the new message group topython/sbp/table.pyandjavascript/sbp/msg.js. -
Generate new clients and documentation by running
make all. Verify that the generated code, which isn't too complicated, meets your expectations, as allowed messages are limited by the underlying language implementation. For example, you can't specify a message that has a variable-length array in the middle of the message, since the generated SBP structs for the C client will materialize a 0-length array C99 extension in the middle of the struct. GCC won't compile this. -
(Optional) Add a
testcase and update the appropriate language libaries usingmake gen. If a test case is not added, incrementEXPECTED_MISSING_MESSAGESinpython/tests/sbp/test_messages.py. -
Run
make test. -
Submit a pull request.
-
If Swift's internal test tooling needs to be updated to use your new message, deploy the updated Python client first, and then the C client. We haven't quite decided on the details of this process.
There are some tools that can assist with generating YAML based tests, like the
ones already defined in the test directory.
These YAML files are used to generate tests in the various languages that libsbp
supports, to ensure that serializing and deserializing messages works as
intended
For messages that are already being sent (eg: by Starling, or by a Piksi), the
generator/missing.py script can be used to connect to a socket and
automatically generate tests for any received messages that do not already have
tests.
Usage for missing:
python missing.py --host [HOST] --port [PORT]The json2test script can be used to automatically generate tests for newly
defined messages. The json2test script uses uv to
manage its dependencies, which can be installed following the instructions on that
website
To use json2test a JSON file should be hand written with example contents of a
message. For example, to generate tests for the MSG_HEARTBEAT message (which
contains a single field named flags), you would generate a JSON file of the
form:
{
"msg_type": 65535,
"flags": 12345,
"sender": 9876
}And then generate a test for using json2test with:
uv -n run json2test.py --input heartbeat.json --output ../spec/tests/yaml/swiftnav/sbp/system/test_MsgHeartbeat.yamlNOTE because the json2test file imports the local version of sbp & the build
system of it all is a little slapdash, this command will ONLY work if called from
inside of libsbp/generator/ directory because the ${PROJECT_ROOT} in the script
declartion of json2test.py is whatever the PWD is when the command is called, and
caching is turned off.
Usage for json2test
uv -n run json2test --input [PATH_TO_JSON_IN] --output [PATH_TO_YAML_OUT]- The
msg_typecan also be provided through a CLI parameter, with--msg-id [MESSAGE_ID]
Some thoughts to consider when adding a new message:
-
Messages should be as simple as possible but no simpler.
-
Build for the future. Once a message is promoted to "stable" its general structure must never change. You should think very hard about what kinds of fields you may wish you had included in the future. Consider adding some extra reserved bits for future expansion (within reason).
-
Size matters. SBP is designed to be a lightweight protocol used by small, embedded devices. Consider using fixed point representations where possible, and don't specify excessive precision or range. Avoid non-byte aligned types though, they are a pain to decode.
-
Generalize. SBP is a protocol that exists separately from any specific device that uses SBP (e.g. Piksi). It should be with a very heavy heart that you include anything device specific in the protocol, and even then it should only be added to a device specific package.
-
Draft message changes. There are different ways to change draft messages, which are allowed to be in flux. Doing so isn't free of consequences, particularly if that message is used by others during internal development. Changing a message name, its ID, or its field contents is fine, as long as the migrating consumers is a well-understood process.
It's highly recommended to use the docker container to run the release process, the docker container can be pulled from DockerHub and launched via this command:
docker run -v $PWD:/mnt/workspace -i -t swiftnav/libsbp-build:2025-10-29You can invoke individual stages like so:
docker run -v $PWD:/mnt/workspace -i -t swiftnav/libsbp-build:2025-10-29 \
/bin/bash -c "make python"Check this link for newer tags.
Oh boy, so you've decided to release a new version of libsbp. It's recommended
this process is performed using the above docker container. You'll likely want
to run the git commands outside of the container and the make ... commands
inside the container (so you don't have to setup git inside the docker container).
This process describes running make <language> in multiple places. These targets will
both regenerate the language bindings and then build and run any test suites.
Skipping tests should not be done by default, but most languages have a "gen" target
available - make gen-<language> - which will only regenerate the bindings without
running tests. This can be used to split or speed up the process should any errors occur
and something needs to be repeated.
-
It's easiest to do this on the master branch. Start by tagging the release version:
# Produces most recent tag (e.g., v2.7.5) git describe --abbrev=0 --tags # Increment that value, create a new one (e.g, v2.7.6) git tag -a <INCREMENTED_TAG> -m "Version <INCREMENTED_TAG> of libsbp."
For library versions (i.e.
<INCREMENTED_TAG>) we try to follow SemVer. For message versioning refer to this document on versioning. -
Make sure that the repo is reported as clean, e.g.
git describe --tags --dirty --always
This will ensure that version information for language libraries will be generated cleanly.
-
Run make targets for each language and re-tag. For python:
make python git add python/sbp/RELEASE-VERSION git commit -m 'Release <INCREMENTED_TAG>' git tag -f -a INCREMENTED_TAG -m "Version <INCREMENTED_TAG> of libsbp."
For Java, jsonschema, and Protobuf (these should not require bumping the git tag, unless the generated files are out of date):
make java jsonschema protobuf
For C, Haskell and JavaScript (JavaScript, make needs to be run twice to update the package information):
make c haskell rust git add c/include/libsbp/version.h haskell/sbp.cabal rust/sbp/Cargo.toml git commit --amend -a -m 'Release <INCREMENTED_TAG>' git tag -f -a INCREMENTED_TAG -m "Version INCREMENTED_TAG of libsbp."
For JavaScript, needs to be run twice to update the package information
make javascript make javascript git add javascript/sbp/RELEASE-VERSION package.json package-lock.json git commit --amend -a -m 'Release <INCREMENTED_TAG>' git tag -f -a INCREMENTED_TAG -m "Version INCREMENTED_TAG of libsbp."
For Kaitai
make kaitai git add kaitai/ksy/sbp.ksy git commit --amend -a -m 'Release <INCREMENTED_TAG>' git tag -f -a INCREMENTED_TAG -m "Version INCREMENTED_TAG of libsbp."
-
Build the docs:
make docs
Be sure to inspect the docs manually, as LaTeX sometimes needs to be run multiple times to compile properly. If something looks off with the docs, run
make docsrepeatedly until the issue is fixed.Then commit the docs and re-tag:
git add docs/sbp.pdf git commit --amend -a -m 'Release <INCREMENTED_TAG>' git tag -f -a INCREMENTED_TAG -m "Version INCREMENTED_TAG of libsbp."
-
Update the CHANGELOG details with
make release. Submit a pull request and get it merged. This requires a GitHub token to be loaded into your environment atCHANGELOG_GITHUB_TOKEN. The Makefile will use docker to run the tool that generates aDRAFT_CHANGELOG.md.It's generally a good idea to scrub any internal ticket numbers from
DRAFT_CHANGELOG.mdas they add unnecessary noise for customers. -
Verify that package dependencies, their version numbers, and the libsbp version number in the C, Python, JavaScript, and LaTeX developer documentation are consistent.
-
Push the release to GitHub:
git push origin master <INCREMENTED_TAG>
-
Create a release on GitHub and add the section for the new release from
DRAFT_CHANGELOG.mdto the release notes.It's also nice to add a link to the protocol docs for that release below the "Full Changelog" link, for example:
[Protocol Documentation](https://github.com/swift-nav/libsbp/blob/v3.4.6/docs/sbp.pdf)
-
Prep for the next development cycle. Create an empty commit so that version numbers get regenerated with the
-alphatag of the next release, then rebuild all languagesAgain, javascript needs to be built twice to get the correct package versions
git commit --allow-empty -m "prep for next release #no_auto_pr" make all make javascript git add python/sbp/RELEASE-VERSION c/include/libsbp/version.h haskell/sbp.cabal javascript/sbp/RELEASE-VERSION package.json package-lock.json rust/sbp/Cargo.toml docs/sbp.pdf kaitai/ksy/sbp.ksy git commit --amend -a -m 'prep for next release #no_auto_pr' git push origin master
-
Distribute release packages. You can attempt to run all releases with
make dist-- this will likely not work through... it is advisable to run each dist target separately. In particular:make dist-javascriptmake dist-haskellmake dist-rust(see section on Rust below)make dist-python(see section on Python below)make dist-java(see section on Java below)
You may need credentials on the appropriate package repositories. Ignore the GPG error in
stack, the package will get uploaded correctly anyway. If the release is a Python only change it may be appropriate to just publish to PyPI withmake dist-python(see section on Python below) -- we typically update all other supported languages when we make an official firmware release. -
Releases are not only never perfect, they never really end. Please pay special attention to any downstream projects or users that may have issues or regressions as a consequence of the release version.
For web clients we generate JSON schema definitions of the SBP message. This allows web clients to build "native" objects out of SBP JSON. We use the quick QuickType tool to generate libraries for JavaScript, TypeScript, and Elm.
In order to run the make quicktype-* target you need to install the
quicktype tool first. No particular version of this tool is required
at the moment.
To distribute Rust. Use the cargo-release tool:
cargo install cargo-releaseOnce you have logged in to crates.io with cargo:
cargo release <INCREMENTED_TAG> --allow-branch HEAD --executeThen rollback any commits that are created:
git reset --hard v<INCREMENTED_TAG>The build of the libsbp wheel can be done via the libsbp-build container
described above.
You must have the correct token set in your environment to publish to PyPI.
This usually means the git checkout you're building from is not in a "clean" state. The
build scripts will use the git command git describe --tag --always --dirty to generate
a version. Either temporarily force update the tag with git tag -f vM.N.X (do not
push this unintentionally) and/or make sure you're submodule are up-to-date with
git submodule update --init --checkout --recursive.
Tox needs to be run with the Python it was installed with (and apparently must run with Python 2) otherwise you'll get an error similar to:
ERROR: FAIL could not package project - v = InvocationError('/home/ubuntu/dev/libsbp/python/.tox/.tox/bin/python setup.py sdist --formats=zip --dist-dir /home/ubuntu/dev/libsbp/python/.tox/dist', -11)Tox also seems to have issues interacting with conda environments. The easiest way to work around this is to remove conda from your path and make sure tox is installed with a Python2 version of the interpreter.
Tox may fail with the following error:
ERROR: cowardly refusing to delete `envdir` (it does not look like a virtualenv): /home/ubuntu/dev/libsbp/python/.tox/py38-nojitThere's an open tox issue for this: tox-dev/tox#1354 -- the only workaround that resolved this was to downgrade tox:
pip install --upgrade --force-reinstall tox==3.12.1To distribute java, ensure you have the correct credentials and prerequisites
- Gradle 7+
- gradle.properties
- Sonatype deployer account
- Your own GPG key
SonaType open source repo requires a GPG key for signatures. Generate GPG key via:
gpg --gen-key
gpg --export-secret-keys >keys.gpgExport your public key
gpg --export -a > pub.keyGo to https://keyserver.ubuntu.com/#submitKey and upload your PUBLIC key
To locate the value for signing.keyId (needed below) run:
❯ gpg --list-keys --keyid-format short (base)
/home/ubuntu/.gnupg/pubring.kbx
-------------------------------
pub rsa3072/AB7D02BF 2022-05-03 [SC] [expires: 2024-05-02]
573C656383B86BBD618F4ABCFEB6DDB1AB7D02BF
uid [ultimate] Jason Anthony Mobarak <jason@swift-nav.com>
sub rsa3072/BB59B113 2022-05-03 [E] [expires: 2024-05-02]The signing.keyId value to use from above is BB59B113. The /keys folder
to should map to location where your gpg key will be stored. Then, create
gradle.properties in the java directory as follows:
# last 8 digit of gpg key
signing.keyId=xxx
# password for gpg key
signing.password=xxx
# path to exported secret gpg keys
signing.secretKeyRingFile=/keys/keys.gpg
# sonatype logins
ossrhUsername=xxx
ossrhPassword=xxxModify ossrhUsername and ossrhPassword with the sonatype deployer account
(or an individual one with deployer role). See SonaType getting started
guide for more details.
Internal Swift developers should have access to shared credentials via
LastPass.
For more info see: https://docs.gradle.org/current/userguide/signing_plugin.html
Now, invoke docker like this in order to run the dist-java task:
docker run -v $HOME/Documents:/keys -v $PWD:/mnt/workspace -i -t swiftnav/libsbp-build:2025-02-10To publish, you'll run make dist-java (which will run gradle sign and
gradle publish). After publishing, go to Nexus Repository
Manager. Select the deployed version, close the
staging repository and release to finish it off.
Follow the instructions here for how to "close" and then "release" and staging repository on SonaType's repository manager:
This library is developed internally by Swift Navigation. We welcome GitHub issues and pull requests, as well as discussions of potential problems and enhancement suggestions.