From 0babe9c2cde73a529093e5f980565c34cca21e61 Mon Sep 17 00:00:00 2001 From: alexklimaj Date: Wed, 15 Jul 2026 21:07:47 +0000 Subject: [PATCH 1/2] Add u-blox PointPerfect NTRIP client as optional ARK-OS service Integrate pointperfect-client-mavlink (submodule) for GNSS corrections over NTRIP, with systemd units, UI manifest, default config, and a dedicated mavlink-router UDP endpoint on 14554. Opt-in like polaris. --- .gitmodules | 3 ++ README.md | 5 +++- packaging/DEBIAN/postinst | 9 +++--- packaging/DEBIAN/prerm | 2 +- packaging/build_binaries.sh | 4 +++ packaging/config/pointperfect.toml | 28 +++++++++++++++++++ .../service-files/jetson/pointperfect.service | 15 ++++++++++ .../service-files/pi/pointperfect.service | 15 ++++++++++ .../03-ark-service-manager.rules | 1 + services/mavlink-router/main.conf | 5 ++++ .../pointperfect/pointperfect-client-mavlink | 1 + .../pointperfect/pointperfect.manifest.json | 6 ++++ 12 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 packaging/config/pointperfect.toml create mode 100644 packaging/service-files/jetson/pointperfect.service create mode 100644 packaging/service-files/pi/pointperfect.service create mode 160000 services/pointperfect/pointperfect-client-mavlink create mode 100644 services/pointperfect/pointperfect.manifest.json diff --git a/.gitmodules b/.gitmodules index ad893e1..0d763b1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "services/dds-agent/Micro-XRCE-DDS-Agent"] path = services/dds-agent/Micro-XRCE-DDS-Agent url = https://github.com/eProsima/Micro-XRCE-DDS-Agent.git +[submodule "services/pointperfect/pointperfect-client-mavlink"] + path = services/pointperfect/pointperfect-client-mavlink + url = https://github.com/ARK-Electronics/pointperfect-client-mavlink.git diff --git a/README.md b/README.md index d5971e9..4311d2f 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ A web based UI is provided to more easily manage your device. The webpage is hos ![alt text](ark-ui4.png) ## Services -The package installs the services below as system-level [systemd services](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html) running as the unprivileged platform user (`jetson` or `pi`). The always-on services are enabled automatically; the optional services (`dds-agent`, `logloader`, `polaris`, `flight-review`, `rid-transmitter`) are installed but disabled — enable them from the web UI or with `systemctl enable --now `. +The package installs the services below as system-level [systemd services](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html) running as the unprivileged platform user (`jetson` or `pi`). The always-on services are enabled automatically; the optional services (`dds-agent`, `logloader`, `polaris`, `pointperfect`, `flight-review`, `rid-transmitter`) are installed but disabled — enable them from the web UI or with `systemctl enable --now `. ## Jetson and Pi @@ -140,6 +140,9 @@ This service provides an RTSP server via gstreamer. The stream from the first co **polaris.service**
This service receives RTCM corrections from the PointOne GNSS Corrections service and publishes them to the flight controller via MAVLink. +**pointperfect.service**
+This service receives GNSS corrections from the u-blox PointPerfect NTRIP service (RTCM by default, optional SPARTN) and publishes them to the flight controller via MAVLink. + **ark-ui-backend.service**
This service provides an API gateway for the ARK UI. diff --git a/packaging/DEBIAN/postinst b/packaging/DEBIAN/postinst index 02e8a20..bdbe327 100755 --- a/packaging/DEBIAN/postinst +++ b/packaging/DEBIAN/postinst @@ -194,7 +194,8 @@ rm -rf /var/lib/ark-os/.config-backup chgrp -R "$ARK_USER" /etc/ark-os chmod 2775 /etc/ark-os # TODO(security): these configs are world-readable (0664; dir 2775), so any local -# user can read secrets entered via the web UI (polaris_api_key, flight-review SMTP +# user can read secrets entered via the web UI (polaris_api_key, pointperfect +# ntrip credentials, flight-review SMTP # password, rid-transmitter serial, ...). Revisit: restrict secret-bearing files to # 0640 root:$ARK_USER and drop world-read once every reader (service-manager web UI # and each service) is confirmed to run as $ARK_USER or in its group. @@ -208,8 +209,8 @@ ln -sf /etc/ark-os/flight-review.ini /usr/lib/ark-os/flight-review/app/config_us # =========================================================================== # Step 5/6: Enable always-on services (and the group target). Opt-in services -# (dds-agent, logloader, polaris, flight-review, rid-transmitter) are installed -# but deliberately NOT enabled — the operator turns them on via the web UI. +# (dds-agent, logloader, polaris, pointperfect, flight-review, rid-transmitter) +# are installed but deliberately NOT enabled — the operator turns them on via the web UI. # systemctl enable is a static symlink operation that works offline in a chroot. # =========================================================================== ENABLE_SERVICES="mavlink-router rtsp-server go2rtc ark-ui-backend autopilot-manager connection-manager service-manager system-manager" @@ -285,7 +286,7 @@ if [ -d /run/systemd/system ]; then # failed (e.g. mavlink-router with no FC) or operator-stopped service is # left as-is rather than force-started. if [ -n "$2" ]; then - RESTART_SERVICES="$ENABLE_SERVICES dds-agent logloader polaris flight-review rid-transmitter" + RESTART_SERVICES="$ENABLE_SERVICES dds-agent logloader polaris pointperfect flight-review rid-transmitter" systemctl try-restart $RESTART_SERVICES 2>/dev/null || true fi diff --git a/packaging/DEBIAN/prerm b/packaging/DEBIAN/prerm index 2547cc6..2872b97 100755 --- a/packaging/DEBIAN/prerm +++ b/packaging/DEBIAN/prerm @@ -13,7 +13,7 @@ set -e # Platform-specific units (jetson-can, rid-transmitter) are simply not loaded on # the other platform, so stopping them there is a harmless no-op. -ARK_SERVICES="mavlink-router rtsp-server ark-ui-backend autopilot-manager connection-manager service-manager system-manager dds-agent logloader polaris flight-review rid-transmitter jetson-can" +ARK_SERVICES="mavlink-router rtsp-server ark-ui-backend autopilot-manager connection-manager service-manager system-manager dds-agent logloader polaris pointperfect flight-review rid-transmitter jetson-can" case "$1" in remove) diff --git a/packaging/build_binaries.sh b/packaging/build_binaries.sh index e92bde4..c472181 100755 --- a/packaging/build_binaries.sh +++ b/packaging/build_binaries.sh @@ -91,6 +91,10 @@ while read -r lib; do install_bundled_lib "$lib" done <<<"$polaris_extra_libs" +echo "==> pointperfect-client-mavlink (make -> cmake)" +( cd services/pointperfect/pointperfect-client-mavlink && make ) +install -m 0755 services/pointperfect/pointperfect-client-mavlink/build/pointperfect-client-mavlink "$BIN_DIR/" + if [ "$PLATFORM" = "jetson" ]; then echo "==> rid-transmitter (make -> cmake, jetson only)" ( cd services/rid-transmitter/RemoteIDTransmitter && make ) diff --git a/packaging/config/pointperfect.toml b/packaging/config/pointperfect.toml new file mode 100644 index 0000000..2eba631 --- /dev/null +++ b/packaging/config/pointperfect.toml @@ -0,0 +1,28 @@ +# MAVLink connection to the autopilot (mavlink-router UdpEndpoint pointperfect) +connection_url = "udp://:14554" + +# u-blox PointPerfect corrections over NTRIP. +# NTRIP credentials are found in the Thingstream portal under your PointPerfect +# Thing > Credentials. Each Thing allows a single simultaneous NTRIP connection. +ntrip_host = "ppntrip.services.u-blox.com" +ntrip_port = 2102 # 2102 = TLS, 2101 = plain TCP + +# Correction format: "rtcm" (default, natively supported by PX4) or "spartn". +# PX4 currently only injects/parses RTCM; use "spartn" only if your +# receiver/firmware supports SPARTN forwarded over GPS_RTCM_DATA. +correction_format = "rtcm" + +# Mountpoint. Leave empty to derive it from correction_format +# (NEAR-RTCM or NEAR-SPARTN); set it explicitly to override. +ntrip_mountpoint = "" + +ntrip_username = "your_username_goes_here" +ntrip_password = "your_password_goes_here" + +# Use TLS (recommended, matches port 2102). +use_tls = true + +# The caster requires a periodic NMEA-GGA position report from the rover to +# provide localized corrections. The position is taken from the autopilot's +# GPS_RAW_INT messages and sent every 10 seconds. +send_gga = true diff --git a/packaging/service-files/jetson/pointperfect.service b/packaging/service-files/jetson/pointperfect.service new file mode 100644 index 0000000..3b0c380 --- /dev/null +++ b/packaging/service-files/jetson/pointperfect.service @@ -0,0 +1,15 @@ +[Unit] +Description=u-blox PointPerfect GNSS corrections client for MAVLink +Wants=network.target +After=network-online.target mavlink-router.service + +[Service] +Type=simple +User=jetson +Group=jetson +ExecStart=/usr/lib/ark-os/bin/pointperfect-client-mavlink --config /etc/ark-os/pointperfect.toml +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target ark-os.target diff --git a/packaging/service-files/pi/pointperfect.service b/packaging/service-files/pi/pointperfect.service new file mode 100644 index 0000000..141d38b --- /dev/null +++ b/packaging/service-files/pi/pointperfect.service @@ -0,0 +1,15 @@ +[Unit] +Description=u-blox PointPerfect GNSS corrections client for MAVLink +Wants=network.target +After=network-online.target mavlink-router.service + +[Service] +Type=simple +User=pi +Group=pi +ExecStart=/usr/lib/ark-os/bin/pointperfect-client-mavlink --config /etc/ark-os/pointperfect.toml +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target ark-os.target diff --git a/packaging/system-config/03-ark-service-manager.rules b/packaging/system-config/03-ark-service-manager.rules index 00a75ab..39561e0 100644 --- a/packaging/system-config/03-ark-service-manager.rules +++ b/packaging/system-config/03-ark-service-manager.rules @@ -14,6 +14,7 @@ polkit.addRule(function(action, subject) { "logloader.service", "rtsp-server.service", "polaris.service", + "pointperfect.service", "rid-transmitter.service", // JETSON-ONLY "jetson-can.service", // JETSON-ONLY "flight-review.service", diff --git a/services/mavlink-router/main.conf b/services/mavlink-router/main.conf index ae8cab5..4d8c48f 100644 --- a/services/mavlink-router/main.conf +++ b/services/mavlink-router/main.conf @@ -24,6 +24,11 @@ Mode = Normal Address = 127.0.0.1 Port = 14552 +[UdpEndpoint pointperfect] +Mode = Normal +Address = 127.0.0.1 +Port = 14554 + [UdpEndpoint rid_transmitter] Mode = Normal Address = 127.0.0.1 diff --git a/services/pointperfect/pointperfect-client-mavlink b/services/pointperfect/pointperfect-client-mavlink new file mode 160000 index 0000000..5f02c81 --- /dev/null +++ b/services/pointperfect/pointperfect-client-mavlink @@ -0,0 +1 @@ +Subproject commit 5f02c819830d29b2f1857901d6a0717447dfeb34 diff --git a/services/pointperfect/pointperfect.manifest.json b/services/pointperfect/pointperfect.manifest.json new file mode 100644 index 0000000..ef0c25b --- /dev/null +++ b/services/pointperfect/pointperfect.manifest.json @@ -0,0 +1,6 @@ +{ + "displayName": "PointPerfect Mavlink", + "description": "Client for the u-blox PointPerfect GNSS corrections service (NTRIP) to provide corrections over Mavlink.", + "configFile": "pointperfect.toml", + "visible": true +} From bf3ed4106603b143b363c015d6b339d299139b84 Mon Sep 17 00:00:00 2001 From: alexklimaj Date: Wed, 15 Jul 2026 21:34:16 +0000 Subject: [PATCH 2/2] UI: dropdown for PointPerfect correction_format (rtcm/spartn) TomlEditor only supported *_options dropdowns inside nested tables; extend that to top-level keys and ship correction_format_options in the PointPerfect default config so the Services editor shows a select. --- frontend/src/pages/components/TomlEditor.vue | 49 +++++++++++++++----- packaging/config/pointperfect.toml | 2 + 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/frontend/src/pages/components/TomlEditor.vue b/frontend/src/pages/components/TomlEditor.vue index 8444a0d..33c9ecf 100644 --- a/frontend/src/pages/components/TomlEditor.vue +++ b/frontend/src/pages/components/TomlEditor.vue @@ -5,13 +5,29 @@

