Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <service>`.
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 <service>`.

## Jetson and Pi

Expand All @@ -140,6 +140,9 @@ This service provides an RTSP server via gstreamer. The stream from the first co
**polaris.service** <br>
This service receives RTCM corrections from the PointOne GNSS Corrections service and publishes them to the flight controller via MAVLink.

**pointperfect.service** <br>
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** <br>
This service provides an API gateway for the ARK UI.

Expand Down
49 changes: 37 additions & 12 deletions frontend/src/pages/components/TomlEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,37 @@
<h1>{{ serviceName }}</h1>

<div class="toml-editor">
<!-- Top-level primitive values first -->
<div v-for="(value, key) in topLevelPrimitives" :key="key" class="form-group">
<!-- Top-level primitive values first (skip *_options arrays used for dropdowns) -->
<div v-for="key in topLevelFieldKeys" :key="key" class="form-group">
<label :for="key">{{ formatKey(key) }}</label>

<!-- Dropdown when a sibling <field>_options array is present -->
<select
v-if="hasTopLevelOptions(key)"
:id="key"
v-model="config[key]"
class="select-input"
>
<option
v-for="option in getTopLevelOptions(key)"
:key="option"
:value="option"
>
{{ option }}
</option>
</select>

<!-- String input -->
<input
v-if="fieldType(key, value) === 'string'"
v-else-if="fieldType(key, config[key]) === 'string'"
type="text"
:id="key"
v-model="config[key]"
class="text-input"
/>

<!-- Boolean input -->
<div v-else-if="fieldType(key, value) === 'boolean'" class="checkbox-group">
<div v-else-if="fieldType(key, config[key]) === 'boolean'" class="checkbox-group">
<input
type="checkbox"
:id="key"
Expand All @@ -30,7 +46,7 @@

<!-- Number input -->
<input
v-else-if="fieldType(key, value) === 'number'"
v-else-if="fieldType(key, config[key]) === 'number'"
type="number"
:id="key"
v-model.number="config[key]"
Expand Down Expand Up @@ -138,15 +154,15 @@ export default {
};
},
computed: {
// Separate top-level primitives from sub-tables
topLevelPrimitives() {
const primitives = {};
Object.entries(this.config).forEach(([key, value]) => {
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() {
Expand Down Expand Up @@ -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
Expand Down
9 changes: 5 additions & 4 deletions packaging/DEBIAN/postinst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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"
Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion packaging/DEBIAN/prerm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions packaging/build_binaries.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand Down
30 changes: 30 additions & 0 deletions packaging/config/pointperfect.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 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_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.
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
15 changes: 15 additions & 0 deletions packaging/service-files/jetson/pointperfect.service
Original file line number Diff line number Diff line change
@@ -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
15 changes: 15 additions & 0 deletions packaging/service-files/pi/pointperfect.service
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions packaging/system-config/03-ark-service-manager.rules
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 5 additions & 0 deletions services/mavlink-router/main.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions services/pointperfect/pointperfect-client-mavlink
6 changes: 6 additions & 0 deletions services/pointperfect/pointperfect.manifest.json
Original file line number Diff line number Diff line change
@@ -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
}
Loading