{{ serviceName }}

- -
+ +
+ + + -
+
{ - if (!this.isObject(value)) { - primitives[key] = value; + // Top-level editable fields (primitives + simple arrays that aren't *_options) + topLevelFieldKeys() { + return Object.keys(this.config).filter((key) => { + if (this.isOptionsField(key)) { + return false; } + const value = this.config[key]; + return !this.isObject(value); }); - return primitives; }, // Get all sub-tables (nested objects) subTables() { @@ -197,6 +213,15 @@ export default { return this.config[tableKey][optionsKey] || []; }, + // Top-level dropdown support (same *_options convention as nested tables) + hasTopLevelOptions(fieldKey) { + return Array.isArray(this.config[`${fieldKey}_options`]); + }, + + getTopLevelOptions(fieldKey) { + return this.config[`${fieldKey}_options`] || []; + }, + // Pick the input from the type in the loaded config rather than the live // value. Vue's .number modifier passes non-numeric input straight through, // so an emptied number field holds "" — keying off the live type would swap diff --git a/packaging/config/pointperfect.toml b/packaging/config/pointperfect.toml index 2eba631..d4aeb8d 100644 --- a/packaging/config/pointperfect.toml +++ b/packaging/config/pointperfect.toml @@ -10,7 +10,9 @@ ntrip_port = 2102 # 2102 = TLS, 2101 = plain TCP # Correction format: "rtcm" (default, natively supported by PX4) or "spartn". # PX4 currently only injects/parses RTCM; use "spartn" only if your # receiver/firmware supports SPARTN forwarded over GPS_RTCM_DATA. +# correction_format_options drives the ARK UI dropdown (ignored by the client). correction_format = "rtcm" +correction_format_options = ["rtcm", "spartn"] # Mountpoint. Leave empty to derive it from correction_format # (NEAR-RTCM or NEAR-SPARTN); set it explicitly to override.