# Welcome

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th></tr></thead><tbody><tr><td>Learn how to setup Cyclops on your drone</td><td><a href="/pages/nyI3VtrHW1F4OaNPlh1Z">/pages/nyI3VtrHW1F4OaNPlh1Z</a></td><td><a href="/files/ipq3wRzxEfYM0JMyFDgk">/files/ipq3wRzxEfYM0JMyFDgk</a></td></tr><tr><td>Learn how to operate Vozilla GCS</td><td><a href="/pages/e3KtjldVismwQYNzaZMx">/pages/e3KtjldVismwQYNzaZMx</a></td><td><a href="/files/foo8y5AoRVbedXkiZYuE">/files/foo8y5AoRVbedXkiZYuE</a></td></tr><tr><td>Learn how to integrate Micro VPS</td><td><a href="/pages/2vkyzxHABxncKcbgtEIM">/pages/2vkyzxHABxncKcbgtEIM</a></td><td><a href="/files/5oRCZixZzeb2RgCRemC6">/files/5oRCZixZzeb2RgCRemC6</a></td></tr></tbody></table>


# Getting Started

Theseus [Vozilla](/gcs-software/vozilla) is an application that lives on the Ground Control Station to facilitate Cyclops installation, evaluation and operation.

<figure><img src="/files/GvXDCjawj2Z6oohCoQYG" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Looking for the guide on installing Theseus on your drone? Head over to [CYCLOPS](/cyclops/getting-started)
{% endhint %}


# Vozilla

How to navigate Vozilla 2.0.0+

The Vozilla Ground Control Station (GCS) App allows you to configure and assess performance of your Theseus system **(MicroVPS/Cyclops).** It is used to generate and upload map data to your device, review Theseus flight logs, and configure your Theseus system for GPS denied flight.

<figure><img src="/files/U4oD6txxbd6rGyX6AImr" alt=""><figcaption><p>Vozilla 2.0+ Homepage</p></figcaption></figure>


# Installation

## Downloading Vozilla

Vozilla is Theseus' app to configure your devices, generate maps, and manage Cyclops. Follow this link to create an account and download Vozilla.

<p align="center"><a href="https://dashboard.theseus.us/downloads" class="button primary">Download Vozilla</a></p>

{% hint style="warning" %}
The Vozilla GCS app is currently only available on **Windows and Linux**. Please contact us if you require support for another OS.
{% endhint %}

## Updates

Updates to the Vozilla app are shipped frequently as the app is still in rapid development. For the best experience using the app we always recommend using the most up to date version.

The app checks for updates when you launch it, so you will be prompted to update if any new versions are released.

<figure><img src="/files/9Yksr2SLa4Db0E5a1Ik4" alt=""><figcaption></figcaption></figure>


# Quick Start

Vozilla GCS app allows you to manage your Micro VPS device. This page will go over the core features of the app. It will also go over connecting your computer to the Micro VPS

{% hint style="info" %}
Make sure you installed the Vozilla GCS app on your Windows/Linux laptop following [Broken mention](broken://pages/qKJI6wM207wbITqste5I) before proceeding.
{% endhint %}

## Log In

You need to log into vozilla to be able to use the ground control station. If you already have a previous account with Theseus, use this to sign in. If not, please [Create an Account](/gcs-software/vozilla/create-an-account).

<figure><img src="/files/r7AEqGpdebqycRLwYtw9" alt=""><figcaption></figcaption></figure>

## Homepage

Once logged in, you will be taken to the homepage. This is where you will find everything you need to fly using Theseus.&#x20;

<figure><img src="/files/GvXDCjawj2Z6oohCoQYG" alt=""><figcaption></figcaption></figure>

## Connecting to Micro VPS

Vozilla GCS app supports connectivity over Ethernet and MAVLink to the Micro VPS. You will need to **edit your computer Ethernet network interface** configuration in order to establish a connection to the Micro VPS.

By default we have the edge device configured to a static ip address of `192.168.218.100`. If you change this default address, your network interface settings will need to match the subnet of the different ip address.

If using the default ip, follow the instructions below to configure your computer to communicate with the Micro VPS over Ethernet.

{% tabs %}
{% tab title="Windows" %}
Here are the IP settings for the network interface:

* IP address: 192.168.218.10
* Subnet mask: 255.255.255.0
* Default gateway: 192.168.218.1
* DNS: 8.8.8.8 or 1.1.1.1

On your Windows computer:

* Open Windows Settings (Windows key + I)
* Click on "Network & Internet"
* Click on "Ethernet"
* Click on your network adapter
* Under "IP assignment", click "Edit"
* Select "Manual" and turn on IPv4
* Enter the IP settings shown above
* Click "Save"
  {% endtab %}

{% tab title="Linux" %}
Here are the IP settings for the network interface:

* IP address: 192.168.218.10
* Subnet mask: 255.255.255.0
* Default gateway: 192.168.218.1

Create a new Ethernet network interface with these IPv4 settings in your settings app.
{% endtab %}
{% endtabs %}

When connected. Vozilla will display status information on the product service

```
Put Gif of connection here
```

### Using another ip address for your edge device

If your system requires a specific ip address for the compute that differs from the default address of `192.168.218.100`, you can still connect to it with Vozilla.

To edit the address, enter the settings and navigate to the device settings. Input your custom ip under device URL

<figure><img src="/files/sRW1u3opu4rhwPuO3d5W" alt=""><figcaption></figcaption></figure>

## Overview

Below is a breakdown of all the main features on the homepage

<div data-full-width="true"><figure><img src="/files/MRUjjQbueT7cncDU3inq" alt=""><figcaption></figcaption></figure></div>

{% stepper %}
{% step %}

### Vozilla Directory and Version

The on the left side of the title bar can be used to determine the version of your Vozilla application. The text also updates as you navigate throughout the app. Displaying "Home" for the homepage, "Maps" when you are setting and generating maps, "Vehicles" when setting and generating vehicle calibrations and so on.
{% endstep %}

{% step %}

### Map Controls

The top left corner of the map contains the map controls. These include, zoom controls, recentering and a lat, lon value for your cursor position on the map.
{% endstep %}

{% step %}

### Search Bars

Search bars will always be located centered along the top of your screen. The global search can be found in the title bar. This can be used to navigate to different components of the app, navigate to certain settings and search through configurations.
{% endstep %}

{% step %}

### Connection Info

The top right corner consists of 3 essential connection info. The right most component (1) is used to connect to your flight controller and set up a mavproxy output to any other receivers. To the left of it (2) is the device status information. This will indicate device status and will be green when everything is running smoothly. The left most component is the recording toggle (3), this can be used to quickly toggle on/off flight recordings.

<figure><img src="/files/sxqhx2QXVibM5og7c4DS" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

### [Navigation Panel](/gcs-software/vozilla/navigation-panel)

On the right of the screen is a dockable panel. This panel is the main place where all navigation will happen. This panel lets you navigate to the maps, vehicles, cameras and flights subpages that allow you to configure and view their respective content.

<figure><img src="/files/IlLifCesRPngfUTidvja" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

### Settings

The settings Button located in the title bar next to the window controls will take you to the settings page. This page is where a majority of device, user, and account settings live.&#x20;
{% endstep %}

{% step %}

### Diagnostics Panel

The panel docked to the bottom of the screen contains all diagnostics information related to the device. This includes any MAV messages, heartbeats, parameters and a MAV console when connected to a MAV input source. In addition to this, there are system diagnostics for the Theseus system when connected.

<figure><img src="/files/WrRS8cNmeXUvMpjURRYo" alt=""><figcaption></figcaption></figure>
{% endstep %}
{% endstepper %}


# Navigation Panel

On the right of the screen is a dockable panel. This panel is the main place where all navigation will happen. This panel lets you navigate to the maps, vehicles, cameras and flights subpages that allow you to configure and view their respective content.

<figure><img src="/files/erEpIBuJb7TBAqekPfCH" alt=""><figcaption></figcaption></figure>

{% stepper %}
{% step %}

### [Maps](/gcs-software/vozilla/maps) Sub page

When in the root panel and a device is connected, you will be able to see the currently active map selected for your device. Clicking "Manage Maps" will take you to the [Maps](/gcs-software/vozilla/maps)
{% endstep %}

{% step %}

### [Vehicles](/gcs-software/vozilla/vehicles) Sub page

When in the root panel and a device is connected, you will be able to see the currently active vehicle configured for your device. Clicking "Manage Vehicles" will take you to the [Vehicles](/gcs-software/vozilla/vehicles) Sub page
{% endstep %}

{% step %}

### [Cameras](/gcs-software/vozilla/cameras) Sub page

When in the root panel and a device is connected, you will be able to see the currently active vehicle configured for your device. Clicking "Manage Cameras" will take you to the [Cameras](/gcs-software/vozilla/cameras) Sub page
{% endstep %}

{% step %}

### [Flights](/gcs-software/vozilla/flights) Sub page

When in the root panel and a device is connected, you will be able to see the currently active vehicle configured for your device. Clicking "View all flights" will take you to the [Flights](/gcs-software/vozilla/flights)Sub page
{% endstep %}
{% endstepper %}


# Maps

The Maps page allows you to generate and upload maps prior to your operations with the system. This is also where you can set your home/takeoff position and switch between generated maps.

<figure><img src="/files/wRIYvhwoYAJwibR7a3eL" alt=""><figcaption><p>Maps Page</p></figcaption></figure>

## Why do I need this?

Theseus products require **pre-processed satellite imagery** to perform map matching. Micro VPS can store up to 50,000 sq. km of map area for any mission (e.g. 20 km x 2500 km area). The Maps tab allows you to generate and download these maps before your mission.

To learn more about Micro VPS, Cyclops and our map matching technology visit the [Technology page on Theseus' website](https://www.theseus.us/technology).&#x20;

## Maps Information

Map file sizes are \~5-6MB per km<sup>2</sup>. **A 20,000 km**<sup>**2**</sup>**&#x20;would be around 100GB**. The amount of time it takes to download a map depends on your internet speed. Large maps take a long time to generate and download, so please be sure to do this well in advance of your flights.

Also note that **transferring the map to the edge device requires a high bandwidth connection**. Uploading the map over a radio link will be extremely slow and error prone.

Maps are generated with a buffer area around them, so the exact size of the map and number of tiles generated will always be more than the selected area. The percentage of the total map that is the buffer area depends on the size and shape of the map.


# Creating a new map

## Map Generation

To generate maps follow these steps on the Maps tab:

{% hint style="info" %}
You can expect the map generation to take \~1 hour per 1000 km<sup>2</sup>. The completed maps take up \~1GB per 200 km<sup>2</sup> so downloading a large map may take a long time over a slow internet connection.
{% endhint %}

{% stepper %}
{% step %}

### Generating a new map

Click the **NEW MAP** button at the bottom of your Map Panel, then navigate to your desired flight area. You can do this by entering either the name of your location or the WGS84 coordinates in the search bar. Hit *enter* and the map will pan to your location.

<figure><img src="/files/PC9TkLCaePuLPyYBc677" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

## Create your map boundary

Draw the polygonal map boundary around your flight area. Ensure the boundary covers the entire flight area for your mission, as your system cannot localize outside of the map. Add a distinct name for your map in the field provided.

<figure><img src="/files/H1j87XI2INfKPmeA8gK6" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
**Your selected area size must be between 50 and 50,000 km**<sup>**2**</sup>**.** Newly generated maps cover the entire selected area. If using maps from before Nov 19th, 2025, make sure that the boundaries of your autonomous mission include at least 2 km of padding to the edges of the selected map area.
{% endhint %}
{% endstep %}

{% step %}

### Generating your map

Once you've selected your map area and added a name, click the **GENERATE** button on the left of the screen. The map will begin generating in the background. This process may take a few hours, so it is best to generate maps well before flying.

## What happens under the hood?

Theseus queries its satellite map provider for imagery over the area you selected. Theseus' map matching technology is invariant to seasonal and landscape changes in the imagery. We employ commercial-grade satellite imagery with medium resolution.

## Large & Non-rectangular maps

Theseus now supports generating maps larger than 2000 km<sup>2</sup> as well as maps that fit any desired shape. You can generate a precise map to fit a mission, or select a large area if you plan on mostly flying many routes in the same area.
{% endstep %}
{% endstepper %}


# Selecting a map

## Map Flow

Maps are large files and are generated and stored on the cloud. If you want to put a map on your edge device, you first need to download it from the cloud to your local machine. This can take a long time even with a good internet connection depending on the size of the map.

After the map is downloaded, you can upload it to the edge device. It will be stored there until you delete it. You can select any of the maps on the edge device as the active map simply by selecting it in the maps tab of the app.

## Map Library

All maps linked to your account can be viewed from the Maps panel on the Vozilla GCS App. Any previously generated map can be downloaded to your device. You will also be able to see and map generations that have failed, as well as archive maps that you are not using.

To download a map onto your device. First navigate to the maps panel. This can be done from anywhere using the spotlight search

<figure><img src="/files/X3KvpQJ8uoa5QFmkO28e" alt=""><figcaption></figcaption></figure>

Then, Download the map using the download button

<figure><img src="/files/OYGTpBbyspD02JAqImtN" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Maps are downloaded as a single **.tar.gz** file to be uploaded to your device.
{% endhint %}

## Uploading maps to the edge device

Once a map has been downloaded to your local machine, you can upload it to the edge device.

<figure><img src="/files/9SKtC3xBK809CDr95OSI" alt=""><figcaption></figcaption></figure>

After being uploaded, the new map is automatically selected as the active map and the home position is set.


# Setting a home position

Each map has a corresponding home position. This position represents the takeoff location of your UAV, and is the location where the vps will initialize. It is represented by a circle within the map boundary on the map screen.

To set your home position, either drag the circle to a desired location within the map boundary or enter coordinates in the *Home Position* field. Then click the **SAVE** button to set this as the new home position.

Saving the home position automatically restarts the vps service to load in the new starting position.

<figure><img src="/files/ag5dxhgMlWJLeBB4jNbY" alt=""><figcaption></figcaption></figure>


# Feature map

Overview of how to mission plan around features

Map matcher performance relies upon having unique terrain to localize to. To help plan missions we created a "quality map" for users to plan missions more effectively and gain a visual understanding of how the product works.

### Generate a quality map

Once your map is generated, you will see an option to generate a quality map in Vozilla. Click this button to begin generation of the quality map. This will go much faster than the map generation, but processing times do scale with map size, so a large (>5,000 square km) map will still take a considerable amount to time to generate.

<figure><img src="/files/AfHpcnH21Cvyk3ri0Y01" alt=""><figcaption></figcaption></figure>


# Manually managing maps

How the edge computer's networking is configured can make it much more difficult to plug in and connect to edge computer with Vozilla. In cases where it is more convenient to directly edit files on the edge computer's storage device, this guide can help.

### Downloading a map with Vozilla

<figure><img src="/files/p9l1CMr1ncE1G4OHbkMD" alt=""><figcaption></figcaption></figure>

Once you have the desired map downloaded on Vozilla, you can click "More map options" --> "Export map package" to save the map as a `.tar.gz` .

The last step before uploading to the edge device is extracting the archive. If you are using linux, you can simply run `tar -xvzf filename.tar.gz` to extract the map archive. You should be left with a map folder named `filename` . If you are using windows, it is easiest to download a tool like 7-zip to extract the archive.

### Uploading to the edge device

Take the storage device out of the edge device (most likely an SD card, but you can also do this with an NVME) and plug it into your computer with the map.

When you attach the drive to your computer you will see a `rootfs` or `writeable` and a `bootfs` or `system-boot` partition. `rootfs` will allow you to edit the filesystem of the edge device directly. Mount `rootfs` and search for `/usr/etc/vns-sdk/config.local.yaml` within it. The contents of that file will tell you where you should place the map.

{% code title="/usr/etc/vns-sdk/config.local.yaml" %}

```yml
paths:
  recordings_dir: /home/pi/recordings

mavproxy:
  devices:
    - device: /dev/ttyAMA0
      baudrate: 921600
```

{% endcode %}

In the case of this device, the map belongs in `/home/pi/recordings/maps` . If there is not already a `maps` folder there on the drive, create one. Then copy the map to that location. Once the extracted map is in the `maps` folder, the last step is setting it as the active map and updating the home position.

### Setting the active map & updating home position

The active map is set using a symlink link to the map folder. I can view the current active map by looking at this symlink. Since my drive is called `writable` the symlink would be at `writable/opt/vns-sdk/maps` .

{% code title="" overflow="wrap" %}

```bash
$ ls -lah writable/opt/vns-sdk/maps
lrwxrwxrwx 1 wolf wolf 41 Jun 22 15:40 writable/opt/vns-sdk/maps -> /home/pi/recordings/maps/144c45_map_12345
```

{% endcode %}

Set the active map with the following command. Ensure that you have the correct path to the map, relative to the edge device's root.

{% code title="" overflow="wrap" %}

```bash
sudo ln -sfn /home/pi/recordings/maps/[name of your map folder] maps
```

{% endcode %}

To update the home position, edit `/usr/etc/vns-sdk/initial-fix.conf` directly. You will need to know the latitude, longitude, and altitude of your launch position.


# Vehicles

The Vehicles page allows you to generate and upload maps prior to your operations with the system. This is also where you can set your home/takeoff position and switch between generated maps.

<figure><img src="/files/T8fZ6Ybpya2mG63EWzp7" alt=""><figcaption></figcaption></figure>

## Why do I need this?

The Micro VPS needs to know the orientation of the flight controller relative to itself in order to accurately give a position estimate. The GCS App has a tool to define this transformation and save it to a file.

Check out our  [Mechanical Installation](/micro-vps/vehicle-integration/mechanical-installation) page for information on how to properly mount you Micro VPS.


# Creating a new vehicle

## Vehicle Configuration

To create vehicle configurations follow the steps on our [Mechanical Installation](/cyclops/vehicle-integration/mechanical-installation) page for information on how to properly mount your camera system. Then come back here to input the vehicle configuration

{% hint style="info" %}
The config will automatically be uploaded and set as the active config on your device if a Theseus system is connected to your computer. Otherwise, the config will be saved locally.
{% endhint %}

{% stepper %}
{% step %}

### Creating a custom vehicle configuration

Click the **NEW CONFIG** button at the bottom of your Vehicle Panel to start creating a custom configuration.&#x20;

<figure><img src="/files/jLYWnHdjvT0OQXDw9dCc" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

### Input custom translation and rotation

Once a you have measured your transformation, follow the steps in [Mechanical Installation](/cyclops/vehicle-integration/mechanical-installation), you can input the translation and rotation values in the top left card.

<figure><img src="/files/WOGPqn0LBi2cgYm8HUJ5" alt=""><figcaption></figcaption></figure>
{% endstep %}
{% endstepper %}


# Cameras


# Creating a new camera calibration

## Camera Calibration

Camera calibrations are required when using cyclops on your system. To start calibrating the camera you plan to use with cyclops on your machine. Navigate to the camera panel.&#x20;

<figure><img src="/files/tuf2fq6uka51ezYFsQMF" alt=""><figcaption></figcaption></figure>

Then click the **NEW CALIBRATION** button at the bottom of the panel. This will open up the camera calibration utility

<figure><img src="/files/mBFpRAYFPXkrvVnBdyaX" alt=""><figcaption></figcaption></figure>

From here, refer to the instructions outlined in [Calibrating your camera](/gcs-software/vozilla/cameras/calibrating-your-camera)

{% hint style="info" %}
The config will automatically be uploaded and set as the active config on your device if a Theseus system is connected to your computer. Otherwise, the config will be saved locally.
{% endhint %}


# Calibrating your camera

<figure><img src="/files/FvQQ7k96WrZgXY6Z3SBS" alt=""><figcaption></figcaption></figure>

The Camera Calibration tool in Vozilla lets you generate and save a calibration file (camera intrinsic) for your camera. A good calibration improves Cyclops tracking accuracy and reduces distortion, especially at the edges of the image.

#### What you need

* A 7×8 asymmetric circle grid calibration target, found below.

{% file src="/files/9jIZU1cdlLUHxN8zqzdi" %}

* For thermal cameras, we’ve found it works best to 3D print the grid and place it on a uniform warm surface so the pattern is clearly visible. A step file for this grid can be found below.

{% file src="/files/YUxFEpZH5geflfCvGavu" %}

{% hint style="info" %}
Placing the 3D print against a monitor works well for thermal cameras, providing a consistent warm background.

<p align="center"><img src="/files/SHX6TwSShF7h2AbpyBpE" alt="" data-size="original"></p>
{% endhint %}

#### Capture calibration images

1. Connect your camera and open the Camera Calibration page.
2. Use Capture Frame to record images of the detected grid.
3. Move the target through a variety of:
   * angles (tilt and rotate)
   * positions (across the full frame, including corners)
   * distances (close, medium, far)

For best results, capture 20+ frames where the pattern is cleanly detected.

<figure><img src="/files/J6P4UgK4k99w5TvPUxbY" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
If your target is white dots on a black background, enable Inverted Pattern so detection works correctly.
{% endhint %}

#### Compute and save

After capturing enough frames, Vozilla will prompt you to compute the calibration. Save it using the default name or a descriptive name (helpful if you switch cameras or lenses later). The saved calibration can be reused in the future.

#### Verify quality

After saving, use the Distortion toggle (bottom-right of the camera view) to preview the effect of the calibration. A good calibration should reduce warping and keep lines and features looking consistent across the frame.

<figure><img src="/files/BcgayQfcqDZpNK9Flpky" alt=""><figcaption></figcaption></figure>

#### Advanced options

{% hint style="danger" %}
We don’t recommend manually editing calibration values or entering custom intrinsics unless you’re an advanced user. If needed, you can also create a calibration by selecting a starting point from the Camera Presets dropdown.
{% endhint %}


# Manual camera calibration

In certain cases you may need to manually configure the calibration parameters for your camera.

<figure><img src="/files/oBrEXSxMZJve07QEYTGq" alt=""><figcaption></figcaption></figure>

To do so:

* Navigate to the Camera section from the right panel on Vozilla
* Select "New Calibration" at the bottom of the panel
* Click the "Advanced" drop down below the image stream
* Click on "Write custom calibration"
* Manually enter your camera calibration parameters in the panel
* When you're done, click the "Save" button

Here is the default configuration for a Flir Boson+ 640 with a 95° HFOV / 4.9mm lens:

```yaml
resolution:
  width: 640
  height: 512

fps: 60

intrinsics:
  fx: 411.37
  fy: 411.22
  cx: 317.80
  cy: 257.89
  k1: 0.133336
  k2: 0.0
  k3: 0.0
  k4: 0.52395
```


# Flights

The **Flights** sub page stores logs from all of your flights using a theseus system. Here, you can analyze the performance of the system, as well as compare the VPS position to GPS, if applicable. You can also use playback tools to review your flights.&#x20;

<figure><img src="/files/vAkNXrmOZDhDnv1owFld" alt=""><figcaption><p>Flights subpage</p></figcaption></figure>

## Syncing Flights

Vozilla automatically synchronizes all logs from your device. This enables you to review logs offline and share logs with our team for further analysis.

We read the `MAV_LANDED_STATE`  values from the autopilot throughout the flight to determine the state of the aircraft. Vozilla looks for transitions from `ON_GROUND` to `IN_AIR` states to determine whether logs synced off the device correspond to a real flight or a recording while on the bench.

{% hint style="info" %}
After a flight, plug your device into your laptop running Vozilla to auto sync flight logs.
{% endhint %}

## Evaluating Performance

Theseus software running on the edge automatically pulls GPS from GPS 1 on ArduPilot when it is available. These GPS positions get logged alongside our estimated position.

Once your flights are synchronized on Vozilla, you can review performance by clicking on flights listed under the Flights panel. Several tracks will appear on the map:

* Gray track shows GPS positions
* Dots show map matching points, color indicates scales with accuracy of the estimated position (red for lower accuracy, green for higher accuracy)

The accuracy figure shown under the Metrics tab is the median accuracy between GPS and map matching throughout the flight.


# Reviewing a flight

Select a flight to display that flight on the map. Both GPS and position estimate tracks are displayed if your UAV has both.&#x20;

### Metrics Tab

The **Metrics** tab shows the median accuracy of the VPS relative to the GPS and the coordinates of both positions.&#x20;

<figure><img src="/files/dnNmqQP9DiFHcsjbnHI2" alt=""><figcaption><p>Metrics tab</p></figcaption></figure>

The **Metrics** tab provides tools to generate an MCAP file for flight visualizations.

<figure><img src="/files/55Rsn07IcPgtqGvMW1ZX" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
We recommend [Foxglove Studio](https://foxglove.dev/download) as your MCAP viewer.
{% endhint %}

### Notes Tab

The **Notes** tab allows you to add observations and comments to your flight logs for later reference.

<figure><img src="/files/bh9C1DpseWrVrrnxAx1s" alt=""><figcaption><p>Notes tab</p></figcaption></figure>


# Settings

The settings page contains all relevant device, account and mav settings that may need to be configured off vozilla.&#x20;

<figure><img src="/files/HZ4DsIq73mcX1F0J3ptf" alt=""><figcaption></figcaption></figure>

{% stepper %}
{% step %}

### Setting Search

This search bar can be used to search for various setttings and information cards that are available on the settings page.
{% endstep %}

{% step %}

### Setting Groups

All the setting groups can be found on the left side of the settings page.
{% endstep %}

{% step %}

### Setting Content

Each setting groups displays various content for the configurable settings and information available. This is where you will find all the tools needed to configure your vozilla application, manage [Organizations](/gcs-software/vozilla/settings/organizations), configure device settings, and configure your mav settings.
{% endstep %}
{% endstepper %}


# Organizations

{% hint style="info" %}
This feature is currently only available in dev versions of Vozilla
{% endhint %}

Theseus organizations allow you to share maps you generate with people on your team, and allow your team to use a single map quota across many accounts. Maps shared with an org subtract from the [owner](#organization-structure)'s map quota rather than from the account that created the map.

### Creating an org

Creating an org can be done either through Vozilla or through [dashboard.theseus.us](https://dashboard.theseus.us). The process in both is the same. Find the organizations tab in the Vozilla settings and create a new org.

<figure><img src="/files/TXwfjyNjeklO9Ib6FI6G" alt=""><figcaption></figcaption></figure>

You can accept invites from others from the same page.

<figure><img src="/files/DjlhRg8QiEazw63lLWOm" alt=""><figcaption></figcaption></figure>

### Organization Structure

Roles include:

* **Owner:** person who created the org
* **Member:** anyone that has been invited to the org by the owner and has accepted the invitation

Owners have full control over who is a part of an org and can remove members at from an org. When a user is removed from an org, the maps they generated as a part of the org are reverted to personal maps.

### Viewing Shared maps

<figure><img src="/files/gyDesoTIimHEYUfUVpqr" alt=""><figcaption></figcaption></figure>


# Recording Control

This page outlines the available control over flight recordings on vozilla

There are 2 types of control over the recordings that can be done through vozilla.

## Start/Stop Recordings

To start or stop a recording, toggle the control on the homepage of vozilla as seen below.

<figure><img src="/files/kQ0uuNXYWHiGjm7ysDpq" alt=""><figcaption></figcaption></figure>

This will either stop a current recording or if one is not already running, will restart the recording service. This will create a new ecal measurement recording within the log folder for the current boot. See image below.

<figure><img src="/files/1MyLj2PliA7Z4cVgDPOq" alt=""><figcaption></figcaption></figure>

Note that this does not control the auto start on boot behavior. To control that refer to the section on the [Enable/Disable Recordings](https://app.gitbook.com/o/3XcGCVYDmpR51XZKRzt0/s/hUdZltvGJCfBMBzRuwSC/~/edit/~/changes/139/gcs-software/vozilla-2.0/settings/recording-control#enable-disable-recordings).

## Enable/Disable Recordings

You can also control the behavior of the recordings on device boot. By default this is set to enable, which means that it will begin recording all values at the start of every boot.&#x20;

If this is not wanted behavior. Navigate to the device setting and disable the auto start on boot toggle seen in the image below. Toggling this off will disable the recordings from automatically starting on every boot until the control is enabled

<figure><img src="/files/CqQKlrrG5Tz5zD96Lr9v" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
Note that if you turn this off. You must manually start the recording before a flight using the button on the HOMEPAGE. See the section on "[Start/Stop Recordings](https://app.gitbook.com/o/3XcGCVYDmpR51XZKRzt0/s/hUdZltvGJCfBMBzRuwSC/~/edit/~/changes/139/gcs-software/vozilla-2.0/settings/recording-control#start-stop-recordings)" above
{% endhint %}

## Clearing log data

To clear out all of the logs on device, navigate to `Settings -> Diagnostics -> Delete all logs`

<figure><img src="/files/BNwfHxX7d4vxVrNbHghd" alt=""><figcaption></figcaption></figure>


# Create an Account

Open the Vozilla GCS app and select the "Create account" text to create your account and access the app.

<figure><img src="/files/7x6P2E9snYPwfOGiJg8e" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
Clicking this will take you to the [Theseus Dashboard](https://dashboard.theseus.us/signup), create an account here in order to sign in on vozilla.
{% endhint %}


# Lockdown Mode

Activating lockdown mode increases the security of your system by enabling encryption of all SDK logs and sensor data, as well as displacing home position information to a volatile disk partition, such that any reboot would cause the data to be erased.

To *activate lockdown mode*, navigate to the Device settings page on Vozilla:

<figure><img src="/files/gHhbrkN3D0Y6ntJTvP1I" alt=""><figcaption></figcaption></figure>

After activation, your device will reboot automatically. SDK logs and sensor data for all new sessions will now be encrypted. Vozilla API and journal logs remain in plaintext, however, we do not log any sensitive information over these channels in lockdown mode (e.g., home position, coordinates, etc.)&#x20;

{% hint style="info" %}
You need to reset the home position after each reboot once you activate lockdown mode.
{% endhint %}

{% hint style="warning" %}
Lockdown mode encrypts SDK logs and sensor data. It does not encrypt the raw map data.
{% endhint %}

## Audit your device

Once you've activated lockdown mode, run a security audit to check whether your device contains logs with sensitive information exposed in clear text:

<figure><img src="/files/p83CA7qW7XA1u385A9cf" alt=""><figcaption></figcaption></figure>

This security audit checks key log locations on your device for plain text log files and common sensitive information patterns, such as:

* Journald logs
* Vozilla API logs
* Cyclops logs
* Sensor recordings

If you see reported failures, make sure to address them to remove sensitive information from your device.

{% hint style="warning" %}
Activating lockdown mode does not delete previous logs or recordings on your device.
{% endhint %}

## Delete offending logs

After running an audit you may choose to delete plaintext logs to ensure no sensitive information remains on your device.

<figure><img src="/files/vPwxaWD7LivJ0XgLNuFM" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Updating the firmware causes the lockdown mode to reset.
{% endhint %}


# System Status

<figure><img src="/files/RIUeHlEPKxnsgEClvdL7" alt=""><figcaption></figcaption></figure>

The system status tab appears in the bottom panel of Vozilla. It gives real-time feedback on the state of the system to help debug issues with setup.

The dots have 4 possible states:

* Green
  * No issues were detected
* Amber
  * Warning: something isn't functioning as expected
  * This could be a blocking issue, or might be something you can ignore depending on you specific setup
  * Hover to read the warning message
* Red
  * Critical: the system will not function properly without this missing field
  * You need to address this if you want to fly the drone
* Gray
  * No data is available
  * Most likely this data comes from a different data source than the one you are connected with

### Data Sources

There are two information sources: **mavlink** and the **Theseus Edge API**

#### Theseus Edge API

This is a service that runs on the edge device. You need to be connected to the edge device over ethernet to be able to access this data source.

#### MAVLink

If you connect to the flight controller over mavlink in vozilla (see [MAVLink](/gcs-software/mavlink#connecting-with-vozilla)) you'll be able to see a limited number of the status indicators.

* [VPS Diag](/gcs-software/mavlink/monitoring-vps-health-over-mavlink)
* Vozilla Mav
  * shows [heartbeat information](https://mavlink.io/en/messages/common.html#MAV_STATE) of cyclops and the flight controller

### User customization

You can customize the view to display only the status fields relevant to you're connection mode or use case.

<figure><img src="/files/ugeA7IoZ18QVmw7Ti3hI" alt=""><figcaption></figcaption></figure>


# Legacy Software

{% content-ref url="/spaces/hUdZltvGJCfBMBzRuwSC/pages/C5GfueJ2FspliDeimHt6" %}
[Theseus GCS App](/gcs-software/legacy-software/theseus-gcs-app)
{% endcontent-ref %}

{% content-ref url="/spaces/hUdZltvGJCfBMBzRuwSC/pages/MycpGXcdAtjnxvB0thy2" %}
[Theseus Maps App](/gcs-software/legacy-software/theseus-maps-app)
{% endcontent-ref %}


# Theseus GCS App

The Theseus Ground Control Station (GCS) app allows you to **control your Micro VPS device and perform system administration tasks**.

<figure><img src="/files/XQX0GpDn9HqIgEYGIxUa" alt=""><figcaption><p>Theseus GCS app</p></figcaption></figure>

Begin by installing the Theseus GCS app with [Installation](/gcs-software/legacy-software/theseus-gcs-app/installation) and learn how to connect and manage your Micro VPS device in [Quick Start](/gcs-software/legacy-software/theseus-gcs-app/quick-start).


# Installation

{% hint style="warning" %}
The Theseus GCS app is currently only available on **Windows and Linux**. Please contact us if you require support for another OS.
{% endhint %}

Go to <https://maps.theseus.us/latest-release> to download the latest version of the Theseus GCS app.

<figure><img src="/files/VH9zSe7PS31GnnzfMPYD" alt=""><figcaption></figcaption></figure>


# Quick Start

Theseus GCS app allows you to manage your Micro VPS device. In this section, we go over **connecting your computer** to the Micro VPS and taking a look at the **core features** of the Theseus GCS app.

Before starting make sure that you have the following items:

* Micro VPS device, powered
* Ethernet cable
* Windows computer
* Theseus GCS app installed

## Connecting to Micro VPS

{% hint style="info" %}
Make sure you installed the Theseus GCS app on your Windows/Linux laptop following [Installation](/gcs-software/legacy-software/theseus-gcs-app/installation) before proceeding.
{% endhint %}

Theseus GCS app supports connectivity over Ethernet and MAVLink to the Micro VPS. You will need to **edit your computer Ethernet network interface** configuration in order to establish a connection to the Micro VPS.&#x20;

Follow the instructions below to configure your computer to communicate with the Micro VPS over Ethernet.

{% tabs %}
{% tab title="Windows" %}
Here are the IP settings for the network interface:

* IP address: 192.168.218.10
* Subnet mask: 255.255.255.0
* Default gateway: 192.168.218.1

On your Windows computer:

* Open Windows Settings (Windows key + I)
* Click on "Network & Internet"
* Click on "Ethernet"
* Click on your network adapter
* Under "IP assignment", click "Edit"
* Select "Manual" and turn on IPv4
* Enter the IP settings shown above
* Click "Save"
  {% endtab %}

{% tab title="Linux" %}
Here are the IP settings for the network interface:

* IP address: 192.168.218.10
* Subnet mask: 255.255.255.0
* Default gateway: 192.168.218.1

Create a new Ethernet network interface with these IPv4 settings in your settings app.
{% endtab %}
{% endtabs %}

Ethernet connection is preferred for all system administration tasks (e.g., software, maps, recordings management).

MAVLink is available as an alternative mode of connection to the Micro VPS to access the system through your vehicle telemetry.

{% hint style="info" %}
Due to bandwidth limitations, MAVLink connectivity limits functionalities to status monitoring, VPS control and launch position setting.
{% endhint %}

## Dashboard Overview

{% hint style="warning" %}
At this stage, you should be able to open the Theseus GCS and connect to your Micro VPS from your laptop over Ethernet.
{% endhint %}

Below is a breakdown of the key features of the central dashboard, essential to the operation of the Micro VPS.

<figure><img src="/files/nEzBBZ6bruJD8JI0UzCi" alt=""><figcaption></figcaption></figure>

### Menu (1)

The menu on the left of the app allows you to access each feature of the system as indexed in [Feature List](/gcs-software/legacy-software/theseus-gcs-app/feature-list).

### VPS control (2)

The VPS control section enables you to start/stop/restart the VPS software running on your Micro VPS device at any time. This enables you to manually restart the system in case of failures without having to physically reboot the system or vehicle.

### Connectivity (3)

Toggle between Ethernet and MAVLink connectivity modes with Micro VPS. Make sure you have configured your Ethernet connection [#connecting-to-micro-vps](#connecting-to-micro-vps "mention"). Find more details about the MAVLink connection in [MAVLink Connection](/gcs-software/legacy-software/theseus-gcs-app/feature-list/mavlink-connection).

### Version numbers (4)

The version number at the bottom of the left side bar displays the version number of the Micro VPS firmware present on the device as well as the Theseus GCS app version.

{% hint style="info" %}
**Both version numbers should be the same.** Ensure your Micro VPS is up to date before flying.
{% endhint %}

## Setting Launch Location

Navigate to [Home Position Setting](/gcs-software/legacy-software/theseus-gcs-app/feature-list/home-position-setting) to see how to set your launch location for your mission with the Theseus GCS.

{% hint style="info" %}
Theseus Micro VPS requires manually setting home launch location prior to flight and **does not require GPS signal for initialization**.
{% endhint %}

## Uploading Maps

Navigate to [Maps Management](/gcs-software/legacy-software/theseus-gcs-app/feature-list/maps-management) to see how to upload maps for your area of operation with the Theseus GCS.


# Feature List


# MAVLink Connection

Theseus GCS app offers the option to connect to the VPS over MAVLink. Click on the "Wire Connection" dropdown at the top of the dashboard to select your connection type. Select "MAVLink Connection" in the dropdown.

See the [MAVLink](https://mavlink.io/en/) and [ArduPilot](https://ardupilot.org/dev/docs/mavlink-commands.html) documentations for reference.&#x20;

<figure><img src="/files/zHsbeJkO65I2GwWgFOcU" alt=""><figcaption></figcaption></figure>

To connect, specify:

* Connection type (Serial, UDP, TCP)
* Host address + port for UDP/TCP connections
* COM port + baud rate for serial connections

Then, click "Connect". Make sure the port you indicated is open and available.

<figure><img src="/files/acA9f3BOZgomVIncR4cK" alt=""><figcaption></figcaption></figure>

Due to bandwidth limitations, only select features are available over MAVLink:

* VPS control
* [System Health](/gcs-software/legacy-software/theseus-gcs-app/feature-list/system-health)
* Recording start/stop
* [Home Position Setting](/gcs-software/legacy-software/theseus-gcs-app/feature-list/home-position-setting)


# System Health

The Micro VPS exposes system health indicators for three key system services and hardware components. An overview of the system health is present on the main dashboard.

<figure><img src="/files/CPbAhpj0wu9KkOsKByaH" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Recording service is not required for operation of the VPS.
{% endhint %}

## Health Indicator Overview

Services break down as follows:

* MAVProxy service, connects with autopilot over MAVLink.
* VPS service, core VPS software.
* Recording service, records sensor and system data.

Only the MAVProxy and VPS services are required for operation of the VPS.

Both hardware components are required for normal operation of the VPS:

* NVMe storage, disk storage to save maps and logs on the device.
* Sensor connectivity, connection to the sensor module.

<figure><img src="/files/nCkuonb1fVUor0zxaskm" alt=""><figcaption></figcaption></figure>

## Home Position Outside Map Bound

If the current home position is outside of the bounds of the currently selected map, you might see an error like this at the top of the page.

<figure><img src="/files/3j0sFN9Cc52Qc1SpBckb" alt=""><figcaption></figcaption></figure>

When the VPS service is unhealthy, the error logs will be printed out in the system diagnostics. To fix the bad initial fix error, visit [Home Position Setting](/gcs-software/legacy-software/theseus-gcs-app/feature-list/home-position-setting).

## Troubleshooting

Use the "System Health" tab to diagnose issues with your Micro VPS.

| Issue                         | Troubleshooting Steps                                                                                                                                                                                                                                                                                                                          |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Unhealthy NVMe storage        | Issue with firmware setup or hardware failure. Contact your Theseus point of contact immediately.                                                                                                                                                                                                                                              |
| Unhealthy sensor connectivity | <p>Is the sensor powered on?<br>Is the Ethernet cable connected to the compute module?</p>                                                                                                                                                                                                                                                     |
| Unhealthy MAVProxy service    | <p>Is the serial cable connected between the compute module and flight controller? See <a data-mention href="/pages/lmO1ZMulAZRFUNEaq9zm">/pages/lmO1ZMulAZRFUNEaq9zm</a>.<br>Is the serial port configured for MAVLink in the ArduPilot settings? See <a data-mention href="/pages/kITZHqxqBpnINYbZ3c2O">/pages/kITZHqxqBpnINYbZ3c2O</a>.</p> |
| Unhealthy VPS service         | <p>Is your MAVProxy service healthy?</p><p>Is the launch location set? See <a data-mention href="/pages/1bjRAvJkpANoflWxpK16">/pages/1bjRAvJkpANoflWxpK16</a>.<br>Are maps uploaded on your device? See <a data-mention href="/pages/8iZRqGvYbyz7HeG76sqb">/pages/8iZRqGvYbyz7HeG76sqb</a>.</p>                                                |
| Unhealthy recording service   | Not an issue unless you are trying to record sensor data for log sharing. See [Recording Management](/gcs-software/legacy-software/theseus-gcs-app/feature-list/recording-management).                                                                                                                                                         |


# Vehicle Selection


# Recording Management

Micro VPS offers the option to record all sensor data and real time processes to support development and troubleshooting.

{% hint style="info" %}
Enable recordings when conducting integration tests for sharing with the Theseus team.
{% endhint %}

The recording service can be started/stopped from the main dashboard on the Theseus GCS app.

<figure><img src="/files/KZ20nJxNhPdlRoCW6jZZ" alt=""><figcaption></figcaption></figure>

To access all your recordings, navigate to the "Recordings" tab on the left menu.

## Recordings Page

The recordings page allows you to view the available storage space on your device, control your active recording, list all recordings on your device, and download recordings.

The Micro VPS comes with two disks. The NVMe disk is where recordings are stored. eMMC disk is essential to operating system tasks.

{% hint style="warning" %}
Make sure **both disks remain below 80% capacity**. Get in touch with the Theseus team if it goes above.
{% endhint %}

<figure><img src="/files/tS3PYgA7ub6yDMpCY0Wj" alt=""><figcaption></figcaption></figure>

### Current Recording

The "Current Recording" panel shows you the recording for your current boot cycle. Recording directories are unique per sessions which are defined by boot cycles of the device.

You can **start and stop recording** by clicking the "Start Recording" or "Stop Recording" button at the top left of the panel.&#x20;

Click the green "Download" button to **initiate a download of your recording** to your local computer.

<figure><img src="/files/EZ3t9tkmG4FjgueIW8QB" alt=""><figcaption></figcaption></figure>

### All Recordings

The "All Recordings" panel offers the option to **list all available recordings** on disk and download them.

<figure><img src="/files/XYFFFzjQczswegVXfrJ9" alt=""><figcaption></figcaption></figure>

## Log Sharing

When sharing logs for a flight with the Theseus team, note the recording id of the flight. Download the recording by clicking the green "Download" button and share that file with us.


# Home Position Setting

Theseus Micro VPS enables operation in GPS-denied environments from takeoff to landing. It allows you to set your home position/launch location from the Theseus GCS app without GPS.

{% hint style="warning" %}
Your launch location needs to be located within your uploaded map area. See [Maps Management](/gcs-software/legacy-software/theseus-gcs-app/feature-list/maps-management).
{% endhint %}

## Setting Home Position with WGS84 Coordinates

Navigate to the "Position" tab on the left menu. Scroll down to the bottom of the page and enter a latitude/longitude pair into the respective fields. Click the "Update Home Position" button to set the home position on the device.

<figure><img src="/files/99m3k3nPegafFH1vQ2r2" alt=""><figcaption></figcaption></figure>

To confirm that your home position was set properly, refresh the page and navigate to the top of the "Position" page.

## Setting Home Position with Search and Map Cursor

Alternatively, the "Position" page allows you to search the name of the location you would like to set your home position at and drag your cursor on the map.&#x20;

<figure><img src="/files/gAlNg1QqvD1BfcnMWiog" alt=""><figcaption></figcaption></figure>

The cursor automatically updates the latitude and longitude fields as set above. Click the "Update Home Position" button once you've set your cursor at the desired location.

<figure><img src="/files/HgCRQz7haZ1Hf2kbLXMR" alt=""><figcaption></figcaption></figure>

The home position should be located within the boundary of the map you currently have selected. If the select a point outside the the map boundary, you will be prompted to confirm the selection. If you get this prompt, check back to confirm you have the correct maps selected.

<figure><img src="/files/qmfpbR5huPg8hLym4yY5" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**Restart the VPS** via the VPS control tab after selecting new maps.
{% endhint %}


# Maps Management

Uploading maps is a key step in getting your Micro VPS ready for operation.

{% hint style="info" %}
Refer to [Theseus Maps App](/gcs-software/legacy-software/theseus-maps-app) to see how to generate maps for your area of operation.
{% endhint %}

{% hint style="warning" %}
You need to **select your maps after uploading** them.
{% endhint %}

Navigate to the "Map Uploads" tab in the left menu to access the Map Uploads dashboard.

<figure><img src="/files/ZBhIiXNiS4MlOtvbNjOd" alt=""><figcaption></figcaption></figure>

## Uploading New Maps

To upload new maps, click the green "Upload Map" button inside of the "Upload Information" panel.

<figure><img src="/files/uIIKXOJsfWvV73KkTw4G" alt=""><figcaption></figcaption></figure>

This will bring up a window into your computer's file system where you can select the .tar.gz map files to upload.

Once you have selected your maps, a progress bar will appear, indicating the estimated time to completion of your map upload process.

<figure><img src="/files/XTkcdE1prPbhj59JSChF" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
After the uploading process completes, give a few seconds to the app to reflect the new map upload.
{% endhint %}

Once completed, a success message will appear and your new map will be listed under the "All Maps" panel.

<figure><img src="/files/WT9RwGeM76Df8Nx3o7ZK" alt=""><figcaption></figcaption></figure>

## Verify Map Checksum

{% hint style="info" %}
First visit [Viewing Maps](/gcs-software/legacy-software/theseus-maps-app/viewing-maps#map-information) to learn where to find the maps app checksum. You will need to do this before proceeding in this step.
{% endhint %}

Our system has checks to ensure the uploaded map has not been corrupted, but there is no way to ensure that 100% of the data is correct. That's why manually verifying that the checksums match is important. Once the map upload has successfully completed, you will see a popup window displaying the checksum of the file you just uploaded. You must manually check to make sure this matches with the checksum on the Maps App.

<figure><picture><source srcset="/files/xhTbyD3FUyeehb5B6S3u" media="(prefers-color-scheme: dark)"><img src="/files/pq2dL5C4fKyhyowzL8rr" alt=""></picture><figcaption></figcaption></figure>

You can also view the same checksum in the maps list after the popup is dismissed.

<figure><picture><source srcset="/files/5NBbHu69Au0TUhmZKLRB" media="(prefers-color-scheme: dark)"><img src="/files/XEup2SxXjoVcvqDA6Xmk" alt=""></picture><figcaption></figcaption></figure>

## Selecting Maps

Once you have uploaded maps to your device, you can select which map you would like to use from the "Map Selection" panel on the left side of the screen.&#x20;

Click on the dropdown showing the currently selected map area and **select the name of the maps you would like to use**.

<figure><img src="/files/13iqAYpmjhjCNlRheat6" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**Restart the VPS** via the VPS control tab after selecting new maps.
{% endhint %}


# VPS Firmware Updates

Theseus continually improves performance of the VPS software. New software updates can be installed on the Micro VPS device via the Theseus GCS app.&#x20;

Navigate to the "Update Firmware" tab on the left menu.

<figure><img src="/files/qN8bT12VJ1Zhw317bPRe" alt=""><figcaption></figcaption></figure>

This brings you to the firmware updating dashboard. Click on the "Test Connection" button on the left side of the screen to verify the health of your connection to the compute module.

<figure><img src="/files/8IHRVayIq6peBQeRXhuL" alt=""><figcaption></figcaption></figure>

## Over-the-air (OTA) Updates

Signing in to your [Theseus Account](/gcs-software/legacy-software/theseus-gcs-app/feature-list/theseus-account) allows us to automatically check for updates to the Micro VPS.\
Once you are signed in, the dashboard will check if your software is out of date. You will see a warning badge on the Update Firmware tab prompting you to update.

<figure><picture><source srcset="/files/rzfDiEtTPPwKmij7y5Fv" media="(prefers-color-scheme: dark)"><img src="/files/TVPxRzJzcD2Olwhx8SVq" alt=""></picture><figcaption><p>Highlighting update firmware tab</p></figcaption></figure>

Click the "Download Update" button to begin updating your Micro VPS. The update process needs an internet connection to download the software, and for you to be signed in to your Theseus account.

<figure><picture><source srcset="/files/avQ6O0NVdnu01EGwpcyA" media="(prefers-color-scheme: dark)"><img src="/files/o9n6OQ7yf9sLqrinGdEl" alt=""></picture><figcaption></figcaption></figure>

Downloading the update can take up to few minutes. When the update has been downloaded successfully, you'll be prompted to install it. Click "Install Firmware" to proceed with the update.

<figure><picture><source srcset="/files/RmM2FJV3S2VcFyfkd1xP" media="(prefers-color-scheme: dark)"><img src="/files/1zxa82iCgYXN3TKFEEYA" alt=""></picture><figcaption></figcaption></figure>

You can view the status updates while the software is being updated.

<figure><picture><source srcset="/files/4Al4WMsdX0CVTN8eSc1a" media="(prefers-color-scheme: dark)"><img src="/files/jcPIDRaLW1sI1Pf3WXJr" alt=""></picture><figcaption></figcaption></figure>

Once the software update is complete, you will be prompted to reboot the device. This ensures all services have been properly refreshed and are using the up-to-date code.

<figure><picture><source srcset="/files/M2VVOeCccHifdtGohMhi" media="(prefers-color-scheme: dark)"><img src="/files/d6hkqA7nQKPPEAb6VpPz" alt=""></picture><figcaption></figcaption></figure>

## Uploading New Software

{% hint style="info" %}
This section is for manually installing a .deb binary. You can also update software with [#over-the-air-ota-updates](#over-the-air-ota-updates "mention").
{% endhint %}

If you'd prefer manually updating the Micro VPS software, you need to go and download the [latest software release](/gcs-software/legacy-software/theseus-maps-app/releases#latest-release) from our website. The Micro VPS software is a contained in a **.deb** file which should be uploaded to the Micro VPS using the "Update Firmware" page.

Click on the "Update Controls" panel to select your .deb file and upload it to the device.

<figure><img src="/files/LcpwfScfIK5eR0Fg5Qsg" alt=""><figcaption></figcaption></figure>

Once you select your file, it will be uploaded to the device.

<figure><img src="/files/XYeh5YXvoBobtOWzNDoJ" alt=""><figcaption></figcaption></figure>

Once your binary is uploaded, it is ready to be deployed. Click the "Update VPS" button to deploy the software.

<figure><img src="/files/Wk2dBv58Rcr09qVC8Kix" alt=""><figcaption></figcaption></figure>

All versions of the software present on the device are shown at the bottom of the page. You may select another version and choose to install it as you see fit.

<figure><img src="/files/RKsZSrC0kRX8eEHgJ6wr" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
We recommend **always using the** [**latest version**](/gcs-software/legacy-software/theseus-maps-app/releases#latest-release) **of the VPS software**.
{% endhint %}


# Deactivating Map Matching

You can deactivate the map matching functionality by toggling the button on the homepage of the dashboard.

{% hint style="warning" %}
Make sure you restart the VPS after deactivating map matching.
{% endhint %}

<figure><img src="/files/JSTShJbGM2yoA3kb3i6s" alt=""><figcaption></figcaption></figure>

When map matching is deactivated, the VPS will only use Visual Odometry to provide position estimates. The position estimate will drift as you fly.

You do not need to load maps for the area of operations when flying without map matching.


# Theseus Account

We've linked your Theseus Maps App account to the Theseus GCS App. This allows us to check for updates to make sure you're using the most up to date versions of our software.

Navigate to the "Account" tab on the left menu.

<figure><picture><source srcset="/files/fktEJ8okuvhwEGb0RIAg" media="(prefers-color-scheme: dark)"><img src="/files/CqsMjltvfJdA68fsAAl4" alt=""></picture><figcaption></figcaption></figure>

This brings you to the Theseus account sign in page. If you don't already have an account, check out [Broken mention](broken://pages/J54vfiVSXpRmBbNpHWnQ).

Once you have an account with the Theseus Maps App, you can sign in with your email and password in the GCS App.

<figure><picture><source srcset="/files/2YvSuiHL0dof8LBgOhXh" media="(prefers-color-scheme: dark)"><img src="/files/mgd2CFpSGxLMAsjDQWMv" alt=""></picture><figcaption></figcaption></figure>

<figure><picture><source srcset="/files/haAhyCS9bFZt4KANATDS" media="(prefers-color-scheme: dark)"><img src="/files/nV82GOeR7iYJggVMsseb" alt=""></picture><figcaption><p>Message upon successful sign in</p></figcaption></figure>

Now you're all ready to receive updates to your product. If your curious about this feature, see [VPS Firmware Updates](/gcs-software/legacy-software/theseus-gcs-app/feature-list/vps-firmware-updates).


# Vehicle Configuration

The Micro VPS needs to know the orientation of the flight controller relative to itself in order to accurately give a position estimate. The GCS App has a tool to define this transformation and save it to a file.

Check out our  [Mechanical Installation](/micro-vps/vehicle-integration/mechanical-installation) page for information on how to properly mount you Micro VPS and create the transformation.

## Generator Tool

<figure><picture><source srcset="/files/S8wUNyp0kcOUqITmf9u3" media="(prefers-color-scheme: dark)"><img src="/files/mvWFjfyVuWYJTlJiWsLC" alt=""></picture><figcaption></figcaption></figure>

<figure><picture><source srcset="/files/4hpmpPoATEzAz4A7yGQV" media="(prefers-color-scheme: dark)"><img src="/files/YbJxdmnLCKSlsHoRhLfq" alt=""></picture><figcaption></figcaption></figure>

## Changing the sysid

You might wish to change the sysid of your flight controller, but by default the Micro VPS searches for sysid 1. To change this default, you can use the vehicle configuration generator tool (≥ v1.7.7).

Create your vehicle config file and change the sysid/compid to the values used by your flight controller, then upload the config file, select it in the vehicle params dropdown, and finally restart the Micro VPS.

<figure><picture><source srcset="/files/9I8IAbwzB9QqrTyCV90W" media="(prefers-color-scheme: dark)"><img src="/files/K1qn5R9JOWWp6Ksem1pj" alt=""></picture><figcaption></figcaption></figure>


# Theseus Maps App

[Theseus Maps app](https://maps.theseus.us/) is a web application that allows you to **generate map sets for your operations** with the Micro VPS.

<figure><img src="/files/ERuUYEKPPpPeoigW3mSU" alt="Theseus Map app"><figcaption><p>Theseus Maps app main dashboard</p></figcaption></figure>

## 3 steps to generate maps

1. [Broken mention](broken://pages/J54vfiVSXpRmBbNpHWnQ)
2. [Generate maps](/gcs-software/legacy-software/theseus-maps-app/generate-maps)
3. [Download maps](/gcs-software/legacy-software/theseus-maps-app/download-maps)

## Why do I need this?

Theseus Micro VPS **requires pre-processed satellite imagery** to perform map matching. Micro VPS can store up to 50,000 sq. km of map area for any mission (e.g. 20 km x 2500 km area). The map app allows you to generate and download these maps before your mission.

To learn more about Micro VPS and our map matching technology visit the [Technology page on Theseus' website](https://www.theseus.us/technology).&#x20;


# Generate maps

## Recent Changes

We recently changed how maps are stored and generated so you can make larger maps, and as well as more efficient non-rectangular maps. If flying near the edge of the map is something you plan on doing, please make sure all your software is up to date. The important changes are listed below:

* New maps require VPS firmware version >=1.7.7  to work properly
  * Versions 1.7.4-1.7.6 will not be able to check the boundary, so leaving the map area runs the risk of breaking the VPS
* Maps are now generated to cover the entire selected area which means you are good to fly to the edges of the map. No buffer is needed anymore
* Since maps have a buffer zone around the selected area built in, map generation takes slightly longer due to more tiles needing to be processed

## Map Generation

To generate maps follow these three steps on the Theseus Maps app:

1. Search for your location [#id-1.-search-location](#id-1.-search-location "mention")
2. Select your area [#id-2.-select-area](#id-2.-select-area "mention")
3. Launch map generation [#id-3.-create-maps](#id-3.-create-maps "mention")

{% hint style="info" %}
You can expect the map generation to take \~1 hour per 1000 square km. The completed maps take up \~1GB per 200 square km so downloading a large map may take a long time with slow internet connection.
{% endhint %}

## 1. Search location

Either enter the name of your location in the search bar or enter the WGS84 coordinates of your area. Hit enter and the map will pan to your location automatically.

{% columns %}
{% column %}

<figure><img src="/files/SRpCQqRUGoZl3QoT6vFV" alt=""><figcaption></figcaption></figure>
{% endcolumn %}

{% column %}

<figure><img src="/files/d5vvXyVJO8qRyYKKMUfw" alt=""><figcaption></figcaption></figure>
{% endcolumn %}
{% endcolumns %}

## 2. Select area

Click the draw area button. This allows you to drag your area onto the map below. A widget appears with information about the size of the selected area, boundaries.

{% hint style="warning" %}
**Your selected area size must be between 50 and 50,000 sqkm.** Newly generated maps cover the entire selected area. If using maps from before Nov 19th, 2025, make sure that the boundaries of your autonomous mission include at least 2 km of padding to the edges of the selected map area.
{% endhint %}

{% columns %}
{% column %}

<figure><img src="/files/IEvnrTE6lHbPK1lzipDP" alt=""><figcaption></figcaption></figure>
{% endcolumn %}

{% column %}

<figure><img src="/files/msoYw5J39aelnRBqV6nn" alt=""><figcaption></figcaption></figure>
{% endcolumn %}
{% endcolumns %}

## 3. Create maps

Once you've selected your map area, click the Generate Maps button in the top right section of the dashboard. This will prompt with you a confirmation modal. Confirm your request to start generating your maps.

{% columns %}
{% column %}

<figure><img src="/files/3q31OGw7fwuaDJWYbO7Q" alt=""><figcaption></figcaption></figure>
{% endcolumn %}

{% column %}

<figure><img src="/files/xmD9VjxGsBPPqMMbQuNq" alt=""><figcaption></figcaption></figure>
{% endcolumn %}
{% endcolumns %}

## What happens under the hood?

Theseus queries its satellite map provider for imagery over the area you selected. Theseus' map matching technology is invariant to seasonal and landscape changes in the imagery. We employ commerical-grade satellite imagery with medium resolution.

## Large & Non-rectangular maps

Theseus now supports generating maps larger than 2000 square km as well as maps that fit any desired shape. You can custom make a map to fit your mission, or just choose a large area if you plan on mostly flying in the same place.


# Download maps

The Theseus Map app allows you to view all your maps that are being generated and have completed generation.&#x20;

## Active Jobs

See all the maps currently being generated under the "Active Jobs" tab on the left side of the dashboard. Each card shows the name of the map and time since creation.

<figure><img src="/files/uMKu5XxQAfZltKLawBbQ" alt=""><figcaption></figcaption></figure>

## Job History

All completed and current jobs are listed in the job history tab. Click on the "Job History" tab on the top menu to pop up a panel with all your map generation jobs.

<figure><img src="/files/FCDyNKc3twUqcIEufTTT" alt=""><figcaption></figcaption></figure>

You can view and download maps from this panel.

<figure><img src="/files/DIfnCQDPWuxr4lKiTZkH" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Maps are downloaded as a single **.tar.gz** file to be uploaded to your device.
{% endhint %}


# Viewing Maps

The Maps App lets you view information about previously generated maps. Click on the map card in the row in the job history.

<figure><img src="/files/xhDAq61QC28al7STDxtK" alt=""><figcaption></figcaption></figure>

It pulls up the original bounding, so you can view the area the map covers. There is also a card with some map metadata.

<figure><img src="/files/4P681Nm2KbGfgAmvAGxy" alt=""><figcaption></figcaption></figure>

## Map Information

<figure><img src="/files/zyNGpL9uhbAq6pB1REgc" alt=""><figcaption></figcaption></figure>

### Map ID

{% hint style="info" %}
If you are having issues with the map contact us citing the map id as it helps us find the map quickly to debug the issue.
{% endhint %}

### Checksum

The checksum allows you to check that the map wasn't corrupted on its journey from the cloud to your Micro VPS.


# Releases

The maps app has tabs to view and download software releases.

## Latest Release

<figure><img src="/files/7iljE2Im7M8gPDqdXDgY" alt=""><figcaption></figcaption></figure>

The "Latest Release" tab shows only the most recent release for the Micro VPS. Also included in these releases are the latest GCS App version. If the release is missing the binaries for the GCS App it means there were no new updates to the GCS App since the last release.

## Releases

<figure><img src="/files/OujNCZdJviBCaZ1nBNJF" alt=""><figcaption></figcaption></figure>

The "Releases" tab show all software releases for Theseus products. Changelogs for the maps app can also be found here, but they do not have binaries since the maps app is hosted in the cloud.

### Subscribe to release notes

You can subscribe to be notified by email about our software releases and stay up to date with what the team is working.

<figure><img src="/files/5X5STpmfmJrBZZ2IQpgT" alt=""><figcaption></figcaption></figure>

You can also find release notes posted in the releases channel of our support discord server.


# Vozilla (legacy)

The following documents information on how to navigate the Vozilla Ground Control Station v1.14.2 and below.

The Vozilla Ground Control Station (GCS) App allows you to **control your Micro VPS device, generate and upload new maps, review flight logs, and perform system administration tasks**.

<figure><img src="/files/SNS3y6rWzPEGT0HWgLDP" alt=""><figcaption><p>Vozilla GCS app</p></figcaption></figure>

Begin by installing the Vozilla GCS app with [Broken mention](broken://pages/qKJI6wM207wbITqste5I) and learn how to connect and manage your Micro VPS device in [Broken mention](broken://pages/qj8Jsn13WrMBDh1WRYvp).


# Maps

The **Maps** tab allows you to **generate and upload maps prior to your operations** with the Micro VPS. This is also where you can **set your home/takeoff position and switch between generated maps.**

<figure><img src="/files/SNS3y6rWzPEGT0HWgLDP" alt="Vozilla Maps tab"><figcaption><p>Maps Tab</p></figcaption></figure>

## Why do I need this?

Theseus Micro VPS **requires pre-processed satellite imagery** to perform map matching. Micro VPS can store up to 50,000 sq. km of map area for any mission (e.g. 20 km x 2500 km area). The Maps tab allows you to generate and download these maps before your mission.

To learn more about Micro VPS and our map matching technology visit the [Technology page on Theseus' website](https://www.theseus.us/technology).&#x20;

## Maps Information

Map file sizes are \~5-6MB per km<sup>2</sup>. A 20,000 km<sup>2</sup> would be around 100GB. The amount of time it takes to download a map depends on your internet speed. Large maps take a long time to generate and download, so please be sure to do this well in advance of your flights.

Maps are generated with a buffer area around them, so the exact size of the map and number of tiles generated will always be more than the selected area. The percentage of the total map that is the buffer area depends on the size and shape of the map.


# Creating a new map

## Map Generation

To generate maps follow these steps on the Maps tab:

1. Click the **New Map** button at the top of your Map Library
2. Create your map boundary

{% hint style="info" %}
You can expect the map generation to take \~1 hour per 1000 km<sup>2</sup>. The completed maps take up \~1GB per 200 km<sup>2</sup> so downloading a large map may take a long time over a slow internet connection.
{% endhint %}

## 1. Generating a new map

Click the **NEW MAP** button at the top of your Map Library, then navigate to your desired flight area. You can do this by entering either the name of your location or the WGS84 coordinates in the search bar. Hit *enter* and the map will pan to your location.

<figure><img src="/files/r5Ml03PZqHb5rUcZZDSo" alt=""><figcaption></figcaption></figure>

## 2. Create your map boundary

Draw the polygonal map boundary around your flight area. Ensure the boundary covers the entire flight area for your mission, as your system cannot localize outside of the map. Add a distinct name for your map in the field provided.

<figure><img src="/files/ChaWrTUfhlchxvAvNS2D" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
**Your selected area size must be between 50 and 50,000 km**<sup>**2**</sup>**.** Newly generated maps cover the entire selected area. If using maps from before Nov 19th, 2025, make sure that the boundaries of your autonomous mission include at least 2 km of padding to the edges of the selected map area.
{% endhint %}

## 3. Generating your map

Once you've selected your map area and added a name, click the **GENERATE** button on the left of the screen. The map will begin generating in the background. This process may take a few hours, so it is best to generate maps well before flying.

## What happens under the hood?

Theseus queries its satellite map provider for imagery over the area you selected. Theseus' map matching technology is invariant to seasonal and landscape changes in the imagery. We employ commercial-grade satellite imagery with medium resolution.

## Large & Non-rectangular maps

Theseus now supports generating maps larger than 2000 km<sup>2</sup> as well as maps that fit any desired shape. You can generate a precise map to fit a mission, or select a large area if you plan on mostly flying many routes in the same area.


# Selecting a map

## Map Flow

Maps are large files and are generated and stored on the cloud. If you want to put a map on your edge device, you first need to download it from the cloud to your local machine. This can take a long time even with a good internet connection depending on the size of the map.

After the map is downloaded, you can upload it to the edge device. It will be stored there until you delete it. You can select any of the maps on the edge device as the active map simply by selecting it in the maps tab of the app.

## Map Library

All maps linked to your account can be viewed from the Maps Tab on the Vozilla GCS App. Any previously generated map can be downloaded to your device. You will also be able to see and map generations that have failed, as well as archive maps that you are not using.

To download a map, click on the name in the Map Library, then click the orange DOWNLOAD button.

<figure><img src="/files/duJJqrUK9Ut476JaWCBk" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Maps are downloaded as a single **.tar.gz** file to be uploaded to your device.
{% endhint %}

## Uploading maps to the edge device

Once a map has been downloaded to your local machine, you can upload it to the edge device.

<figure><img src="/files/mtF5UyhgYdXgQpBAV691" alt=""><figcaption></figcaption></figure>

After being uploaded, the new map is automatically selected as the active map and the home position is set.


# Setting a home position

Each map has a corresponding home position. This position represents the takeoff location of your UAV, and is the location where the Micro VPS will initialize. It is represented by a circle within the map boundary on the map screen.

To set your home position, either drag the circle to a desired location within the map boundary or enter coordinates in the *Home Position* field. Then click the **SAVE** button to set this as the new home position.

Saving the home position automatically restarts the MVPS service to load in the new starting position.

<figure><img src="/files/anlSAbzvPOO1mCP4gQRG" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/vyA4YzUGw8RnkmhjsLoD" alt=""><figcaption></figcaption></figure>

### Uploading to the edge device

Once your map has been generated, click the download button and then upload to VPS.

<figure><img src="/files/SPlr2ZMnpA6f2UZKi2Mc" alt=""><figcaption></figcaption></figure>


# Flights

The **Flights** tab stores logs from all of your flights using the Micro VPS. Here, you can analyze the performance of the system, as well as compare the VPS position to GPS, if applicable. You can also use playback tools to review your flights.&#x20;

<figure><img src="/files/AZTLRiRsFRSupyD7KeNF" alt=""><figcaption><p>Flights Tab</p></figcaption></figure>

## Syncing Flights

Vozilla automatically synchronizes all logs from your device. This enables you to review logs offline and share logs with our team for further analysis.

We read the `MAV_LANDED_STATE`  values from the autopilot throughout the flight to determine the state of the aircraft. Vozilla looks for transitions from `ON_GROUND` to `IN_AIR` states to determine whether logs synced off the device correspond to a real flight or a recording while on the bench.

{% hint style="info" %}
After a flight, plug your device into your laptop running Vozilla to auto sync flight logs.
{% endhint %}

## Evaluating Performance

Theseus software running on the edge automatically pulls GPS from GPS 1 on ArduPilot when it is available. These GPS positions get logged alongside our estimated position.

Once your flights are synchronized on Vozilla, you can review performance by clicking on flights listed under the Flights panel. Several tracks will appear on the map:

* Gray track shows GPS positions
* Dots show map matching points, color indicates scales with accuracy of the estimated position (red for lower accuracy, green for higher accuracy)

The accuracy figure shown under the Metrics tab is the median accuracy between GPS and map matching throughout the flight.


# Reviewing a flight

Select a flight to display that flight on the map. Both GPS and position estimate tracks are displayed if your UAV has both. Pressing the play button below the map replays the flight so you can track the accuracy at any point of the flight. You can also drag the slider along the playback bar to scrub through the flight.

The **Metrics** tab shows the median accuracy of the VPS relative to the GPS and the coordinates of both positions. Moving the slider on the playback bar to any given point during the flight will display the accuracy and coordinates at that point in the flight.

<figure><img src="/files/AZTLRiRsFRSupyD7KeNF" alt=""><figcaption><p>Metrics Tab</p></figcaption></figure>

The **Notes** tab allows you to add observations and comments to your flight logs for later reference.

<figure><img src="/files/FqhcpwSofmXd7Z7SZUOc" alt=""><figcaption><p>Notes Tab</p></figcaption></figure>


# Managing flight logs


# MAVLink

The MAVLink Tab is where you can connect your preferred GCS software such as Mission Planner or QGroundControl to the Vozilla app through a MAVProxy connection. This allows you to see messages and data from your UAV while it is in the air.

You can view the MAVProxy console, edit parameters, view MAVLink messages, and see a real-time map of ArduPilot's GPS and GPS2 positions.

<figure><img src="/files/0Dd2NizhnDjQaoozwLVV" alt=""><figcaption></figcaption></figure>


# System

## VPS Status

The service responsible for publishing global position updates is the most important part of the system.

<figure><img src="/files/bhlpDpkvoq42GFuVc41J" alt=""><figcaption></figcaption></figure>

The status of this service should be "*active*" if you're planning to fly. If it displays a "*failed*" state, try restarting it as it needs an active MAVLink connection to work properly. If it continues to fail, check the service diagnostics area for a detailed error message.

<figure><img src="/files/uzRKFbqwL6bgGbm7lptK" alt=""><figcaption></figcaption></figure>

The most common reasons for the service failing are not having set an active map or home position, and not having a proper connection to a flight controller.

## Configuring MAVProxy

We use [MAVProxy](https://ardupilot.org/mavproxy/) onboard the compute to route MAV connections. The Configuration panel lets you customize MAV settings when connecting to your flight controller.

<figure><img src="/files/V359z0tWv08TZ2hcpblY" alt=""><figcaption></figcaption></figure>

## Sensor Recordings

All of the important information and sensor data is stored as protobuf messages in a live recording format called [hdf5](https://www.hdfgroup.org/solutions/hdf5/). You can enable or disable these recordings from any tab in the Vozilla app using the **REC** toggle in the top right corner .

<figure><img src="/files/W8nH8l3ZKIXL0JMEthg8" alt=""><figcaption></figcaption></figure>

Sensor recordings are large files and can fill up space on the NVMe drive. It is recommended that your disable recording while testing on the bench.

We strongly advise that you enabled recording before flying your UAV. It will be difficult to help debug any issues with VIO performance without sensor recordings.

{% hint style="warning" %}
The recording setting is persistent. If you disable it for bench testing, you will need to remember to turn it back on when you go out and fly. It is better to leave it enabled at all times if you're worried you might forget to enable it when flying.
{% endhint %}

## Remote Support

The remote support feature allows Theseus to create an ssh connection to your edge device so we can help configure things and debug issues with the product.

<figure><img src="/files/Zk4kebePB7AokACBfhPA" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Please set up a call with the Theseus team if you're running into issues. This feature allows our team to offer better remote support to customers. There is no on-call engineer waiting to fix issues.
{% endhint %}


# Feature List


# Vehicle Configuration

{% hint style="info" %}
Cyclops users don't need to do this step. The transformation is needed for the vio only.
{% endhint %}

The Micro VPS needs to know the orientation of the flight controller relative to itself in order to accurately give a position estimate. The GCS App has a tool to define this transformation and save it to a file.

Check out our  [Mechanical Installation](/micro-vps/vehicle-integration/mechanical-installation) page for information on how to properly mount you Micro VPS.

<figure><img src="/files/9Ava0gougcYdg4LfywvB" alt=""><figcaption></figcaption></figure>

Clicking the edit button will bring up a panel with some 3D models. You can translate and rotate the Micro VPS, then upload the config to the edge device when it matches your physical setup.

<figure><img src="/files/NSRhdUM8d198zq1Sw6l6" alt=""><figcaption></figcaption></figure>


# VPS Firmware Updates

Theseus continually improves performance of the VPS software. We always recommend using the most up to date firmware. Many times when the Micro VPS displays poor performance or there is a serious issue, the user is on an outdated firmware version and the problems are already fixed on the most recent release.

## Over-the-air (OTA) Updates

Vozilla requires firmware version 1.10.0 or higher to work properly. If you connect it to an outdated Micro VPS it will prompt you to update.

<figure><img src="/files/14w0whycX13hH6Xotcsj" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/zlahYdJncOLw5bzk18A5" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
It's important that you keep the edge device on and powered until the install process is complete. If it doesn't finish installing properly you will need to reinstall the firmware package.
{% endhint %}

When new firmware updates are available you will be able to check in the system tab and update from there.


# MAVLink

How to connect to your device over MAVLink

[MAVLink](https://mavlink.io/en/about/overview.html) is a standardized protocol used frequently with drones. It works with many different flight controllers, programming languages, and links. Visit the mavlink docs for more info about how it works.

We also use a program called mavproxy that implements the mavlink protocol and includes lots of important features to make mav usable. The docs for mavproxy can be found [here](https://deepwiki.com/ArduPilot/MAVProxy).

Below is a diagram of a proposed mavlink system. The below setup is how we setup our drones for testing at Theseus.

<figure><img src="/files/iXTPcHIMv0XYAYxd8VBv" alt=""><figcaption></figcaption></figure>

Theseus provides two ways to connect to your system over mavlink:

1. Connect to the edge device (for debug)
   1. Devices running Theseus software route mav packets through mavproxy
   2. Mavproxy has an open udp input connection (1) that you can connect to with udpout
   3. Mavproxy on the edge device lists the flight controller (4) as the one and only master
      1. Cyclops/mvps (3) and the external mav connection (1) are listed as outputs
      2. Mav packets are only routed from an output to a master, or a master to all outputs
      3. You will not be able to see mav packets from the edge device, or send mav packets to the edge device if you connect over this link
   4. It exists solely as a pass through to allow you to connect to your flight controller through the edge device (mainly for convenience)
2. Direct connect to the flight controller
   1. Use this connection method for flying
   2. If using ardupilot, status text messages will be routed from the edge device to your ground station

### Connecting with Vozilla

<figure><img src="/files/tPUvwGfxnzJeclmi5uoF" alt=""><figcaption></figcaption></figure>

Use this dropdown menu in the top right corner of Vozilla to select a connection profile. However you connect to your flight controller normally is what you should use. You can also add output ports so you can connect on other applications like mission planner simultaneously.

### Other MAVLink info

mvps mavlink info

{% content-ref url="/pages/N1GmS41CBG7VyFgmIkDt" %}
[MAVLink](/micro-vps/autopilot-integration/mavlink)
{% endcontent-ref %}

cyclops mavlink info

{% content-ref url="/pages/9QMyFIrShlxm3bbAbiKb" %}
[MAVLink](/cyclops/autopilot-integration/mavlink)
{% endcontent-ref %}

vozilla (<2.0) mavlink page

{% content-ref url="/pages/OZzHzPKCdfyGrhjSmHo3" %}
[MAVLink](/gcs-software/legacy-software/vozilla-legacy/mavlink)
{% endcontent-ref %}


# Monitoring VPS Health Over MAVLink

The VPS reports its health on the same MAVLink link you already use for telemetry, so you can monitor it from QGroundControl, Mission Planner, MAVProxy, a Lua script on the autopilot, or any other MAVLink consumer.

Two messages do the work:

1. A standard [`HEARTBEAT`](https://mavlink.io/en/messages/common.html#HEARTBEAT) that tells you the VPS lifecycle state (booting, ready, active, failed, etc.).
2. A [`NAMED_VALUE_INT`](https://mavlink.io/en/messages/common.html#NAMED_VALUE_INT) named `VPS_ERR` that carries a numeric error code explaining *why* the VPS is in its current state.

You only need the heartbeat for a quick "is it alive and active?" check. Add `VPS_ERR` when you want to know what to do about it.

## At a Glance

### HEARTBEAT

Sent at 1 Hz with [`type = MAV_TYPE_ONBOARD_CONTROLLER` (18)](https://mavlink.io/en/messages/common.html#MAV_TYPE_ONBOARD_CONTROLLER). The field you care about is `system_status`, a standard [`MAV_STATE`](https://mavlink.io/en/messages/common.html#MAV_STATE) value. The most common ones to watch for:

* **`MAV_STATE_STANDBY` (3)** — VPS is ready.
* **`MAV_STATE_ACTIVE` (4)** — VPS is operating normally.
* **`MAV_STATE_CRITICAL` (5)** — VPS is running but degraded.
* **`MAV_STATE_EMERGENCY` (6)** — VPS has failed.

If the heartbeat stops entirely, treat that as "VPS not running" — the heartbeat only flows while the VPS service is alive.

### VPS\_ERR

Sent at 1 Hz as a `NAMED_VALUE_INT` with `name = "VPS_ERR"`. The integer value is the error code. **`0` means everything is healthy.** Any non-zero value identifies a specific condition.

`VPS_ERR` is published by a separate monitoring service that runs independently of the VPS itself, so it keeps reporting even when the VPS has crashed or has been stopped. This is the channel to listen on if you want to know *why* the heartbeat went away.

## How to Read These Messages

If you are using QGroundControl or Mission Planner, both messages are visible in the standard MAVLink Inspector. `VPS_ERR` appears alongside any other `NAMED_VALUE_INT` from the vehicle.

From pymavlink:

```python
from pymavlink import mavutil

conn = mavutil.mavlink_connection('udpin:0.0.0.0:14550')

while True:
    msg = conn.recv_match(blocking=True)
    if msg is None:
        continue
    if msg.get_type() == 'HEARTBEAT' and msg.type == 18:
        print(f"VPS lifecycle: MAV_STATE={msg.system_status}")
    elif msg.get_type() == 'NAMED_VALUE_INT' and msg.name.startswith('VPS_ERR'):
        print(f"VPS_ERR={msg.value}")
```

From an ArduPilot Lua script, use `mavlink:receive_chan()` and filter on message ID 251 (`NAMED_VALUE_INT`) with the name `VPS_ERR`.

## Common Conditions

These are the codes you are most likely to see during normal use. The full table is at the end of this guide.

| `VPS_ERR` | What it means                                           | What to check                                                             |
| --------- | ------------------------------------------------------- | ------------------------------------------------------------------------- |
| `0`       | Healthy                                                 | Nothing — all systems nominal                                             |
| `1`       | No autopilot heartbeat received yet                     | Serial / UART connection between the VPS and the flight controller        |
| `2`       | Time sync between VPS and autopilot is still converging | Wait — this clears on its own once enough samples arrive                  |
| `3`       | No camera detected                                      | USB / CSI camera connection                                               |
| `4`       | Camera detected but not streaming                       | Power-cycle the camera or restart the camera service                      |
| `5`       | Missing autopilot odometry streams                      | `ATTITUDE` and `LOCAL_POSITION_NED` stream rates on the flight controller |
| `20`      | VPS service is stopped (clean shutdown)                 | Start the service                                                         |
| `30–50`   | VPS service crashed                                     | See the crash codes table below for the specific reason                   |

Codes 1–5 are normal during startup and clear themselves as each subsystem comes up. Anything in the 20s or above means operator attention is needed.

## What to Expect During a Healthy Boot

When the system powers on, you will typically see `VPS_ERR` step through these values as each subsystem initializes:

```
1  -> waiting for autopilot heartbeat
2  -> heartbeat received, time sync converging
3  -> time sync done, waiting for camera
0  -> all subsystems up, VPS healthy
```

In parallel, `system_status` in the heartbeat moves from `BOOT` (1) through `CALIBRATING` (2) and `STANDBY` (3) to `ACTIVE` (4).

If `VPS_ERR` gets stuck on a non-zero value for more than about 30 seconds, that subsystem has a problem worth investigating.

## Reference Tables

### `MAV_STATE` Mapping

The `system_status` field in the heartbeat reflects the VPS lifecycle:

| `MAV_STATE`             | Value | Meaning                                                              |
| ----------------------- | ----- | -------------------------------------------------------------------- |
| `MAV_STATE_UNINIT`      | 0     | Not yet initialized                                                  |
| `MAV_STATE_BOOT`        | 1     | Starting up — loading config, waiting for autopilot, or syncing time |
| `MAV_STATE_CALIBRATING` | 2     | Waiting for the camera to start streaming                            |
| `MAV_STATE_STANDBY`     | 3     | Ready, waiting to be armed                                           |
| `MAV_STATE_ACTIVE`      | 4     | Operationally active                                                 |
| `MAV_STATE_CRITICAL`    | 5     | Running with issues                                                  |
| `MAV_STATE_EMERGENCY`   | 6     | Failed                                                               |
| `MAV_STATE_POWEROFF`    | 7     | Stopped                                                              |

### `VPS_ERR` Codes

#### Boot and waiting states (1–9)

These are normal during startup. They clear themselves once the corresponding subsystem comes up.

| Code | Name                      | Message                                                                                                   |
| ---- | ------------------------- | --------------------------------------------------------------------------------------------------------- |
| `0`  | `NONE`                    | System healthy                                                                                            |
| `1`  | `WAITING_MAV_HEARTBEAT`   | No autopilot detected. Check serial connection.                                                           |
| `2`  | `WAITING_TIMESYNC`        | MAV connected, time sync converging.                                                                      |
| `3`  | `WAITING_CAMERA_IMAGES`   | Camera not publishing images. Check USB/CSI.                                                              |
| `4`  | `CAMERA_OPEN_FAILED`      | Camera detected but not streaming. Restart usb-camera service.                                            |
| `5`  | `WAITING_MAV_EKF_STREAMS` | Not receiving `ATTITUDE` / `LOCAL_POSITION_NED` from the autopilot. Check stream rates and FC connection. |

#### Service-level errors (20–29)

These are reported when the VPS process itself is not running.

| Code | Name                  | Message                  |
| ---- | --------------------- | ------------------------ |
| `20` | `SERVICE_NOT_RUNNING` | VPS service not running. |

#### Crash reasons (30–50)

When the VPS service has exited with an error, the code identifies the failure category.

| Code | Name               | Message                                                      |
| ---- | ------------------ | ------------------------------------------------------------ |
| `30` | `CRASH_UNKNOWN`    | VPS crashed. Check logs.                                     |
| `31` | `CRASH_LICENSE`    | VPS crashed: license validation failed.                      |
| `32` | `CRASH_CONFIG`     | VPS crashed: invalid configuration file.                     |
| `33` | `CRASH_MAVLINK`    | VPS crashed: MAVLink init failed. Check serial connection.   |
| `34` | `CRASH_TIMESYNC`   | VPS crashed: time sync init failed.                          |
| `35` | `CRASH_ECAL`       | VPS crashed: internal messaging init failed.                 |
| `36` | `CRASH_MAP`        | VPS crashed: map matcher failed. Check GPS fix and map data. |
| `37` | `CRASH_VIO`        | VPS crashed: VIO init failed.                                |
| `38` | `CRASH_EKF`        | VPS crashed: EKF init failed.                                |
| `39` | `CRASH_CAMERA`     | VPS crashed: camera pipeline failed.                         |
| `40` | `CRASH_GEOSPATIAL` | VPS crashed: geospatial data error.                          |
| `41` | `CRASH_MODEL`      | VPS crashed: inference model failed to load.                 |
| `42` | `CRASH_FILESYSTEM` | VPS crashed: file I/O error.                                 |
| `50` | `CRASH_SIGNAL`     | VPS killed by signal (segfault/abort).                       |

A crash code is informational — it tells you *what* failed so you know where to look. For full diagnostics, collect the system logs and contact support.


# QGC Home Position Sender

This setup adds a QGroundControl Fly View action that tells an ArduPilot Lua script to send the flight controller's home position to the VNS Pi.

## Components

* `../home_position_sender.lua` runs on the flight controller. It accepts either `COMMAND_LONG 31000` or a marker request (`COMMAND_LONG 512` with `param1=242`, `param2=31000`), reads the FC home position, then sends `MAV_CMD_DO_SET_HOME` to the Pi at sysid `218`, compid `190`.
* `FlyViewCustomActions.json` runs on the QGC computer. It adds the `Send Home to Pi` action in Fly View and sends command `31000` to the active vehicle.

## Flight Controller Setup

{% stepper %}
{% step %}

## Enable Lua scripting

Enable ArduPilot Lua scripting (`SCR_ENABLE=1`) and reboot the flight controller.
{% endstep %}

{% step %}

## Copy the home position sender script

{% file src="/files/PUPmXn3Vu2BUpjtkp36i" %}

Copy `home_position_sender.lua` to the flight controller SD card:

```
/APM/scripts/home_position_sender.lua
```

{% endstep %}

{% step %}

## Copy the MAVLink Lua modules

Copy the MAVLink Lua modules to:

```
/APM/scripts/modules/MAVLink/
```

At minimum this script needs:

```
mavlink_msgs.lua
mavlink_utils.lua
mavlink_msg_COMMAND_LONG.lua
mavlink_msg_COMMAND_ACK.lua
mavlink_msg_HEARTBEAT.lua
```

{% endstep %}

{% step %}

## Reboot or restart scripting

Reboot the flight controller or restart scripting.
{% endstep %}
{% endstepper %}

## Home and Estimator-Origin Requirements

The script has deterministic home selection logic:

1. Use `vehicle:home()` first.
2. Use `ahrs:get_home()` only if `vehicle:home()` did not return a home object.

What this means operationally:

* The script sends only the selected home position (`lat/lon`) to the Pi.
  * The device will use the terrain data from onboard maps to determine altitude.
* Estimator origin coordinates are never sent to the Pi by this script.
* Estimator origin must be initialized at least once before the home position is considered valid.
* Home position and estimator origin do not need exact coordinate equality, but they must be in the same operating area.
* If estimator origin is not initialized, or home resolves to an invalid value (`nil` or `0,0`), the script does not send `MAV_CMD_DO_SET_HOME` and QGC can show a timeout.

Expected startup message:

```
Home Sender ready (REQUEST_MESSAGE marker 31000, ... MAV channels)
```

## QGroundControl Setup

{% file src="/files/DpHkpve7GGRTMLYIVYob" %}

Copy `FlyViewCustomActions.json` to QGC's MAVLink actions directory.

```
~/Documents/QGroundControl/MavlinkActions/FlyViewCustomActions.json
```

Restart QGC after copying the file.

## Testing the feature

1. Click on the map and select "Set Estimator Origin"\
   ![](/files/M0XPZvSQsUBUGNMZ1nii)
2. Click on the map and select "Set home here"\
   ![](/files/bWT30FwRPqtC7cWT5oc7)
3. In Fly View, open the Actions menu and click "Send Home to pi"\
   ![](/files/wWFIkE277ofQBz9k7T3M)

You should see the vps startup status text messages in the mav messages when the vps restarts.

## References

* ArduPilot Lua scripting: <https://ardupilot.org/copter/docs/common-lua-scripts.html>
* ArduPilot MAVLink command Lua example: <https://github.com/ArduPilot/ardupilot/blob/master/libraries/AP_Scripting/examples/MAVLink_Commands.lua>
* QGC custom MAVLink actions: <https://docs.qgroundcontrol.com/Stable_V5.0/en/qgc-user-guide/custom_actions/custom_actions.html>


# Getting Started

<figure><img src="/files/OtmyHikHcsDDcYytRGEP" alt="" width="375"><figcaption><p>Theseus Micro VPS sensor module</p></figcaption></figure>

## Vehicle Integration Process

1. Have required Micro VPS hardware: [Theseus Micro VPS](/micro-vps/theseus-micro-vps)
2. Check pre-requisites: [Pre-Requisites](/micro-vps/pre-requisites)
3. Mount Micro VPS to your vehicle: [Vehicle Integration](/micro-vps/vehicle-integration)
4. Configure ArduPilot for Micro VPS: [Autopilot Integration](/micro-vps/autopilot-integration)
5. Configure Micro VPS in software: [Theseus GCS App](/gcs-software/legacy-software/theseus-gcs-app)
6. Perform a bench test: [Bench test](/micro-vps/validation/bench-test)
7. Perform a flight test: [Flight test](/micro-vps/validation/flight-test)


# Pre-Requisites

## Platform Requirements

The UAV must have a mounting point for the sensor and compute modules. The system needs to be able to support the added payload weight (300 g total). See [Mechanical Installation](/micro-vps/vehicle-integration/mechanical-installation) for more information on the mounting procedure.

## Power Requirements

Theseus Micro VPS requires a 2S-6S (6-27V) power input for the compute module and 5V power input for the sensor module. It is possible to power the sensor from the compute module. Find more information about power integration for Theseus Micro VPS in [Power](/micro-vps/vehicle-integration/power).

## Flight Controller Requirements

Theseus has integrated with the following flight controller hardware successfully:

* Cube Orange +
* Matek Sys H743

Micro VPS requires a single UART serial port on the flight controller to run MAVLink telemetry to the compute module. See [Flight Controller](/micro-vps/vehicle-integration/flight-controller) for more information on flight controller integration.

Your flight controller must have a barometer (or other altitude source other than GPS) as well as a reliable compass. See [Autopilot Integration](/micro-vps/autopilot-integration) for more information on how Micro VPS interfaces with ArduPilot.&#x20;

{% hint style="info" %}
Micro VPS currently **only supports ArduPilot**. If you would like to integrate other autopilot software, contact our team.
{% endhint %}

## Components

Before proceeding with integration, make sure that you have the following:

* Micro VPS box (including compute and sensor modules, UART cables, power cables)
* Drone with ArduPilot-compatible flight controller
* Windows laptop
* Ethernet cable
* Power source for UAV and Micro VPS

See [Theseus Micro VPS](/micro-vps/theseus-micro-vps) for more information on the specifications of the Micro VPS.

{% hint style="success" %}
Check that you have all listed components and proceed to the next section.
{% endhint %}


# Theseus Micro VPS

Theseus Micro Visual Positioning System (VPS) is camera-based system enabling daytime visual navigation for drones in GPS-denied environments.

<figure><img src="/files/yf42NjhZxI4XXVOmluqw" alt="Micro VPS" width="375"><figcaption><p>Micro VPS</p></figcaption></figure>

<figure><img src="/files/7MhnDEuei9nj2l6QU4s9" alt="Micro VPS All-In-One (AIO)" width="375"><figcaption><p>Micro VPS All-in-One (AIO)</p></figcaption></figure>

## What's in the box

The Micro VPS package includes the following items:

* Sensor module
* Compute module
* UART serial cables (x2)
* USB-C sensor power cable (x1)
* XT30-F compute power cable (x1)
* 1/4"-20 mounting screws for sensor (x2)

The Micro VPS **AIO** package includes the following items:

* All-in-One sensor + compute module with picatinny rail mount
* UART serial cables (x2)
* XT30-F power cable (x1)
* M4x35mm SHCS for rail mount (x2 pre-installed)

<figure><img src="/files/gM0i1sJiO1aYm0IZ8EsO" alt="" width="563"><figcaption><p>AIO package contents</p></figcaption></figure>

{% file src="/files/szEhhPLFWJYET2OYTqad" %}

{% file src="/files/ROMr3Ah9z2PExbaTempw" %}


# Vehicle Integration


# Mechanical Installation

There are three steps to mounting the Theseus Micro VPS on your aircraft:

1. Figure out [#sensor-mounting](#sensor-mounting "mention")
2. Calculate [#mounting-calibration](#mounting-calibration "mention") (very important!)
3. Finally, [#compute-mounting](#compute-mounting "mention")

## Sensor Mounting

{% hint style="info" %}
For any question about your mounting solution, contact us and we will assist.
{% endhint %}

The sensor modules comes with three 1/4"-20 mounting holes. The sensor module can withstand light rain and environmental wear.

<figure><img src="/files/4SgSf6WuN22lwvZqiVCc" alt="" width="563"><figcaption></figcaption></figure>

The sensor can be mounted under the wings or on the belly of your aircraft. It is fine if your mounting solution partially obstructs some of the cameras of the sensor module.

For example, if you are mounting under a wing on a fixed-wing aircraft, it is acceptable for one of the side cameras to be obstructed up to 70%.

{% hint style="warning" %}
Add vibration isolation to your mounting solution if you are mounting near a source of vibration or on a rigid surface.
{% endhint %}

{% hint style="warning" %}
We recommend using the top mounting (1&2) holes instead of the GoPro mount (3) to avoid calibration issues.
{% endhint %}

### Example mounting solutions

{% file src="/files/9Id3ZiGAqUv2enNtISPu" %}

{% file src="/files/wHoZuKSWMVA2yT4gLJ9n" %}

{% file src="/files/frI8JqwKjBKz9D4PYvag" %}

Once you've settled on your mounting solution, proceed to measuring the orientation and translation of the sensor relative to your flight controller.

## Mounting Calibration

**This step is crucial to ensure nominal performance of the Micro VPS.**

**Note:** If your compute module and sensor are separate units, the orientation and position of the compute module is irrelevant here, we're only interested in the **sensor** and the **flight controller**. If the compute and the sensor are integrated you can ignore this note.

<details>

<summary>Why does this matter?</summary>

Part of the Micro VPS stack involves inertial measurements, which tracks the movement of the vehicle in a local frame of reference (e.g., the point where the system initializes is the origin, and displacement is measured relative to that point). The Micro VPS communicates with the autopilot to transform this inertial frame into a North-East-Down frame using the attitude data from the autopilot (onboard IMU and compass). Proper calibration ensures this transformation is optimal and reduces projection error.

</details>

### Orientation

The system needs to know which way the sensor is mounted relative to the flight controller (FC). Take your **right** hand, index finger pointing forward, and align it with the FC, as shown. Usually there will be an arrow on the FC that indicates the forward direction. If your FC is not flat, you'll have to rotate around your index finger to match the orientation of the FC. See the image for reference. This is called the **flight controller reference frame**.

<details>

<summary>Note on reference frames</summary>

This exercise is done using NWU (XYZ => North, West, Up) frames to make it as intuitive as possible. If you're used to the flight controller/Ardupilot reference frame being NED (XYZ ⇒ North, West, Down), don't worry, we will account for this in a later step.

</details>

&#x20;![Flight controller reference frame](/files/spqatc2EWbeEy6twfoWy)

Now we will do the same thing to find the **sensor reference frame**. Align your right hand index finger pointing directly out of the front camera, your middle finger pointing left, and your thumb pointing up at right-angles to the top surface of the sensor. See image for reference. This is the **sensor reference frame**.

&#x20;![](/files/QNgrbDrZeNrXgmmnvdXB)

Now we need to find the sequence of rotations around each of the 3 axes (i.e. index finger, middle finger and thumb) that rotate the sensor reference frame to the flight controller reference frame.

We will label these axes:

* X: index finger
* Y: middle finger
* Z: thumb

If the two reference frames are already aligned, then you have nothing to worry about. If not follow on.

A positive rotation means going counter-clockwise around the axis. If you take your right hand thumb and align it with the axis, curling your fingers represents this positive, counter-clockwise direction.

![](/files/fcR9yQvgJZh7z8mg9o0n)

Let's run through an example. The left image shows the sensor reference frame, and the right shows the FC reference frame. This is a simple scenario - we only have to tilt our hand upwards a bit to get from sensor to FC reference frame, i.e. a rotation around the Y axis.&#x20;

Now, is the rotation negative or positive? If we align our thumb with the Y axis and curl our thumb, a positive rotation (in the direction of our curled fingers) means rotating downwards. So, here we have the opposite, a negative rotation.

![](/files/SrwLkmZ6U5DpnArozTor)![](/files/8OlaSeaFaeEyXTTnUDBw)

How much are we rotating by? We know the mounting for the sensor here is angled by 20 degrees.

So our final rotations, also known as **euler angles**, are: {X: 0, Y: -20, Z: 0}.

For vehicles with more than one axis of rotation, the order matters. Make sure you keep track of the order of rotations as well.

#### Euler angles to rotation matrix

Use Theseus Micro VPS Dashboard to enter the euler angles under and generate the vehicle configuration. In the main dashboard, select "Generate Config" under the Vehicle Parameters widget.

<figure><picture><source srcset="/files/S8wUNyp0kcOUqITmf9u3" media="(prefers-color-scheme: dark)"><img src="/files/mvWFjfyVuWYJTlJiWsLC" alt=""></picture><figcaption></figcaption></figure>

Enter the euler angles from the previous step into the fields in the Vehicle Configuration Generator window.

<figure><picture><source srcset="/files/4hpmpPoATEzAz4A7yGQV" media="(prefers-color-scheme: dark)"><img src="/files/YbJxdmnLCKSlsHoRhLfq" alt=""></picture><figcaption></figcaption></figure>

You can save the config to your computer to upload later, or upload it right away to the drone. Make sure to select the newly created from the configuration files dropdown menu after you upload it.

## Compute Mounting

The compute module offers flat surfaces for adhesive mounting.

{% hint style="success" %}
Sensor and compute modules are mounted on your vehicle and you completed the calibration procedure.
{% endhint %}


# Flight Controller

The Micro VPS requires **one (1) UART port on the flight controller** to communicate with ArduPilot. 4-pin to 6-pin GH-JST serial cables are provided in the box.

For the Cube Orange+ flight controller, this means the **TELEM 1, TELEM 2, and GPS 2 UART ports** are ready to interface with the Micro VPS.

<figure><img src="/files/IFWdFIDJgPjmL9mvb1mF" alt="" width="188"><figcaption></figcaption></figure>

Using the provided serial cables, connect the 4-pin GH-JST end to the Micro VPS compute module and the 6-pin end of the cable to your available UART port on your flight controller.

<figure><img src="/files/UQmWMNGjK9PZ2IByEMUO" alt="" width="563"><figcaption></figcaption></figure>

Navigate to [Autopilot Integration](/micro-vps/autopilot-integration) to see how to configure ArduPilot to connect with the Micro VPS over these UART ports.

{% hint style="success" %}
Serial data cable connects the Micro VPS compute module to your flight controller.&#x20;
{% endhint %}


# Power

The sensor and compute module need to be powered separately.

| Component | Input Voltage                          | Power Draw           |
| --------- | -------------------------------------- | -------------------- |
| Compute   | 2S-6S via XT-30 / 5 VDC via USB-C port | 12 W idle, 15 W peak |
| Sensor    | 5 VDC                                  | 10 W idle, 15 W peak |

{% hint style="warning" %}
**Do not power the compute module via XT-30 and USB-C simultaneously.**
{% endhint %}

The USB-C port on the compute module does not support USB-C Power Delivery. It requires a 5V DC power source.

{% hint style="success" %}
Sensor and compute module have adequate power sources.
{% endhint %}


# Custom Subnet

## Background

If you have a custom subnet address to add to your nanopi connection, follow the commands below. From factory, we create a subnet under "192.168.218.10/24" for connection and all connections are through this IP schema. Instead of changing this for everything you can instead add a secondary IPv4 address to the bridge for custom IP connection.

Please note the addresses in these commands have the xxx present, so you must change the IP address to your specific custom IP address.

```bash
# Create a connection profile for the custom subnet
sudo ip addr add 192.168.xxx.xx/24 dev br0

sudo nmcli connection modify "Bridge0" +ipv4.addresses "192.168.xxx.xx/24"

# Activate it
sudo nmcli device reapply br0
sudo nmcli connection up "Bridge0"
```


# Autopilot Integration

{% hint style="warning" %}
Theseus Micro VPS currently only supports ArduPilot.
{% endhint %}

The Theseus Micro VPS communicates with ArduPilot over MAVLink. Several operations are performed:

* **Synchronize time**: the Micro VPS contains a real-time clock and synchronizes the time on your flight controller.
* **Pull sensor readings**: compass and barometer data are read over MAVLink and used by Micro VPS.
* **Send GPS data**: Micro VPS sends GPS inputs to ArduPilot over MAVLink.

This section goes over each parameter that needs to be configured in [ArduPilot](/micro-vps/autopilot-integration/ardupilot) and how to do so from [Mission Planner](/micro-vps/autopilot-integration/mission-planner) or [QGroundControl](/micro-vps/autopilot-integration/qgroundcontrol).


# MAVLink

[MAVLink](https://mavlink.io/en/) is a communication protocol supported on a variety of UAV systems. The Micro VPS uses MAVLink for two-way communication with the flight controller. This allows us to send MAVLink packets over the same serial link that receives important information from the flight controller. By using MAVLink, we only need one serial connection between the flight controller and the Micro VPS.

Below is a list of the messages we use. If your flight controller does *not* support these message types, or only allows for one-way communication over MAVLink, contact the Theseus team for help integrating on your system. /

## Messages Sent

We send a number of packets aside from just the GPS Input packets.

* [STATUSTEXT (253)](https://mavlink.io/en/messages/common.html#STATUSTEXT)
* [MAV\_CMD\_USER\_2 (31011)](https://mavlink.io/en/messages/common.html#MAV_CMD_USER_2)
  * crosstrack start command
* [GPS\_INPUT (232)](https://mavlink.io/en/messages/common.html#GPS_INPUT)
* [SYSTEM\_TIME (2)](https://mavlink.io/en/messages/common.html#SYSTEM_TIME)
* [TIMESYNC (111)](https://mavlink.io/en/messages/common.html#TIMESYNC)
* [MAV\_CMD\_SET\_MESSAGE\_INTERVAL (511)](https://mavlink.io/en/messages/common.html#MAV_CMD_SET_MESSAGE_INTERVAL)

## Messages Received

* [LOCAL\_POSITION\_NED (32)](https://mavlink.io/en/messages/common.html#LOCAL_POSITION_NED)
* [ATTITUDE\_QUATERNION (31)](https://mavlink.io/en/messages/common.html#ATTITUDE_QUATERNION)
* [GPS\_RAW\_INT (24)](https://mavlink.io/en/messages/common.html#GPS_RAW_INT)
* [NAV\_CONTROLLER\_OUTPUT (62)](https://mavlink.io/en/messages/common.html#NAV_CONTROLLER_OUTPUT)
* [VFR\_HUD (74)](https://mavlink.io/en/messages/common.html#VFR_HUD)
* [SYSTEM\_TIME (2)](https://mavlink.io/en/messages/common.html#SYSTEM_TIME)
* [TIMESYNC (111)](https://mavlink.io/en/messages/common.html#TIMESYNC)
* [EXTENDED\_SYS\_STATE (245)](https://mavlink.io/en/messages/common.html#EXTENDED_SYS_STATE)

## System ID (sysid)

If you want to connect to multiple systems over MAVLink, you'll need to use different sysids for each flight controller. The default sysid is 1 so the Micro VPS automatically looks to connect with sysid 1. If the flight controller has a different sysid than the default, the Micro VPS will report no MAV heartbeat.

To configure your Micro VPS to connect with a flight controller with some other sysid, you'll need to edit the vehicle config. Take a look at [Vehicle Configuration](/gcs-software/legacy-software/theseus-gcs-app/feature-list/vehicle-configuration#changing-the-sysid) for more information.


# ArduPilot

[ArduPilot](https://ardupilot.org/) is an open-source autopilot. Theseus is proud to be an [ArduPilot partner](https://ardupilot.org/ardupilot/docs/common-partners.html).

Theseus Micro VPS does not require you to install any custom version of ArduPilot, Mission Planner, or QGroundControl. Our system works out of the box with existing software.

Theseus **Micro VPS v1.5.3 is tested on ArduPilot v4.6**. We recommend using ArduPilot v4.6+ to guarantee compatibility.

{% hint style="info" %}
Theseus Micro VPS is expected to be compatible with ArduPilot v4.x. Please let us know if you integrate with an older version of ArduPilot.

If using a flight controller with a MCU other than the H7, e.g. F405,  you may need to build custom firmware ([custom.ardupilot.org](https://custom.ardupilot.org)) for your board and **enable MAVlink GPS** under the GPS Drivers drop-down. This is not enabled in the default ArduPilot firmware build on certain boards using MCUs such as the F405.
{% endhint %}

## Parameters

Prior to configuring the ArduPilot parameters to enable communication with the Micro VPS, enable MAVLink on the port you selected when integrating with your flight controller in [Flight Controller](/micro-vps/vehicle-integration/flight-controller).

### UART Port

Set the port protocol to **MAVLink 2** and baud rate to **921600**.

Here is the map between physical and UART ports for the Cube Orange +:

| UART   | Serial   | Physical Port |
| ------ | -------- | ------------- |
| UART 2 | SERIAL 1 | TELEM 1       |
| UART 3 | SERIAL 2 | TELEM 2       |
| UART 8 | SERIAL 4 | GPS 2         |

### ArduPilot System Params

These parameters govern system time synchronization as well as GPS configuration. We disable GPS blending and auto switching.

We choose to set the GPS 2 source to the VPS via MAVLink. This is interchangeable with GPS 1.

| ArduPilot Parameter | Param Value | Description                |
| ------------------- | ----------- | -------------------------- |
| BRD\_RTC\_TYPES     | 2           | Set time from MAVLink      |
| GPS2\_TYPE          | 14          | Set GPS 2 to MAVLink       |
| GPS\_BLEND\_MASK    | 0           | Disable GPS blending       |
| GPS\_AUTO\_SWITCH   | 0           | Disable GPS auto switching |

{% hint style="info" %}
To enable navigation by VPS, **change GPS\_PRIMARY param to "1"** (second GPS) or whichever GPS is designated for the VPS system.
{% endhint %}

### ArduPilot EKF Params

These parameters govern how the ArduPilot EKF integrates sensor measurements to estimate its position. This is one of the key components in ArduPilot's navigation and control mechanisms.

ArduPilot's EKF has three sensor source sets (EK3\_SRC1, EK3\_SRC2, EK3\_SRC3). You can configure each source sets with different sensor inputs. The configuration below sets EK3\_SRC1 to pull its information from the GPS, barometer and compass.

The VPS relies on the barometer to estimate altitude. The altitude it forwards via its GPS signal is copied from the [VFR\_HUD](https://mavlink.io/en/messages/common.html#VFR_HUD) altitude messages it receives. Do not use this GPS POSZ measurement as it can cause undesired feedback loops in the EKF.

The Micro VPS estimates velocity on the XY axis. However, we found through evaluation that setting VEL\_XY to pull from the VPS causes instabilities in the EKF. We recommend to not use the GPS as a velocity source to the EKF.

We recommend enabling continuous calibration of the compass during flight. It is recommend to set EK3\_MAG\_CAL to 3 (in the air) or 4 (always).

{% hint style="warning" %}
Do not pull velocity (VELXY, VELZ), altitude (POSZ), or yaw (YAW) inputs from the GPS when VPS is on.
{% endhint %}

| ArduPilot Parameter | Param Value | Description                                   |
| ------------------- | ----------- | --------------------------------------------- |
| EK3\_SRC\_OPTIONS   | 0           | Disable fusing velocity sources               |
| EK3\_GPS\_CHECK     | 23          | Disable horizontal accuracy checks for arming |
| EK3\_SRC1\_POSXY    | 3           | Pull GPS for xy position input                |
| EK3\_SRC1\_VELXY    | 0           | Disable xy velocity inputs                    |
| EK3\_SRC1\_POSZ     | 1           | Pull altitude from barometer                  |
| EK3\_SRC1\_VELZ     | 0           | Disable z velocity inputs                     |
| EK3\_SRC1\_YAW      | 1           | Pull yaw from compass                         |
| EK3\_MAG\_CAL       | 3           | Enabling mag cal in the air                   |

Go to [Mission Planner](/micro-vps/autopilot-integration/mission-planner) or [QGroundControl](/micro-vps/autopilot-integration/qgroundcontrol) to see how to configure your drone to operate with Micro VPS.


# Mission Planner

This page shows you how to configure your ArduPilot UAV described in [ArduPilot](/micro-vps/autopilot-integration/ardupilot) using Mission Planner. To begin, open Mission Planner and connect to your UAV.

## Configuring Serial Ports

{% hint style="warning" %}
We've noticed mavlink connection issues with slower baud rates. If you use a slower baud rate and encounter issues with mavlink connection, consider switching to a higher baud or contact <support@theseus.us>.
{% endhint %}

In Mission Planner, click on "*SETUP*", then "*Mandatory Hardware*", then "*Serial Ports*". Find the serial port that matches the port you used in [Flight Controller](/micro-vps/vehicle-integration/flight-controller) and configure the **Protocol** to **MAVLink 2** and the **Speed (baud rate)** to **921600**.

<figure><img src="/files/egyo26tIAcEkpDu0qNvo" alt=""><figcaption></figcaption></figure>

Restart your autopilot after setting the serial port.

### Changing the default baud rate

Your flight controller may be limited to a baud rate other than 921600. In this case, you will need to change the baud rate your Micro VPS uses. The Micro VPS uses very little bandwidth and should work with most common baud rates. You can edit the baud rate through the [Theseus GCS App](/gcs-software/legacy-software/theseus-gcs-app) by going to the flight controller settings panel.

<figure><picture><source srcset="/files/pJVTumJ3X3bmvHt1QqfX" media="(prefers-color-scheme: dark)"><img src="/files/huL8xRvEJxtigaMkJM8Z" alt=""></picture><figcaption></figcaption></figure>

You should see a widget at the top that allows you to configure the baud rate. If the widget is missing, you need to update both the GCS App and the VPS to **Version 1.7.7** or higher. Check out [VPS Firmware Updates](/gcs-software/legacy-software/theseus-gcs-app/feature-list/vps-firmware-updates) for more info on updating your system.

This feature only configures the baud rate for the Micro VPS; you need to also configure the baud rate on your flight controller.

## Editing ArduPilot Parameters

Navigate to the "*CONFIG*" tab on main menu, then "*Full Parameter List*", then click on the "*Search*" bar in the menu to the right of the screen.

{% hint style="success" %}
Make sure to click "*Write Params*" to the right of the list before exiting the Full Parameter List!
{% endhint %}

Set **GPS\_AUTO\_SWITCH to 0** and **GPS\_BLEND\_MASK to 0**.

<figure><img src="/files/zoRTQ7okCsuCG2hf3Kse" alt=""><figcaption></figcaption></figure>

Set **GPS2\_TYPE to 14**.&#x20;

<figure><img src="/files/4fwB329Llh4ml95KZAfl" alt=""><figcaption></figcaption></figure>

Set **EK3\_SRC\_OPTIONS to 0**.

<figure><img src="/files/BrkIqWooTq4mJiZBGNo6" alt=""><figcaption></figcaption></figure>

Set **EK3\_MAG\_CAL to 3**.

<figure><img src="/files/fuHJtGdMsX8KG9AIyurt" alt=""><figcaption></figcaption></figure>

Set **BRD\_RTC\_TYPES to 2**.

<figure><img src="/files/qXwW0isiiDalzBrngLSn" alt=""><figcaption></figcaption></figure>

Set **EK3\_GPS\_CHECK to 23**.

<figure><img src="/files/8U2eJYGeHUIZ8EgXj8sV" alt=""><figcaption></figcaption></figure>

Set EK3\_SRC1 param values as shown below.

<figure><img src="/files/6VKt4rarKs1FbQcNoOoy" alt=""><figcaption></figcaption></figure>

Once you have set all your parameters, click the "*Write Params*" button on the right side of the screen before exiting.

{% hint style="success" %}
All parameters have been written to the autopilot from Mission Planner.
{% endhint %}


# QGroundControl

This page shows you how to configure your ArduPilot drone like described in [ArduPilot](/micro-vps/autopilot-integration/ardupilot) using QGroundControl (QGC).

## Configuring Serial Ports

On the main panel on QGC, click on the QGC logo on the top left corner of the screen, then in the "Select Tool" pop up menu, select "Vehicle Setup". At the bottom of the menu on the left side of the screen on the Vehicle Setup page, click on "Parameters". Then, click on the search bar at the top of the screen to search for your parameter.

Search for the serial port which matches the setting you chose in [Flight Controller](/micro-vps/vehicle-integration/flight-controller). In our case, it is serial2.

<figure><img src="/files/yawejM41j6ovFYlvfHlR" alt=""><figcaption></figcaption></figure>

## Editing ArduPilot Params

Set **GPS\_BLEND\_MASK to 0** and **GPS\_AUTO\_SWITCH to 0** (use primary).

<figure><img src="/files/8kNpZ1PZ4AKTatUoWXdT" alt=""><figcaption></figcaption></figure>

Set **GPS2\_TYPE to 14**.

<figure><img src="/files/ho9d9e6E1RnmsrFnccCU" alt=""><figcaption></figcaption></figure>

Set **EK3\_SRC\_OPTIONS to 0**.

<figure><img src="/files/xwziDi2yZ7FPFvUXem7R" alt=""><figcaption></figcaption></figure>

Set **EK3\_MAG\_CAL to 3** (after first climb yaw reset).

<figure><img src="/files/TMrJWfpgbiAfNVA04s1o" alt=""><figcaption></figcaption></figure>

Set EK3\_SRC1 param values.

<figure><img src="/files/uglLA0l7kOwzkFUn0Cvi" alt=""><figcaption></figcaption></figure>

Set **EK3\_GPS\_CHECK to 23**.

<figure><img src="/files/q8RKXXyh4lreXbjjFKik" alt=""><figcaption></figcaption></figure>

Set **BRD\_RTC\_TYPES to 2**.

<figure><img src="/files/W2Erd6Bo7q4wFBSd2JXY" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
All parameters have been written to the autopilot from QGroundControl.
{% endhint %}


# Developer API

The Micro VPS exposes a REST API over HTTP for direct integration with your GCS software.

**Default Base URL:** `http://192.168.218.100:5000`

All responses are JSON. Endpoints return `{"code": "SUCCESS", ...}` on success and `{"code": "ERROR", "message": "..."}` on failure.

***

### Integration Flow

1. `GET /api/ping` — verify the module is reachable
2. `POST /api/upload/maps` — upload a map archive
3. `GET /api/extraction/status` — poll until extraction completes
4. `POST /api/maps/set` — activate the uploaded map
5. `POST /api/home-position` — set the takeoff / starting location

***

### Connectivity

#### Ping

```
GET /api/ping
```

**Response `200`:**

```json
{
  "status": "ok"
}
```

***

### Map Management

#### Upload a Map

```
POST /api/upload/maps
```

Uploads a `.tar.gz` map archive. The file is received synchronously; extraction happens in the background.

**Headers:**

| Header     | Required | Description                                 |
| ---------- | -------- | ------------------------------------------- |
| `filename` | Yes      | URL-encoded filename, must end in `.tar.gz` |
| `checksum` | No       | SHA256 checksum for integrity verification  |

**Body:** Raw binary stream of the `.tar.gz` file.

**Response `200`:**

```json
{
  "code": "SUCCESS",
  "message": "Upload received, extraction started for my-map.tar.gz",
  "size": 104857600,
  "chunks_processed": 12800,
  "checksum": "abc123...",
  "extraction_queued": true,
  "map_name": "my-map"
}
```

After a successful upload, poll `/api/extraction/status` until extraction is complete.

***

#### Get Extraction Status

```
GET /api/extraction/status
```

Poll this endpoint after uploading a map to monitor background extraction progress.

**Response `200`:**

```json
{
  "code": "SUCCESS",
  "data": { ... }
}
```

***

#### List Available Maps

```
GET /api/maps
```

**Response `200`:**

```json
{
  "code": "SUCCESS",
  "data": [
    {
      "name": "my-map",
      "size": 209715200,
      "size_human": "200.0 MB",
      "modified": 1710000000.0,
      "completed": true,
      "checksum": "abc123...",
      "format_version": "1.0.0",
      "min_firmware_version": "1.16.0"
    }
  ]
}
```

`completed` is `false` while extraction is still in progress. A map cannot be activated until `completed` is `true`.

***

#### Get Active Map

```
GET /api/maps/current
```

**Response `200`:**

```json
{
  "code": "SUCCESS",
  "data": {
    "active_map": "my-map"
  }
}
```

Returns `"active_map": null` if no map is selected.

***

#### Set Active Map

```
POST /api/maps/set
Content-Type: application/json
```

**Request body:**

```json
{
  "map_name": "my-map"
}
```

**Response `200`:**

```json
{
  "code": "SUCCESS",
  "data": {
    "active_map": "my-map",
    "message": "Active map set to my-map"
  }
}
```

| Error Code | Reason                          |
| ---------- | ------------------------------- |
| 400        | Missing `map_name`              |
| 400        | Map extraction not yet complete |
| 404        | Map not found                   |

***

#### Delete a Map

```
POST /api/maps/delete
Content-Type: application/json
```

**Request body:**

```json
{
  "map_name": "my-map"
}
```

| Field               | Required | Description                                                |
| ------------------- | -------- | ---------------------------------------------------------- |
| `map_name`          | Yes      | Name of the map to delete                                  |
| `new_active_map`    | No       | If deleting the active map, switch to this map first       |
| `new_home_position` | No       | Home position for the new active map (`lat`, `lon`, `alt`) |

***

### Home Position

#### Get Home Position

```
GET /api/home-position
```

**Response `200`:**

```json
{
  "home_position": {
    "lat": 54.0887,
    "lon": 12.1407,
    "alt": 42.5
  }
}
```

***

#### Set Home Position

```
POST /api/home-position
Content-Type: application/json
```

Sets the starting point for navigation. **A map must be active before setting the home position.**

**Request body:**

```json
{
  "lat": 54.0887,
  "lon": 12.1407,
  "alt": 0
}
```

| Field | Required | Description                                                                                 |
| ----- | -------- | ------------------------------------------------------------------------------------------- |
| `lat` | Yes      | Latitude in degrees (-90 to 90)                                                             |
| `lon` | Yes      | Longitude in degrees (-180 to 180)                                                          |
| `alt` | Yes      | Altitude in meters — automatically replaced by an elevation lookup from the active map data |

**Response `200`:**

```json
{
  "code": "SUCCESS",
  "message": "Home position updated successfully",
  "home_position": {
    "lat": 54.0887,
    "lon": 12.1407,
    "alt": 42.5
  }
}
```

| Error Code | Reason                                        |
| ---------- | --------------------------------------------- |
| 400        | Missing `lat` or `lon`                        |
| 400        | Coordinates not numeric or out of valid range |
| 500        | Elevation lookup failed — is a map loaded?    |


# Map Download

This page describes how to download generated maps from our servers

This document describes how to authenticate, list available maps, and download a map archive using raw HTTP requests against the Supabase backend.

All Supabase requests require the project URL and anon key, referred to as `SUPABASE_URL` and `SUPABASE_ANON_KEY` throughout this document.

{% stepper %}
{% step %}

### Authenticate

#### `POST /auth/v1/token?grant_type=password`

Exchange an email and password for a session containing an access token.

**Headers:**

| Header         | Value               |
| -------------- | ------------------- |
| `apikey`       | `SUPABASE_ANON_KEY` |
| `Content-Type` | `application/json`  |

**Body:**

```json
{
  "email": "pilot@example.com",
  "password": "hunter2"
}
```

**Example:**

```
POST <SUPABASE_URL>/auth/v1/token?grant_type=password
apikey: <SUPABASE_ANON_KEY>
Content-Type: application/json

{
  "email": "pilot@example.com",
  "password": "hunter2"
}
```

**Response 200:**

```json
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 3600,
  "expires_at": 1711324800,
  "refresh_token": "v1.MjQ3...",
  "user": {
    "id": "d0d8c19e-1b2a-4c3d-8e4f-5a6b7c8d9e0f",
    "email": "pilot@example.com",
    "role": "authenticated"
  }
}
```

Save the `access_token` — every subsequent request uses it as a Bearer token.
{% endstep %}

{% step %}

### List Maps

#### `GET /rest/v1/map_requests`

Fetch all non-deleted map requests for the authenticated user, joined with their results. Row-Level Security (RLS) restricts rows to those owned by the caller or shared via an organization.

**Headers:**

| Header          | Value                   |
| --------------- | ----------------------- |
| `apikey`        | `SUPABASE_ANON_KEY`     |
| `Authorization` | `Bearer <access_token>` |
| `Accept`        | `application/json`      |
| `Prefer`        | `count=exact`           |

**Query parameters:**

| Parameter    | Value              | Purpose                              |
| ------------ | ------------------ | ------------------------------------ |
| `select`     | `*,map_results(*)` | Join `map_results` onto each request |
| `deleted_at` | `is.null`          | Exclude soft-deleted maps            |
| `order`      | `created_at.desc`  | Newest first                         |
| `limit`      | `50`               | Page size (optional, default varies) |
| `offset`     | `0`                | Pagination offset (optional)         |

**Example:**

```
GET <SUPABASE_URL>/rest/v1/map_requests?select=*,map_results(*)&deleted_at=is.null&order=created_at.desc&limit=50&offset=0
apikey: <SUPABASE_ANON_KEY>
Authorization: Bearer <access_token>
Accept: application/json
Prefer: count=exact
```

**Response 200:**

The response is a JSON array. Each element is a `map_request` row with a nested `map_results` object (or `null` if the map is still processing).

```json
[
  {
    "id": 42,
    "user_id": "d0d8c19e-1b2a-4c3d-8e4f-5a6b7c8d9e0f",
    "workflow_id": "wf-abc-123",
    "area_name": "Downtown Austin",
    "status": "completed",
    "percent_complete": 100,
    "status_message": null,
    "sw_lat": 30.26,
    "sw_lon": -97.75,
    "ne_lat": 30.28,
    "ne_lon": -97.73,
    "geometry_geojson": null,
    "home_position": { "lat": 30.27, "lon": -97.74, "alt": 0 },
    "created_at": "2026-03-20T14:30:00Z",
    "started_at": "2026-03-20T14:31:00Z",
    "completed_at": "2026-03-20T15:05:00Z",
    "failed_at": null,
    "deleted_at": null,
    "error_message": null,
    "area_km2": 1.25,
    "org_id": null,
    "map_results": {
      "id": 99,
      "request_id": 42,
      "unique_id": "map-1710942300000",
      "download_url": "https://storage.example.com/maps/map-1710942300000_Downtown_Austin.tar.gz",
      "compressed_size_mb": 85.4,
      "compressed_file": "map-1710942300000_Downtown_Austin.tar.gz",
      "upload_path": "/maps/map-1710942300000_Downtown_Austin",
      "checksum": "e3b0c44298fc1c149afbf4c8996fb924...",
      "created_at": "2026-03-20T15:05:00Z"
    }
  },
  {
    "id": 41,
    "workflow_id": "wf-def-456",
    "area_name": "Lake Travis",
    "status": "processing",
    "percent_complete": 63,
    "map_results": null
  }
]
```

**Key fields for download:**

A map is downloadable when `map_results` is **not** `null` and `map_results.download_url` is present. Maps where `map_results` is `null` are still being generated.

| Field                            | Description                                              |
| -------------------------------- | -------------------------------------------------------- |
| `workflow_id`                    | Unique identifier for this map generation job            |
| `area_name`                      | Human-readable name of the mapped area                   |
| `status`                         | `queued`, `started`, `processing`, `completed`, `failed` |
| `map_results.download_url`       | Direct URL to the `.tar.gz` archive                      |
| `map_results.compressed_size_mb` | Archive size in megabytes                                |
| `map_results.checksum`           | SHA-256 hex digest for integrity verification            |
| `map_results.unique_id`          | Unique map identifier used in the archive filename       |

The `content-range` response header contains the total count (e.g., `0-49/73`) when `Prefer: count=exact` is set, useful for pagination.
{% endstep %}

{% step %}

### Get Download Link for a Specific Map

#### `GET /rest/v1/map_requests`

If you already know the `workflow_id`, you can fetch just the download-relevant fields.

**Query parameters:**

| Parameter     | Value                                                             |
| ------------- | ----------------------------------------------------------------- |
| `select`      | `area_name,map_results(download_url,compressed_size_mb,checksum)` |
| `workflow_id` | `eq.<workflow_id>`                                                |
| `deleted_at`  | `is.null`                                                         |

**Example:**

```
GET <SUPABASE_URL>/rest/v1/map_requests?select=area_name,map_results(download_url,compressed_size_mb,checksum)&workflow_id=eq.wf-abc-123&deleted_at=is.null
apikey: <SUPABASE_ANON_KEY>
Authorization: Bearer <access_token>
Accept: application/vnd.pgrst.object+json
```

The `Accept: application/vnd.pgrst.object+json` header tells PostgREST to return a single object instead of an array (equivalent to `.single()` in the JS client).

**Response 200:**

```json
{
  "area_name": "Downtown Austin",
  "map_results": {
    "download_url": "https://storage.example.com/maps/map-1710942300000_Downtown_Austin.tar.gz",
    "compressed_size_mb": 85.4,
    "checksum": "e3b0c44298fc1c149afbf4c8996fb924..."
  }
}
```

{% endstep %}

{% step %}

### Download the Map File

#### `GET <download_url>`

Download the `.tar.gz` archive directly from the URL returned in `map_results.download_url`. This request requires **no authentication** — the URL is publicly accessible.

**Headers:** None required.

**Example:**

```
GET https://storage.example.com/maps/map-1710942300000_Downtown_Austin.tar.gz
```

**Response 200:**

Raw binary stream of the `.tar.gz` file. The `Content-Length` header indicates the total size in bytes.

**Post-download verification:**

If `map_results.checksum` was provided, verify the downloaded file's integrity by computing its SHA-256 hash and comparing:

```bash
sha256sum map-1710942300000_Downtown_Austin.tar.gz
# should match the checksum value from the API response
```

{% endstep %}
{% endstepper %}

## Complete Flow Summary

```mermaid
flowchart TD
    A["POST /auth/v1/token?grant_type=password
    Body: { email, password }
    ← access_token"] --> B

    B["GET /rest/v1/map_requests?select=\*,map_results(*)
    Headers: Authorization: Bearer access_token
    ← Array of map requests with nested results"] --> C

    C{"Find a completed map where
    map_results is not null"}
    C -- "map_results exists" --> D
    C -- "map_results is null" --> W["Map still processing — poll again"]
    W -.-> B

    D["Extract map_results.download_url
    and map_results.checksum"] --> E

    E["GET download_url
    No auth required
    ← .tar.gz binary stream"] --> G

    G["Verify: sha256(file) == map_results.checksum"]
    G -- Match --> H["Done ✓"]
    G -- Mismatch --> I["Re-download or abort"]
```

### Expected filename format

The archive filename follows the pattern:

```
<unique_id>_<area_name>.tar.gz
```

For example: `map-1710942300000_Downtown_Austin.tar.gz`


# Map Generation

This document describes how to submit a map generation job, monitor its progress, and cancel jobs using raw HTTP requests against the Supabase backend.

All requests go through Supabase and require the project URL and anon key, referred to as `SUPABASE_URL` and `SUPABASE_ANON_KEY` throughout this document. Quota enforcement is handled server-side — the submit endpoint rejects requests that exceed the caller's annual km² allowance.

{% stepper %}
{% step %}

#### Authenticate

**`POST /auth/v1/token?grant_type=password`**

Exchange an email and password for a session containing an access token.

**Headers:**

| Header         | Value               |
| -------------- | ------------------- |
| `apikey`       | `SUPABASE_ANON_KEY` |
| `Content-Type` | `application/json`  |

**Body:**

```json
{
  "email": "pilot@example.com",
  "password": "hunter2"
}
```

**Example:**

```
POST <SUPABASE_URL>/auth/v1/token?grant_type=password
apikey: <SUPABASE_ANON_KEY>
Content-Type: application/json

{
  "email": "pilot@example.com",
  "password": "hunter2"
}
```

**Response 200:**

```json
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 3600,
  "expires_at": 1711324800,
  "refresh_token": "v1.MjQ3...",
  "user": {
    "id": "d0d8c19e-1b2a-4c3d-8e4f-5a6b7c8d9e0f",
    "email": "pilot@example.com",
    "role": "authenticated"
  }
}
```

Save the `access_token` — every subsequent request uses it as a Bearer token.
{% endstep %}

{% step %}

#### Submit Map Generation Job

{% hint style="warning" %}
There is a hard per-map size cap of 100,000 km² regardless of map quota usage. If you require a larger map, contact <support@theseus.us>.
{% endhint %}

**`POST /functions/v1/submit-map-job`**

Submit a map generation job. The server validates the caller's quota before accepting the job. If the requested area would exceed the annual km² allowance, the request is rejected with a `403` and a descriptive error. On success, the server records the area and quota year automatically — no separate quota recording step is needed.

**Headers:**

| Header          | Value                   |
| --------------- | ----------------------- |
| `apikey`        | `SUPABASE_ANON_KEY`     |
| `Authorization` | `Bearer <access_token>` |
| `Content-Type`  | `application/json`      |

**Body:**

| Field       | Type      | Required | Description                                         |
| ----------- | --------- | -------- | --------------------------------------------------- |
| `area_name` | `string`  | Yes      | 3–50 chars: lowercase letters, numbers, underscores |
| `geometry`  | `Polygon` | Yes      | GeoJSON polygon of the map area                     |
| `org_id`    | `string`  | No       | Organization ID if generating under an org's quota  |

**Example:**

```
POST <SUPABASE_URL>/functions/v1/submit-map-job
apikey: <SUPABASE_ANON_KEY>
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "area_name": "downtown_austin",
  "geometry": {
    "type": "Polygon",
    "coordinates": [[
      [-97.75, 30.26],
      [-97.73, 30.26],
      [-97.73, 30.28],
      [-97.75, 30.28],
      [-97.75, 30.26]
    ]]
  }
}
```

**Response 200 (job accepted):**

```json
{
  "workflow_id": "wf-abc-123",
  "request_id": 42,
  "status": "queued",
  "message": "Map generation job submitted successfully",
  "area_name": "downtown_austin",
  "area_km2": 1.25,
  "quota_info": {
    "total_quota_km2": 500,
    "used_km2": 120.5,
    "remaining_km2": 378.25,
    "requested_km2": 1.25,
    "after_request_km2": 121.75
  }
}
```

Save the `workflow_id` — it is used for status polling, cancellation, and download.

**Response 403 (quota exceeded):**

```json
{
  "error": "Quota exceeded. You have 0.2 km² remaining, but requested 1.3 km².",
  "quota_info": {
    "total_quota_km2": 500,
    "used_km2": 499.8,
    "remaining_km2": 0.2,
    "requested_km2": 1.3,
    "after_request_km2": 501.1
  }
}
```

**Response 403 (no license):**

```json
{
  "error": "No active license found. Please purchase a license at https://dashboard.theseus.us",
  "quota_info": null
}
```

**Key fields:**

| Field                        | Description                                      |
| ---------------------------- | ------------------------------------------------ |
| `workflow_id`                | Unique identifier for this generation job        |
| `area_km2`                   | Calculated area of the submitted polygon in km²  |
| `quota_info.remaining_km2`   | Remaining quota after this submission            |
| `quota_info.total_quota_km2` | `0` means unlimited — no quota limit is enforced |
| {% endstep %}                |                                                  |

{% step %}

#### Poll Job Status

**`POST /functions/v1/map-workflow-status`**

Poll for real-time progress on a generation job. Returns the current state and percent complete.

**Headers:**

| Header          | Value                   |
| --------------- | ----------------------- |
| `apikey`        | `SUPABASE_ANON_KEY`     |
| `Authorization` | `Bearer <access_token>` |
| `Content-Type`  | `application/json`      |

**Body:**

| Field         | Type     | Required | Description                            |
| ------------- | -------- | -------- | -------------------------------------- |
| `workflow_id` | `string` | Yes      | The `workflow_id` from the submit step |

**Example:**

```
POST <SUPABASE_URL>/functions/v1/map-workflow-status
apikey: <SUPABASE_ANON_KEY>
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "workflow_id": "wf-abc-123"
}
```

**Response 200 (in progress):**

```json
{
  "state": "processing",
  "status": "Generating tiles (batch 3/8)",
  "percent_complete": 42,
  "area_name": "downtown_austin",
  "task_id": "wf-abc-123"
}
```

**Response 200 (completed):**

```json
{
  "state": "completed",
  "status": "Map generation complete",
  "percent_complete": 100,
  "area_name": "downtown_austin",
  "task_id": "wf-abc-123"
}
```

**Response 200 (failed):**

```json
{
  "state": "failed",
  "error": "Satellite imagery unavailable for the requested area",
  "area_name": "downtown_austin",
  "task_id": "wf-abc-123"
}
```

**Key `state` values:**

| State        | Meaning                                       |
| ------------ | --------------------------------------------- |
| `queued`     | Job accepted, waiting for a worker            |
| `started`    | Worker picked up the job                      |
| `processing` | Actively generating tiles                     |
| `completed`  | Generation finished, download available       |
| `failed`     | Generation failed — check `error` for details |
| `cancelled`  | Job was cancelled by the user                 |

**Recommended polling interval:** 5 seconds. Continue polling until `state` is `completed`, `failed`, or `cancelled`.
{% endstep %}

{% step %}

#### Cancel a Job (Optional)

**`POST /functions/v1/cancel-map-workflow`**

Cancel a running or queued map generation job.

**Headers:**

| Header          | Value                   |
| --------------- | ----------------------- |
| `apikey`        | `SUPABASE_ANON_KEY`     |
| `Authorization` | `Bearer <access_token>` |
| `Content-Type`  | `application/json`      |

**Body:**

| Field         | Type     | Required | Description                 |
| ------------- | -------- | -------- | --------------------------- |
| `workflow_id` | `string` | Yes      | The `workflow_id` to cancel |

**Example:**

```
POST <SUPABASE_URL>/functions/v1/cancel-map-workflow
apikey: <SUPABASE_ANON_KEY>
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "workflow_id": "wf-abc-123"
}
```

**Response 200:**

```json
{
  "workflow_id": "wf-abc-123",
  "status": "cancelled",
  "message": "Workflow cancelled successfully",
  "area_name": "downtown_austin"
}
```

{% endstep %}

{% step %}

#### Confirm Completion via Supabase

Once the status endpoint reports `completed`, the map result will also appear in the Supabase `map_requests` table with a populated `map_results` join. This is the same query described in [Map Download — List Maps](/micro-vps/autopilot-integration/developer-api/map-download#list-maps).

Poll the Supabase table or filter by `workflow_id` to confirm the result is available:

**Headers:**

| Header          | Value                               |
| --------------- | ----------------------------------- |
| `apikey`        | `SUPABASE_ANON_KEY`                 |
| `Authorization` | `Bearer <access_token>`             |
| `Accept`        | `application/vnd.pgrst.object+json` |

**Example:**

```
GET <SUPABASE_URL>/rest/v1/map_requests?select=*,map_results(*)&workflow_id=eq.wf-abc-123&deleted_at=is.null
apikey: <SUPABASE_ANON_KEY>
Authorization: Bearer <access_token>
Accept: application/vnd.pgrst.object+json
```

When `map_results` is no longer `null` and `map_results.download_url` is present, the map is ready to download. Follow the [Map Download](/micro-vps/autopilot-integration/developer-api/map-download) flow from there.
{% endstep %}
{% endstepper %}

## Complete Flow Summary

```mermaid
flowchart TD
    A["POST /auth/v1/token?grant_type=password
    Headers: apikey
    Body: { email, password }
    ← access_token"] --> B

    B["POST /functions/v1/submit-map-job
    Headers: apikey, Authorization
    Body: { area_name, geometry, org_id? }
    ← { workflow_id, quota_info }"] --> C

    C{"Response status?"}
    C -- "200 OK" --> D
    C -- "403 Forbidden" --> X["Quota exceeded or no license
    — check error and quota_info"]

    D["POST /functions/v1/map-workflow-status
    Headers: apikey, Authorization
    Body: { workflow_id }
    ← { state, percent_complete }"] --> E

    E{"state?"}
    E -- "completed" --> F
    E -- "processing / queued" --> W["Wait 5s"]
    W -.-> D
    E -- "failed" --> Y["Handle error"]
    E -- "cancelled" --> Z["Job cancelled"]

    F["GET /rest/v1/map_requests
    Headers: apikey, Authorization
    ?workflow_id=eq.workflow_id
    ← map_results.download_url"] --> G

    G["Download .tar.gz archive
    (see Map Download doc)"]
```

## Geometry Format

The `geometry` field must be a valid [GeoJSON Polygon](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6). Coordinates are `[longitude, latitude]` pairs (GeoJSON order). The ring must be closed — the first and last coordinate must be identical.

```json
{
  "type": "Polygon",
  "coordinates": [[
    [-97.75, 30.26],
    [-97.73, 30.26],
    [-97.73, 30.28],
    [-97.75, 30.28],
    [-97.75, 30.26]
  ]]
}
```

Minimum 4 coordinates (3 unique vertices + closing point). The server calculates the polygon area in km² using a spherical trapezoidal approximation.

## Area Name Requirements

| Rule         | Constraint                              |
| ------------ | --------------------------------------- |
| Length       | 3–50 characters                         |
| Characters   | Lowercase letters, numbers, underscores |
| Uniqueness   | Must not match an existing map name     |
| Sanitization | Hyphens are converted to underscores    |


# Mavlink Wall-Clock Sync

This guide walks through configuring the NanoPi to set its system clock from a MAVLink peer's `SYSTEM_TIME` messages. This is useful in systems where there is one device that all of the other device logs should be synced with.

The wall-clock source can be any MAVLink device that sends `SYSTEM_TIME` with a valid epoch timestamp — a flight controller with GPS, a companion computer with NTP, a dedicated time module, etc. The timesync module does not care where the peer gets its time from.

## How It Works

```
Time source (GPS, NTP, etc.) → MAVLink peer → SYSTEM_TIME → SHM → chrony → Pi clock
```

1. Some MAVLink device on the bus has an accurate wall clock (however it obtained it).
2. That device sends `SYSTEM_TIME` messages containing `time_unix_usec`.
3. The timesync module receives those messages and writes valid timestamps into an NTP shared memory segment.
4. Chrony reads that segment as a reference clock and adjusts the Pi's system clock.
5. All other Theseus sensors sync from the Pi via NTP automatically.

## Prerequisites

* The configured MAVLink peer must be sending `SYSTEM_TIME` with a valid epoch `time_unix_usec` (i.e., a real wall-clock time, not zero or boot-relative). Without valid timestamps, chrony falls back to whatever time source it had before.
* If the peer is a flight controller using GPS as its time source, set `BRD_RTC_TYPES=1` on the FCU (reboot required). Do **not** use `2` — it creates a circular time loop where the FCU trusts the Pi's clock, which is trying to trust the FCU's clock.
* `chrony.conf` must include the SHM refclock line. This is included by default on mvps devices, but verify it is present:

```
refclock SHM 0 refid MAV precision 1e-3 delay 0.01 poll 1
```

## Parameters

All parameters live in `mav_timesync_params.yaml` (file ID: `components/timesync/mav_timesync_params`). Four parameters control wall-clock sync:

| Parameter                    | Type   | Default         | Description                                                                                                                                                                                        |
| ---------------------------- | ------ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mav_clock_input`            | bool   | `false`         | Master switch. When `true`, valid `SYSTEM_TIME` messages from the configured source are written into chrony's SHM segment. When `false`, no clock adjustment happens regardless of other settings. |
| `system_clock_source_mode`   | string | `timesync_peer` | Determines which MAVLink peer provides the wall-clock time for chrony. `timesync_peer` reuses the normal TIMESYNC target. `dedicated_peer` uses the explicit sysid/compid below instead.           |
| `system_clock_source_sysid`  | int    | `42`            | MAVLink system ID of the dedicated wall-clock peer. Only used when `system_clock_source_mode` is `dedicated_peer`.                                                                                 |
| `system_clock_source_compid` | int    | `1`             | MAVLink component ID of the dedicated wall-clock peer. Only used when `system_clock_source_mode` is `dedicated_peer`.                                                                              |

### When to use each mode

**`timesync_peer` (default):** The same MAVLink peer used for TIMESYNC round-trip offset estimation also provides `SYSTEM_TIME` for chrony. This is the simpler configuration and works whenever that peer has a valid wall clock.

**`dedicated_peer`:** A different MAVLink device provides the wall-clock time for chrony. Use this when:

* The TIMESYNC peer does not have a reliable wall clock.
* You want to source wall-clock time from a specific device on the MAVLink bus that is not the TIMESYNC target.

In dedicated peer mode, the TIMESYNC peer still handles boot-offset initialization and round-trip time measurement. Only the chrony wall-clock feed is redirected to the dedicated peer.

## Configuration

{% tabs %}
{% tab title="Option A: Helper Script" %}
Start by downloading the below python helper script:

{% file src="/files/AV6mN9MhnjE5bPCXTrT9" %}

The `timesync_clock_source.py` script wraps the Params API so you don't need to remember individual curl commands. It lives at `scripts/device/timesync_clock_source.py` in the repository.

**Check current status:**

```bash
python3 scripts/device/timesync_clock_source.py status
```

Example output:

```
TIMESYNC peer:              1:1
System clock source:        fallback to TIMESYNC peer (1:1)
Configured dedicated peer:  42:1 (ignored in current mode)
clock_source_mode:          timesync_peer
mav_clock_input:            False
Note: restart the consuming service after changing params.
```

**Enable wall-clock sync with the default TIMESYNC peer:**

```bash
python3 scripts/device/timesync_clock_source.py enable-clock-input
```

This sets `mav_clock_input: true` while leaving `system_clock_source_mode: timesync_peer`, so chrony gets its time from the same peer used for TIMESYNC.

**Switch to a dedicated peer:**

```bash
python3 scripts/device/timesync_clock_source.py set --sysid 42 --compid 1
```

This sets `system_clock_source_mode: dedicated_peer` and configures the peer IDs. If `mav_clock_input` is already enabled, the dedicated peer will start feeding chrony on the next service restart.

**Clear the dedicated peer (revert to TIMESYNC peer):**

```bash
python3 scripts/device/timesync_clock_source.py clear
```

**Disable wall-clock sync entirely:**

```bash
python3 scripts/device/timesync_clock_source.py disable-clock-input
```

**Custom API URL** (if the edge API is not at the default `http://192.168.218.100:5050`):

```bash
python3 scripts/device/timesync_clock_source.py --base-url http://192.168.218.100:5050/api/v2/params status
```

{% endtab %}

{% tab title="Option B: curl Commands" %}
All parameters use the Params API at `http://<device>:5050/api/v2/params`. The file ID for timesync is `components/timesync/mav_timesync_params`.

**Read all timesync params:**

```bash
curl http://192.168.218.100:5050/api/v2/params/components/timesync/mav_timesync_params
```

**Enable wall-clock sync:**

```bash
curl -X PUT http://192.168.218.100:5050/api/v2/params/components/timesync/mav_timesync_params/mav_clock_input \
  -H 'Content-Type: application/json' \
  -d '{"value": true}'
```

**Switch to dedicated peer mode with sysid 42, compid 1:**

```bash
# Set the peer IDs first
curl -X PUT http://192.168.218.100:5050/api/v2/params/components/timesync/mav_timesync_params/system_clock_source_sysid \
  -H 'Content-Type: application/json' \
  -d '{"value": 42}'

curl -X PUT http://192.168.218.100:5050/api/v2/params/components/timesync/mav_timesync_params/system_clock_source_compid \
  -H 'Content-Type: application/json' \
  -d '{"value": 1}'

# Then switch the mode
curl -X PUT http://192.168.218.100:5050/api/v2/params/components/timesync/mav_timesync_params/system_clock_source_mode \
  -H 'Content-Type: application/json' \
  -d '{"value": "dedicated_peer"}'
```

**Revert to TIMESYNC peer mode:**

```bash
curl -X PUT http://192.168.218.100:5050/api/v2/params/components/timesync/mav_timesync_params/system_clock_source_mode \
  -H 'Content-Type: application/json' \
  -d '{"value": "timesync_peer"}'
```

**Reset peer IDs to defaults:**

```bash
curl -X DELETE http://192.168.218.100:5050/api/v2/params/components/timesync/mav_timesync_params/system_clock_source_sysid
curl -X DELETE http://192.168.218.100:5050/api/v2/params/components/timesync/mav_timesync_params/system_clock_source_compid
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Parameter changes require restarting the consuming service (Cyclops or MVPS) for the new values to take effect. The process reads its YAML config once at startup.
{% endhint %}

## Verification

After restarting the service, verify that chrony is receiving time from the MAVLink source:

```bash
chronyc sources
```

Look for a line with `#* MAV` — the `*` means chrony has selected it as the active source:

```
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
#* MAV                           0   1   377     1   +125us[ +130us] +/-  500us
```

Verify the system clock is correct:

```bash
date
```

If chrony shows `MAV` but without the `*`, it means chrony sees the source but has not selected it yet (it may still be evaluating samples). Give it 30-60 seconds after the peer starts sending valid timestamps.

## Troubleshooting

**No `MAV` source in `chronyc sources`:**

* Check that `mav_clock_input: true` is set and the service was restarted.
* Verify `refclock SHM 0 refid MAV ...` exists in `/etc/chrony/chrony.conf`.

**`MAV` source present but not selected (`#?` instead of `#*`):**

* The peer may not have a valid wall clock yet. Check `time_unix_usec` in the `SYSTEM_TIME` messages — values near zero mean the peer does not have a time source.
* Chrony may be preferring another source. Check `chronyc sources -v` for competing sources with better stratum.

**`system_time_invalid` events in logs:**

* The configured source is sending `SYSTEM_TIME` with a `time_unix_usec` that is not a valid epoch timestamp (before Feb 2009). This means the peer does not yet have a valid wall clock. The events will stop once the peer starts sending valid time, and a `system_time_recovered` event will be emitted.

**Dedicated peer configured but no clock feed:**

* Verify `system_clock_source_mode` is `dedicated_peer`, not `timesync_peer`. In `timesync_peer` mode the explicit sysid/compid values are ignored.
* Verify the dedicated peer is on the MAVLink bus and sending `SYSTEM_TIME` at the expected sysid:compid.


# State API

The State API provides a read-only snapshot of system information collected by theseus-edge-api. Use it to diagnose whether VPS is blocked by hardware, services, eCAL topics, MAVLink, time sync, or licensing.

**Default State API Base URL:** `http://192.168.218.100:5050`

If the Developer API base URL is `http://192.168.218.100:5000`, use the same host with port `5050` for these v2 state endpoints.

## MicroVPS / VIO State

To check whether MicroVPS VIO is running, use the Developer API:

```
GET http://192.168.218.100:5000/api/vio/status
```

This is the direct VIO status indicator. It returns `state: "running"` when VIO is running, `state: "stopped"` when it is not running, and `state: "not_applicable"` on EKF builds.

The v2 State API does not include a direct VIO initialized/running field. The closest v2 signal is the `vio_transformer/vio` topic in `/api/v2/state/ecal`: `publishing: true` means VIO odometry is flowing; `not_publishing` or `stale` means VIO output is not currently flowing.

All endpoints below are under `/api/v2/state`.

## System State

### Get Complete State

```
GET /api/v2/state
```

Returns one response with all available state groups.

**Response `200`:**

```json
{
  "success": true,
  "data": {
    "hardware": {},
    "services": {},
    "ecal": {},
    "mavlink": {},
    "timesync": {},
    "license": {},
    "flight_log": {},
    "vps_error": {
      "code": 0,
      "message": "System healthy"
    }
  }
}
```

`hardware` is the only required group. Other groups may be `null` or omitted if their monitor is not available.

### Get Hardware State

```
GET /api/v2/state/hardware
```

Returns voltage, CPU temperature, CPU load, memory use, disk use, mounted storage devices, internet connectivity, and camera state.

Useful for diagnosing power, thermal, storage, network, and device-level issues.

### Get Camera State

```
GET /api/v2/state/camera
```

Returns active camera stream status, device path, name, resolution, FPS, pixel format, thermal flag, config match, and health fields.

Useful for diagnosing an unplugged camera, a camera that is not streaming, or a calibration/config mismatch.

### Get eCAL State

```
GET /api/v2/state/ecal
```

Returns eCAL availability plus monitored topic publishing state, message age, staleness threshold, and topic health.

Useful for diagnosing missing or stale data between components.

### Get MAVLink State

```
GET /api/v2/state/mavlink
```

Returns MAVProxy listener status, heartbeat status, autopilot IDs, vehicle type, mode/status fields, heartbeat age/rate, port status, and health fields.

Useful for diagnosing autopilot connection loss, wrong system/component ID, or MAVProxy port issues.

### Get Time Sync State

```
GET /api/v2/state/timesync
```

Returns chrony tracking, chrony sources, MAVLink TIMESYNC convergence, recent warnings, deployed timesync params, waitsync status, and health fields.

Useful for diagnosing unsynchronized clocks, a bad time source, high RTT, invalid FCU time, or timesync startup failure.

### Get Services State

```
GET /api/v2/state/services
```

Returns monitored systemd service state, enabled state, sub-state, result, PID, memory, CPU, restart count, exit code, signal exit flag, health fields, and blockers.

Useful for diagnosing service crashes, clean stops, restart loops, and missing runtime dependencies.

## Additional Fields

The complete state response may also include:

| Field        | Information sent                                                                                                                | Useful for diagnosing                                             |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| `license`    | License validity, product, customer, tier, features, expiration, quota, hardware match, clock sync status, and error/fix fields | License missing, expired, invalid, or bound to different hardware |
| `flight_log` | Flight log status from the edge API state logger                                                                                | Whether state snapshots are being written to the flight log       |
| `vps_error`  | Current VPS error code and user-facing message                                                                                  | Quick HTTP equivalent of the MAVLink `VPS_ERR` health signal      |

Most groups include `health_status`, `health_error`, and `health_fix` when the monitor can provide a direct diagnosis.

## Error Codes

| Error Code | Reason                                                                        |
| ---------- | ----------------------------------------------------------------------------- |
| 503        | State store or requested monitor is not initialized or has no state available |

## curl Examples

```bash
# Get the complete state snapshot
curl http://192.168.218.100:5050/api/v2/state

# Check only camera state
curl http://192.168.218.100:5050/api/v2/state/camera

# Check only MAVLink state
curl http://192.168.218.100:5050/api/v2/state/mavlink

# Check all monitored services
curl http://192.168.218.100:5050/api/v2/state/services
```


# Lockdown API

Use these endpoints on the same device API base URL as `POST /api/lockdown-mode`, for example `http://<device-ip>:5000`.

These endpoints are implemented in the device API:

| Purpose                         | Method | Endpoint                                  |
| ------------------------------- | ------ | ----------------------------------------- |
| Read lockdown state             | `GET`  | `/api/lockdown-mode`                      |
| Enable or disable lockdown mode | `POST` | `/api/lockdown-mode`                      |
| Run lockdown system scan        | `POST` | `/api/lockdown-diagnostics/audit`         |
| Preview log cleanup             | `POST` | `/api/lockdown-diagnostics/clear-preview` |
| Clear plaintext logs and reboot | `POST` | `/api/lockdown-diagnostics/clear`         |
| Encrypt existing plaintext logs | `POST` | `/api/lockdown-diagnostics/encrypt`       |
| Poll diagnostics job status     | `GET`  | `/api/lockdown-diagnostics/job-status`    |

## Recommended Flow

After enabling lockdown mode, the usual flow is:

1. Confirm lockdown mode is enabled.
2. Run the audit scan to see whether plaintext logs or other sensitive artifacts still exist.
3. If the scan reports artifacts that should be removed, run a clear preview.
4. Run the actual clear operation.
5. Poll job status until complete.
6. Confirm the clear result includes `reboot_scheduled: true`.

The diagnostics operations are asynchronous. A `POST` starts a job; `GET /api/lockdown-diagnostics/job-status` returns progress and final results. Only one diagnostics job can run at a time.

## 1. Confirm Lockdown Mode

Why: make sure the device is in lockdown mode before scanning or clearing old plaintext artifacts.

```http
GET /api/lockdown-mode
```

Response:

```json
{
  "code": "SUCCESS",
  "data": true,
  "message": "..."
}
```

`data: true` means lockdown mode is enabled.

## 2. Enable Lockdown Mode

Why: enables encrypted logging behavior and schedules a device reboot when changing from disabled to enabled.

```http
POST /api/lockdown-mode
Content-Type: application/json

{ "enabled": true }
```

Expected response when the value changes from disabled to enabled:

```json
{
  "code": "SUCCESS",
  "message": "Lockdown mode enabled. Device is rebooting.",
  "data": true,
  "service_restarted": true,
  "reboot_scheduled": true
}
```

`reboot_scheduled: true` means the device API successfully scheduled the edge device reboot.

## 3. Run The Audit Scan

Why: scan the device after lockdown mode is enabled to check for remaining plaintext logs or other lockdown-related findings.

```http
POST /api/lockdown-diagnostics/audit
Content-Type: application/json
```

No body is required.

Response:

```json
{
  "code": "SUCCESS",
  "message": "Started lockdown diagnostics audit",
  "data": {
    "action": "audit",
    "in_progress": true,
    "phase": "running",
    "error": null,
    "started_at": "2026-05-30T00:00:00Z",
    "finished_at": null,
    "output_lines": [],
    "result": null
  }
}
```

Then poll job status.

```http
GET /api/lockdown-diagnostics/job-status
```

The audit is finished when `data.in_progress` is `false`. A completed audit result can include `summary`, `findings`, and a human-readable `message`.

Example:

```json
{
  "code": "SUCCESS",
  "data": {
    "action": "audit",
    "in_progress": false,
    "phase": "complete",
    "error": null,
    "started_at": "2026-05-30T00:00:00Z",
    "finished_at": "2026-05-30T00:00:05Z",
    "output_lines": [],
    "result": {
      "message": "Lockdown audit completed",
      "summary": {
        "pass": 10,
        "warn": 1,
        "fail": 0
      },
      "findings": []
    }
  }
}
```

## 4. Preview The Clear Operation

Why: see what the cleanup operation would delete before deleting anything.

```http
POST /api/lockdown-diagnostics/clear-preview
Content-Type: application/json
```

No body is required. This is a dry run.

Poll job status until `data.in_progress` is `false`.

Example completed preview result:

```json
{
  "code": "SUCCESS",
  "data": {
    "action": "clear-preview",
    "in_progress": false,
    "phase": "complete",
    "error": null,
    "started_at": "2026-05-30T00:00:00Z",
    "finished_at": "2026-05-30T00:00:05Z",
    "output_lines": [],
    "result": {
      "message": "Preview found 3 log artifacts",
      "dry_run": true,
      "deleted_count": 3,
      "inspected_count": 25,
      "paths": [
        "/path/to/log-1",
        "/path/to/log-2",
        "/path/to/log-3"
      ],
      "truncated_paths": 0,
      "preview_token": "example-token",
      "preview_expires_at": 1760000000
    }
  }
}
```

`deleted_count` is the number of artifacts that would be removed by the real clear operation.

## 5. Clear Logs After Lockdown

Why: remove old plaintext log artifacts and purge the journal after lockdown is enabled, then reboot the device so it comes back in a clean lockdown state.

```http
POST /api/lockdown-diagnostics/clear
Content-Type: application/json
```

No body is required.

If you want to enforce that the clear matches a recent preview, pass the preview token from `clear-preview`:

```json
{
  "preview_token": "example-token"
}
```

The preview token expires after 5 minutes. If no token is provided, the clear operation still runs.

Starting the clear job returns:

```json
{
  "code": "SUCCESS",
  "message": "Started lockdown diagnostics clear; ecal-rec stopped and device will reboot after cleanup",
  "data": {
    "action": "clear",
    "in_progress": true,
    "phase": "running",
    "error": null,
    "started_at": "2026-05-30T00:00:00Z",
    "finished_at": null,
    "output_lines": [],
    "result": null
  }
}
```

Poll job status until `data.in_progress` is `false`.

Example completed clear result:

```json
{
  "code": "SUCCESS",
  "data": {
    "action": "clear",
    "in_progress": false,
    "phase": "complete",
    "error": null,
    "started_at": "2026-05-30T00:00:00Z",
    "finished_at": "2026-05-30T00:00:05Z",
    "output_lines": [],
    "result": {
      "message": "Deleted 3 log artifacts. Device is rebooting.",
      "deleted_count": 3,
      "inspected_count": 25,
      "reboot_scheduled": true
    }
  }
}
```

`reboot_scheduled: true` means the device API scheduled the reboot after cleanup.

## Encrypt Existing Plaintext Logs

Why: preserve existing plaintext logs by encrypting them instead of deleting them.

```http
POST /api/lockdown-diagnostics/encrypt
Content-Type: application/json
```

No body is required.

Poll job status until complete.

Example completed encrypt result:

```json
{
  "code": "SUCCESS",
  "data": {
    "action": "encrypt",
    "in_progress": false,
    "phase": "complete",
    "error": null,
    "started_at": "2026-05-30T00:00:00Z",
    "finished_at": "2026-05-30T00:00:05Z",
    "output_lines": [],
    "result": {
      "message": "Encrypted 3 plaintext files",
      "files_encrypted": 3
    }
  }
}
```

Use either clear or encrypt depending on whether the goal is to delete old plaintext artifacts or preserve them in encrypted form.


# GPS Switching

Theseus provides a Lua script to enable automatic switching between GPS and VPS.

{% hint style="info" %}
The script has been **tested in a jammed environment**. It is not expected to perform auto switching in a spoofed environment.
{% endhint %}

Micro VPS appears as a GPS to ArduPilot. If you followed our recommended parameter setup, VPS should be configured as GPS 2 and your real GPS as GPS 1 on ArduPilot.

Upload the script below to ArduPilot to enable auto switching:

{% file src="/files/Z6OlZjh0fALR0mqV5yJV" %}

## Jamming Detection

The script employs high level heuristics to determine whether jamming is present on GPS 1:

* **Number of satellites < 16**: fewer satellites indicate likely jamming.
* **HDOP > 160**: high spatial concentration of origin satellites.
* **Horizontal, vertical, speed accuracy**: low accuracy (high values) are also indicators of poor satellite geometry or interference in the signal path.

```
local MIN_NUM_SATS_GPS1 = 16
local MAX_HDOP_GPS1 = 160
local MAX_HORIZONTAL_ACCURACY_GPS1 = 5
local MAX_VERTICAL_ACCURACY_GPS1 = 8
local MAX_SPEED_ACCURACY_GPS1 = 1.8
```

These heuristics have been tested against air defense jammers in Eastern Ukraine.

## Switching Logic

The script will automatically try to use the GPS (GPS 1) if it is available. If GPS is not healthy, it will switch to VPS (GPS 2). If GPS becomes healthy again, the script will switch back to GPS.

If the user manually disables either sources of position, the script will not switch into it. For example, if the user disables VPS, the script will stay on GPS.

If both sources of position are unhealthy and/or manually disabled, the ArduPilot GPS backend is disabled using DISABLE\_GPS\_RC\_OPTION.

## Configuring RC Switches

The script also assumes that two RC switches are configured: one for GPS 1 and one for GPS 2. These RC switches can be used by the operator to manually enable or disable either GPS sources in the Lua script.

Select which channels (RCX) you intend on using for these manual switches (e.g., RC7 and RC11), and set the corresponding option to 300 (GPS 1) and 301 (for GPS 2).

{% hint style="info" %}
You also need to set `SCR_USER1=1` to enable these switches.
{% endhint %}

For example:

* RC7\_OPTION = 300, RC7 controls the GPS 1 switch
* RC11\_OPTION = 301, RC11 controls the GPS 2 switch

If the switch is latched, the corresponding GPS will be deactivated in the Lua script and the source switched to the other GPS.

{% hint style="warning" %}
Note: The Scripting RCx\_OPTION values in ArduPilot are hardcoded within the GPS Switching Lua script. If you are using multiple Lua scripts on the same flight controller, make sure that those scripts do not use Scripting1 or Scripting2 (RCx\_OPTION = 300, 301, respectively).
{% endhint %}


# Validation


# Bench test

This section details how to validate that the Micro VPS is operational before executing test flights. We will initialize the VPS and expect the drone to be in a flight-ready state before&#x20;

At this stage of the integration process you have:

* Physically integrated the Micro VPS on your vehicle [Vehicle Integration](/micro-vps/vehicle-integration)
* Configured ArduPilot for the Micro VPS [Autopilot Integration](/micro-vps/autopilot-integration)

{% hint style="warning" %}
You will need a Windows/Linux laptop with the [Theseus GCS App](/gcs-software/legacy-software/theseus-gcs-app) installed in order to proceed.&#x20;
{% endhint %}

We recommend that you go through the operation guide [GCS Software](/gcs-software/getting-started) to familiarize yourself with using the Micro VPS before proceeding.

## Before Powering On

Ensure that the green Ethernet data cable connects the sensor to the compute. You may plug this cable into either Ethernet port available on the compute module.

<figure><img src="/files/Nx6VoIbSwBXCeU7seqKE" alt="" width="563"><figcaption></figcaption></figure>

Ensure that the flight controller is correctly linked to the compute module following [Flight Controller](/micro-vps/vehicle-integration/flight-controller).

## Booting the Vehicle

Power on your vehicle and the Micro VPS (separately if you chose to power the Micro VPS separately from your main vehicle power).

Verify that the two red LEDs on the Micro VPS compute module turn on as shown below. The sensor has no external indicator of power state, but will become warmer than room temperature after being powered for a few minutes.

<figure><img src="/files/GgY8uOAOkBPcyFVeRnEh" alt="" width="563"><figcaption></figcaption></figure>

## Initialize VPS

Connect your laptop to the compute module using the free Ethernet port. Open the Theseus GCS app on your laptop.

The first step will be to upload maps and set the home position on the Micro VPS. Refer to [Maps Management](/gcs-software/legacy-software/theseus-gcs-app/feature-list/maps-management) and [Home Position Setting](/gcs-software/legacy-software/theseus-gcs-app/feature-list/home-position-setting) for more information on how to conduct these operations.

### Verify Data Connectivity

Verify the MAVLink connection on the compute module by navigating to the "*System Health*" screen[System Health](/gcs-software/legacy-software/theseus-gcs-app/feature-list/system-health).

<figure><img src="/files/APNLTRtPxVYt4qCrkWms" alt=""><figcaption></figcaption></figure>

The "*MAVProxy Service*" indicator should show "*Healthy*" with a green dot. If that is the case, the Micro VPS is connected to the autopilot over MAVLink.

Also check the "*Sensor Connectivity*" indicator under the "*Hardware*" section. This displays the status of the connection to the sensor. It should also show "*Healthy*" with a green dot, indicating that the compute module is connected to the sensor.

### Update Active Maps

On the "*Map Uploads*" screen, ensure the correct map is selected.

<figure><img src="/files/2oBJnk7T3mRhPG27dz8v" alt=""><figcaption></figcaption></figure>

### Update Launch Location

Set the home position to the location where you will be launching your UAV.

<figure><img src="/files/99m3k3nPegafFH1vQ2r2" alt=""><figcaption></figcaption></figure>

### Restart VPS

Return to the main dashboard and restart the VPS by clicking the "*Restart*" button under VPS Control.

<figure><img src="/files/bmvj4YbdtE8TEKEQEHQX" alt=""><figcaption></figcaption></figure>

The dashboard should now show that the system is **Running**.

At this point you can expect:

* Micro VPS to be connected to the autopilot over MAVLink.
* Micro VPS sends GPS data to the autopilot over MAVLink.

{% hint style="success" %}
Micro VPS is running and all required systems are healthy on the Theseus GCS app.
{% endhint %}

## Open GCS

Now that the Micro VPS is properly setup and running, move to the GCS to confirm that ArduPilot is receiving GPS data over MAVLink.

### Set GPS 2 as Primary

The first step is to set GPS 2 as the primary GPS. Similar to the previous procedures[Autopilot Integration](/micro-vps/autopilot-integration), navigate to the "*Config*" tab, then "*Full Parameter List*" and search for \
**GPS\_PRIMARY**.&#x20;

Set **GPS\_PRIMARY to 1** (GPS1 is indexed as 0 and GPS2 is indexed as 1). Then, write the parameters by clicking the "*Write Params*" button.

<figure><img src="/files/CgCIPn6BCSRCkDn4k0dP" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
Micro VPS turns on and appears as a GPS source on the GCS. Moving the vehicle reflects as displacement on the GCS map.
{% endhint %}

Go back to the "*Data*" tab in the top menu on Mission Planner. You should see the VPS as **GPS 2 with a 3D fix on the bottom right corner of the HUD**. The aircraft should be located at the launch location you set above.

<figure><img src="/files/qfjJZr34HE8WZSqO0M8z" alt=""><figcaption></figcaption></figure>

Note that this bench test was performed indoors with no GPS fix. Pick up your UAV (if size permits) and walk around. Check that the track overlaid on the map on Mission Planner matches the movement you applied to the vehicle.

{% hint style="success" %}
GPS 2 shows a 3D fix on Mission Planner and aircraft position matches your displacement.
{% endhint %}


# Flight test

At this point, you should have successfully completed the hardware and software integration of the VPS, as well as a bench test. Theseus recommends running several test flights prior to full operational deployment of the VPS.&#x20;

{% hint style="warning" %}
Contact the Theseus team prior to executing test flights. They will assist and confirm validity at each step of the process.
{% endhint %}

<table><thead><tr><th width="162.84375">Test Flight #</th><th>Configuration</th><th>Purpose</th></tr></thead><tbody><tr><td>Flight 1</td><td>GPS is available and primary position source. VPS acts as GPS 2 and not in the loop.</td><td>Collect VPS recording and ArduPilot logs. Analyze and confirm nominal performance of the system.</td></tr><tr><td>Flight 2-3</td><td>GPS is available as a secondary position source. VPS acts as GPS and is in the loop from take off to landing.</td><td>Confirm nominal performance of the system through multiple live flights. Analyze VPS recordings and ArduPilot logs.</td></tr></tbody></table>


# vns\_bench dev toolkit

## Prerequisites

* Make sure you have python 3.10+ available on your system
* Install [Foxglove Studio](https://foxglove.dev/download)
* Optional: set `FOXGLOVE_STUDIO_BIN=/path/to/foxglove-studio` if the binary is not on your `$PATH`

Install `sshpass` (to connect to the VPS):

```bash
# Install sshpass
sudo apt install sshpass
```

## Quick Start

### Installation

```bash
# For production use
pip install "https://theseus-public-releases.s3.us-west-1.amazonaws.com/vns_bench-0.1.0-py3-none-any.whl"

# Verify installation
vns_bench --help
```

### Setting up Foxglove

Open Foxglove Studio. Download the `default_layout.json` below and [upload it on Foxglove Studio](https://docs.foxglove.dev/docs/visualization/layouts):

* Toggle the `default_layout` drop down on the menu bar
* Choose "Import from file"
* Select the default\_layout.json file and import it to Foxglove

{% file src="/files/oe1r0uOIn7vXaIs2AtPH" %}

Here's what you should see once you configure Foxglove Studio and run the `vns_bench analyze` command on your data:

<figure><img src="/files/xpxTP9dciT5orxZn3hsO" alt=""><figcaption></figcaption></figure>

The tool gives some statistics on how the VPS perfomed during a flight and lets you see the tracks for both the VPS and GPS.

### Analyzing Recordings

The `analyze` command detects and processes flights from your recordings. *Make sure you connect and power the VPS before running this.*

{% hint style="success" %}
**Joe, the command you want is:**

```bash
# Pull specific recording and visualize
vns_bench analyze --remote --folder 76_0af56fa4-b899-4380-b462-f03f213177a5
```

{% endhint %}

> Specify or select a recording when running  `vns_bench analyze`. `vns_bench` automatically calculates metrics for your flight and opens Foxglove for visualization.

#### Available commands

Remote logs are downloaded to your current directory and kept for future reference.

```bash
# Analyze and visualize (default - auto-opens in Foxglove)
vns_bench analyze /path/to/recording/

# Pull from device and auto-visualize (downloads to current directory)
vns_bench analyze --remote

# Analyze without launching Foxglove (just export MCAP)
vns_bench analyze --remote --no-launch

# Skip visualization entirely
vns_bench analyze --remote --no-export

# Analyze all recordings in a directory
vns_bench analyze /path/to/recordings/ --batch

# Output results as JSON
vns_bench analyze /path/to/recording/ --json
```

#### Remote Mode

The `--remote` flag allows pulling logs directly from a NanoPi edge device before analysis:

* Connects via SSH to the device (default: `pi@192.168.218.100`)
* Lists available recordings with sizes
* Downloads to your current directory:
  * Structured logs (`structured_logs/` directory with `.jsonl` files)
  * Telemetry data (`telemetry/` directory with `.jsonl` and `.idx` files)
  * Legacy log files (`.log` files)
  * Metadata files (`.json` files)
  * Excludes measurement data (camera images - too large)
* Automatically uses structured logs if available, or converts legacy logs if needed
* Runs flight detection analysis
* Exports to MCAP and launches Foxglove if flight detected
* Keeps all downloaded files for future reference

Remote options:

* `--host`: SSH host (default: `pi@192.168.218.100`)
* `--password`: SSH password (default: `pi`)
* `--folder`: Specific recording folder to download (if not provided, interactive prompt)
* `--base-dir`: Base directory on device (default: `/mnt/nvme`)

### Metrics within Foxglove

{% hint style="info" %}
Version 1.7 and earlier of Micro VPS may only show limited support for metrics. Please reach out if you are interested in a metric that is not accessible from your data.
{% endhint %}

#### Position

* `/vns/gps_location`, `/vns/map_match_location` - LocationFix (for Map)
* `/vns/gps_pose`, `/vns/map_match_pose` - PoseInFrame (for 3D)
* `/tf` - Frame transforms

#### Events

* `/vns/events` - Diagnostic log messages (WARN, ERROR, INFO) with structured fields

#### Performance

* `/vns/horizontal_error_m` - GPS vs Map Match error (meters)
* `/vns/convergence_m` - Convergence indicator
* `/vns/horizontal_error_rollup`, `/vns/convergence_rollup` - Rolling stats

#### Execution Metrics

* **Timing**: `/vns/execution/map_match_total_ms`, `patch_extract_ms`, `localization_ms`, `map_match_publish_ms`, `frame_prep_ms`
* **Map Reload**: `/vns/execution/map_reload_total_ms`, `map_reload_wait_ms`, `map_reload_grid_ms`
* **System Health**: `/vns/execution/work_queue_depth`, `vio_velocity_mps`, `distance_since_realignment_m`
* **Factor graph**: `/vns/execution/ins_calibration_scale`, `ins_calibration_heading_deg`
* **Counters**: `/vns/execution/odom_skip`, `realignment_blocked`, `map_match_publish`, `waypoint_detected`

#### Grouped Metrics (multi-series plotting)

* `/vns/groups/latencies` - All timing metrics with `metric_name` tag
* `/vns/groups/system_health` - Queue depth, VIO velocity, realignment distance
* `/vns/groups/calibration` - INS scale and heading
* `/vns/groups/map_reload` - Map reload timing breakdown

#### Counter Breakdowns (aggregated tables)

* `/vns/counters/odom_skip_breakdown` - Skip counts by reason
* `/vns/counters/realignment_blocked_breakdown` - Block counts by reason
* `/vns/counters/map_match_publish_breakdown` - Publish counts by status

#### Summary & Debug

* `/vns/summary_table` - Flight metrics (for Table panel)
* `/vns/summary` - Raw JSON summary
* `/vns/debug/nadir`, `/vns/debug/belief_grid` - Debug images

### Troubleshooting

The `mav_manager`  module uses the [EXTENDED\_SYS\_STATE](https://mavlink.io/en/messages/common.html#EXTENDED_SYS_STATE) mav message to determine if the drone is in the air or on the ground. When analyzing logs, vns\_bench looks at the `landed_state` recorded in the mav manager logs to check if the recording is an actual flight. Older versions of ardupilot might not support this message leading to the following output.

```bash
holden@kyobancha:~/Downloads/81_9d$ vns_bench analyze 81_9d9b5ba2-8b78-410b-9673-ffccc24913ed/
ℹ Detected VIO legacy logs, converting...
Parsing global.log...
  Parsed 33 lines
Parsing map_match_gps_fusion.log...
  Parsed 164517 lines
Parsing map_matcher.log...
  Parsed 88015 lines
Parsing vio_transformer.log...
  Parsed 746 lines
Parsing vio_health.log...
  Parsed 3720 lines
Parsing vio_supervisor.log...
  Parsed 84 lines
Parsing mav_manager.log...
  Parsed 138 lines
Parsing scheduler.log...
  Parsed 5 lines
Parsing timesync.log...
  Parsed 8729 lines

Conversion complete:
  Variant: VIO
  Metrics: 65879
  Poses: 37649
  Output: /home/holden/.vns_bench/converted/81_9d9b5ba2-8b78-410b-9673-ffccc24913ed
✔ Converted 65879 metrics, 37649 poses
⚠ No flight detected
```

If you encounter this issue make sure you have a local copy of the logs downloaded, then try running the following command instead:

```bash
vns_bench export /local/path/to/recording/
```

The export command doesn't do the flight detection check and should open up the recording in foxglove.


# Operation


# Pre-Flight Manual

Check that all components are connected to their respective power sources as defined in [Power](/micro-vps/vehicle-integration/power).

Check that the data cable connects the sensor to the compute module.

Ensure the correct vehicle config is selected.

<mark style="color:red;">**INSERT IMAGE**</mark>

Ensure the sensor data is recording. We highly recommend enabling sensor recordings when flying the VPS for debugging any issues with the flight.

<figure><img src="/files/uE7gHeltIwNpaa64PdpE" alt=""><figcaption></figcaption></figure>

Verify the vps service is up and running without errors. This means the map and home position have been correctly set.

<figure><img src="/files/qqU3z0ZpdRVlWiuswLY6" alt=""><figcaption></figcaption></figure>


# Operation Manual

The Micro VPS is a camera-based system that combines inertial and optical data with satellite imagery to deliver drift-free, high-accuracy positioning.

Failure patterns of the Micro VPS differ from a GPS. For example, while it cannot be jammed, the current version of the system cannot operate in dark environments and will fail if all cameras are obstructed.

We outline below a series of potential pitfalls to avoid when using the Micro VPS for a successful flight.

{% hint style="info" %}
If you believe we're missing something below, please let us know!
{% endhint %}

## Initialization

It **can take a minute for the VPS to initialize**. The VPS is waiting for movement to correlate the observed pixel changes from its camera feeds to the measured IMU accelerations and rotations.

During initialization, ensure that Micro VPS is seeing objects or textured elements in its field of view. Gently move the system after powering it on to help with initialization.

{% hint style="info" %}
**Apply some movement to the VPS to help it with initialization.**
{% endhint %}

## Pitfalls

VPS relies on 4 cameras and an inertial measurement unit (IMU) to estimate its position. Below are some patterns to avoid when using Micro VPS on your vehicle.

### Obstructing all cameras

Much like if you place a GPS inside a building where it cannot receive any signal, obfuscating all cameras will make Micro VPS fail because it is not getting any visual data.

{% hint style="danger" %}
When initializing on the vehicle, make sure Micro VPS is clear off the ground and cameras are not obstructed.
{% endhint %}

### Prolonged vertical movement

The Micro VPS relies on its estimation of the size of the world around it. It correlates inertial measurements with pixel movements to determine how much the vehicle moved during a fixed time interval.

When mission planning, avoid large straight vertical climb (>150m). Such movement can cause correlation issues between the inertial and optical measurements, potentially resulting in failure of the VPS.

Instead, follow a stair stepping pattern which breaks up the vertical climb into multiple progressive stages.

{% hint style="info" %}
ArduPilot QuadPlane should handle the climb automatically for VTOL aircrafts.
{% endhint %}

{% hint style="danger" %}
TLDR; don't vertical climb straight for more than 150 m. Break up the climb in several stages.
{% endhint %}

### Prolonged position hold

{% hint style="info" %}
**Theseus is working on a fix** to enable longer position holds on quadcopters.
{% endhint %}

Similar to [#prolonged-vertical-movement](#prolonged-vertical-movement "mention"), flight modes that hold a position limit the relative movement of pixels. The current version of the firmware supports uninterrupted position holds for up to 60 seconds. Any longer position hold risks causing system failure.

Include movement in your flight plan between periods of position holds or alternatively circle around a small radius to mitigate potential failures.

{% hint style="danger" %}
TLDR; on VPS v1.5.3 avoid position hold for more than 60s. Add some movement between periods.&#x20;
{% endhint %}


# Getting Started

Cyclops is a visual positioning system for UAVs operating in GPS-denied environments. It delivers drift-free global position estimates using onboard camera imagery, runs on commercial hardware, works day or night, and integrates directly with ArduPilot.

<figure><img src="/files/foo8y5AoRVbedXkiZYuE" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
The best way to get started with Cyclops is to get a pre-configured hardware unit from us. Reach out to <inbound@theseus.us> to get one.
{% endhint %}

## Before starting

This quick start guide walks you through setting up Cyclops on your drone. By the end you'll be ready to perform bench tests and run your first flight with Cyclops.

**On the drone:**

* ArduPilot v4.5+ with compass, barometer, IMU
  * On fixed-wing, it is preferred to have an airspeed sensor
* Camera (EO or thermal), ≥0.05MP resolution, global shutter is optimal, rolling shutter is ok
* ARM64 edge computer: 2+ cores @ 2.4GHz, 2GB RAM, Ubuntu 22/24
  * Tested: Raspberry Pi 5, NanoPi R6C
* MAVLink connection (UART or UDP) between autopilot and edge device

**On the ground:**

* Host computer running Linux or Windows

{% hint style="info" %}
Learn more about supported hardware [here](/cyclops/pre-requisites#computer-requirements).
{% endhint %}

## Setup steps

The following steps walk you through the software setup process. For information on mechanical installation check out [Vehicle Integration](/cyclops/vehicle-integration).

1. [Create a Theseus account](https://dashboard.theseus.us/signup)
2. [Get Vozilla](/gcs-software/vozilla/installation)
3. [Setup edge device OS with ssh](/cyclops/cyclops-box/pi-5-walkthrough)
4. [Connect your edge device to the flight controller and setup drive](/cyclops/cyclops-box/pi-5-walkthrough/hardware-setup)
5. [Run Cyclops installer](/cyclops/cyclops-box/pi-5-walkthrough/vns-sdk-setup)
6. [Connect to edge device with Vozilla](/gcs-software/vozilla/quick-start#connecting-to-micro-vps)
7. [Configure edge device with Vozilla](#add-your-vehicle)
8. [Calibrate camera](/cyclops/vehicle-integration/camera-calibration)
9. [Generate maps](/gcs-software/legacy-software/vozilla-legacy/maps)
10. [Set ardupilot params](#update-ardupilot-parameters)
11. [Bench test the system](/cyclops/validation/bench-test)

### Add your Vehicle

Cyclops requires the camera pose relative to the flight controller. To ensure this goes smoothly, refer to the [mechanical installation](/cyclops/vehicle-integration/mechanical-installation) guide.&#x20;

Once your camera is mounted to your platform, enter:

* the translation from camera to flight controller, and
* the camera orientation relative to the flight controller

On the vehicle calibration page and upload your vehicle configuration. Cyclops includes a few default configs to help you get started.

<figure><img src="/files/WGeJDbjTrJ861Ukxju3l" alt=""><figcaption></figcaption></figure>

### ArduPilot parameters (AP 4.5+, Cyclops v2+)

Cyclops sends GPS messages over MAVLink to ArduPilot. It requires time synchronization between your autopilot and the edge device. Full setup instructions [here](/cyclops/autopilot-integration).

```
BRD_RTC_TYPES,2
GPS2_TYPE,14
GPS_PRIMARY,0
GPS_BLEND_MASK,0
GPS_AUTO_SWITCH,0
EK3_SRC_OPTIONS,0
EK3_GPS_CHECK,23
EK3_SRC1_POSXY,3
EK3_SRC1_VELXY,0
EK3_SRC1_VELZ,0
EK3_SRC1_POSZ,1
EK3_SRC1_YAW,1
EK3_MAG_CAL,2
EK3_GSF_USE_MASK,0
```

{% hint style="info" %}
For Cyclops v1.x.x, Cyclops sends `MAV_CMD_EXTERNAL_POSITION_ESTIMATE` . See full instructions here to configure ArduPilot for earlier Cyclops versions.
{% endhint %}

Cyclops sends position updates with the `MAV_CMD_EXTERNAL_POSITION_ESTIMATE` command. For ArduPilot to accept these commands, GPS cannot be an EK3 sensor source.

We recommend that you use our EK3 source switching scripts to toggle between dead-reckoning with Cyclops and flying with a GPS during testing. Learn more about ArduPilot configuration [here](/cyclops/autopilot-integration).

### Generate and upload a map

Congrats! You are done with all initial setup steps for Cyclops:

* Vozilla is downloaded and Theseus account is created
* Cyclops is installed on your edge device
* MAVLink ports and storage on edge device
* Cyclops license is activated
* Camera is calibrated

Next, **generate a map** and upload it to Cyclops. Cyclops uses reference maps to estimate your drone's positioning when flying.

Check out [Maps](/gcs-software/legacy-software/vozilla-legacy/maps) for more info on how to complete this step.

### Bench tests and test flights

Before flying, make sure that your home position has been set correctly in Vozilla and that Cyclops is active. Learn more about bench tests [here](/cyclops/validation/bench-test) and flight test [here](/cyclops/validation/flight-test).

## FAQ

#### Does cyclops have MIPI CSI-2 support? — Camera interface

Cyclops doesn't have built in MIPI support, however, we have docs that allow you to create your own custom camera publisher. Check out [Custom Camera Publisher](/cyclops/developers/custom-camera-publisher) for details on implementing a customer camera publisher.

There a corresponding repo here: <https://github.com/Theseus-Dev/cyclops-camera-publisher>

#### **Are there any rolling shutter limitations?**

We have primarily tested on global shutter so far. We should be able to use rolling shutter, as long as the framerate is high. If the image has too much jello that will be a problem, but if the framerate is high and the image quality is good a little jitter is okay. We can support low quality images.

#### **What camera FOV / lens works with cyclops?**

Depends on altitude. We are using anything from 60deg to 100+deg. Any standard FPV camera + lens should be good; whatever is cheapest and most available.

#### **Does cyclops support thermal cameras for night operation?**

We see the same performance between thermal & EO. We have run a caddx eclipse o3 through an analog to digital converter (ADC) which is also 256 pixels and seen good results at 120m altitudes.

#### **What is the operational envelope?**

For the cameras above, expected limits on flight altitude (min/max) and airspeed?

Thermals: We need to test 256 maximum altitude. We are able to fly it at 60 - 120m with our airspace here, but cyclops should be able to go higher. With 640 camera it can go much higher, 700m - 1km should be no problem.

EO: Any 1280x720p EO camera will be good for 60-1000m out of the box.

#### **Module operation without M.2 HAT**

M.2 drives offer much faster read/write speeds, more storage, and more reliability, but at a cost comparable to the cyclops BOM. For that reason micro SD cards have quickly become our preferred method for setting up cyclops.

**Micro SD cards are supported for use with cyclops**, however, they come with an important caveat:

* Smaller SD cards will quickly fill with log data and must be manually cleared to ensure normal operation. [Recording Control](/gcs-software/vozilla/settings/recording-control#clearing-log-data)

## Learn More

1. Check pre-requisites: [Pre-Requisites](/cyclops/pre-requisites)
2. Install Cyclops on your Flight Computer [Cyclops Box](/cyclops/cyclops-box)
3. Mount your Cyclops system to your vehicle: [Vehicle Integration](/cyclops/vehicle-integration)
4. Configure ArduPilot for Cyclops: [Autopilot Integration](/cyclops/autopilot-integration)
5. Configure Cyclops in software: [Vozilla (legacy)](/gcs-software/legacy-software/vozilla-legacy)
6. Perform a bench test: [Bench Test](/cyclops/validation/bench-test)
7. Perform a flight test: [Flight Test](/cyclops/validation/flight-test)


# Pre-Requisites

## UAV Requirements

Cyclops only works with fixed wing platforms that have an airspeed sensor.

The UAV must have a mounting point for your Cyclops camera module that provides an unobstructed view of the terrain below the vehicle.

{% hint style="info" %}
In the future we plan to add support to Cyclops on quads and other non-fixed wing drones, but right now Cyclops cannot be run on such systems.
{% endhint %}

## Power and Data Requirements

You must have power source(s) for your Cyclops system that meet the specs provided by the onboard computer and camera manufacturer. For example, Raspberry Pi 5 accepts 5V DC input power via its GPIO rail.

{% hint style="info" %}
If your camera and flight computer have different power specs, be prepared to power them separately!
{% endhint %}

## Flight Controller Requirements

Theseus has integrated with the following flight controller hardware successfully:

* Cube Orange +

The Cyclops system requires a single UART serial port on the flight controller to run MAVLink telemetry to the compute module. See [Flight Controller](/cyclops/vehicle-integration/flight-controller) for more information on flight controller integration.

Your flight controller must have a barometer (or another altitude source that is not GPS) as well as a reliable compass/magnetometer and airspeed sensor. See [Autopilot Integration](/cyclops/autopilot-integration) for more information on how Cyclops interfaces with ArduPilot.&#x20;

{% hint style="info" %}
Cyclops currently **only supports ArduPilot**. If you would like to integrate other autopilot software, contact our team.
{% endhint %}

## Computer Requirements

| Resource     | Minimum           | Recommended       |
| ------------ | ----------------- | ----------------- |
| Architecture | ARM64             | ARM64             |
| CPU          | 2 cores @ 2.4 GHz | 4 cores @ 2.4 GHz |
| RAM          | 2 GB              | 8 GB              |
| Storage      | 32 GB free        | 256 GB free       |
| OS           | Ubuntu 22.04      | Ubuntu 24.04      |

{% hint style="info" %}
Raspberry pi 5 8GB is the recommended computer to run Cyclops.
{% endhint %}

### SOMs

* Raspberry Pi 5 8GB (recommended):
  * <https://www.amazon.com/Raspberry-Pi-8GB-SC1112-Quad-core/dp/B0CK2FCG1K> (US)
  * <https://prom.ua/p2872354832-mikrokontroler-raspberry-8gb.html> (Ukraine)
* NanoPi R6C 8GB:
  * <https://www.amazon.com/Rockchip-RK3588S-Ethernet-Support-SingleBoard/dp/B0BYRR783B?th=1> (US)

See [Pi 5 Walkthrough](/cyclops/cyclops-box/pi-5-walkthrough) for more information on how to setup your raspberry pi for Cyclops.

{% hint style="warning" %}
If you're using a Jetson Orin, there is a low power mode on the orin that turns off half of the CPU cores. Cyclops uses a library that doesn't account for the turned off cores and will crash. Please ensure the Orin is not in a low power mode.
{% endhint %}

### Active Cooling

It is recommended to use an active cooler on your Raspberry Pi to reduce the chance of thermal throttling.

* <https://a.co/d/04mha9l0> (US)
* <https://prom.ua/ua/m-3513148981465497898-raspberry-active-cooler.html?p=2250872371> (Ukraine)

### Storage

Using a mirco SD card for normal operation is acceptable. You may want to use a pi with an nvme hat. Device boot times are directly correlated to the boot drive speeds, so using an nvme will speed up boot times significantly.

We recommend using an SD card of >128GB to store logging and debug information.

{% hint style="info" %}
A 128GB SD card can store a few hours of flight data before filling up. If you intend on doing much longer flights we recommend more storage.
{% endhint %}

## Camera Requirements

The Cyclops system requires a single wide angle digital video camera for full operation. If you are using an analog video camera (i.e. Runcam or other common FPV cameras), you will need an analog to digital converter. These modules usually have low voltage limitations, so be prepared to power your camera separately in this case.

|            | Minimum           | Recommended      |
| ---------- | ----------------- | ---------------- |
| Resolution | 256 x 192         | 1280 x 800       |
| Type       | EO (daytime-only) | LWIR (nighttime) |
| Interface  | USB               | -                |
| Shutter    | Rolling           | Global           |

{% hint style="warning" %}
We recommend using **fixed-focus** cameras. **Global shutters** are preferred over rolling shutters to minimize motion blur during flight.
{% endhint %}

EO (OV9281 UVC):

* <https://www.amazon.com/Arducam-Distortion-Microphones-Computer-Raspberry/dp/B096M5DKY6> (US)
* <https://prom.ua/p2768422777-usb-kamera-arducam.html> (Ukraine)
* <https://www.arducam.com/arducam-120fps-global-shutter-usb-camera-board-1mp-720p-ov9281-uvc-webcam-module-with-low-distortion-m12-lens-without-microphones-for-computer-laptop-android-device-and-raspberry-pi.html>

LWIR:

* FLIR Boson+ 640
* FLIR Boson 320
* Caddx 256x192
  * <https://prom.ua/ua/p2490791368-teplovizionnaya-fpv-kamera.html> (Ukraine)
  * <https://www.aliexpress.com/item/1005009885355752.html>

{% hint style="info" %}
Cyclops is compatible with EO or thermal cameras; however, if you plan on flying after sunset, a thermal camera is required.
{% endhint %}

## Components

Before proceeding with integration, make sure that you have the following:

* Cyclops system hardware (including camera and flight computer, UART cables, power cables)
* UAV with ArduPilot-compatible flight controller, magnetometer, barometer, and airspeed sensor
* Windows laptop
* Ethernet cable
* Power source for UAV and Cyclops system

{% hint style="success" %}
Check that you have all listed components and proceed to the next section.
{% endhint %}


# Cyclops Box

{% hint style="info" %}
We recommended you procure a pre-configured box from us. Reach out to <inbound@theseus.us> to do so.
{% endhint %}

Cyclops uses a flight computer to process camera data in order to provide a position estimate to the flight controller.&#x20;

Cyclops has successfully integrated with the following flight computers:

* Nano Pi R6C (Ubuntu 22 LTS)
* [Raspberry Pi 5](https://pip-assets.raspberrypi.com/categories/892-raspberry-pi-5/documents/RP-008348-DS-4-raspberry-pi-5-product-brief.pdf?disposition=inline) (Ubuntu 24 LTS)

See [Pi 5 Walkthrough](/cyclops/cyclops-box/pi-5-walkthrough) to get started setting up your Flight Computer


# Pi 5 Walkthrough

This tutorial walks through setting up your Raspberry Pi 5 from scratch

### What you will need

1. [Raspberry Pi 5](https://www.raspberrypi.com/products/raspberry-pi-5/)
2. [Raspberry Pi 5 Active Cooler](https://www.raspberrypi.com/products/active-cooler/)
3. MicroSD Card (128 GB recommended)

#### Step 1: Install Ubuntu 24 on the Pi

We will use the **Raspberry Pi Imager** to flash your microSD card&#x20;

([download for linux](https://downloads.raspberrypi.com/imager/imager_latest_amd64.AppImage) or [download for windows](https://downloads.raspberrypi.com/imager/imager_latest.exe))

We will use the **Desktop build of Ubuntu**, since that has better built in support for external drives. **Ubuntu Server** is fully supported, and recommended for more experienced users.

Set up your Pi:

{% embed url="<https://www.raspberrypi.com/documentation/computers/getting-started.html>" %}

Your Pi should now boot.&#x20;

#### Step 2: SSH into the Pi

Open your terminal and proceed to the ssh setup.

{% content-ref url="/spaces/hUdZltvGJCfBMBzRuwSC/pages/ixLOy10FO5Nv48mewGT2" %}
[SSH Setup](/cyclops/cyclops-box/pi-5-walkthrough/ssh-setup)
{% endcontent-ref %}

You can return to [Getting Started](/cyclops/getting-started#setup-steps) and proceed to step 4 to setup your flight controller.


# SSH Setup

Install the SSH daemon. This must be done in a Terminal window on the Pi.

```bash
sudo apt update && sudo apt install ssh

sudo systemctl start ssh && sudo systemctl enable ssh
```

Next, you will need to [find your IP](https://help.ubuntu.com/stable/ubuntu-help/net-findip.html.en#:~:text=network\)%20IP%20address-,Open%20the%20Activities%20overview%20and%20start%20typing%20Settings.,sidebar%20to%20open%20the%20panel.\&text=Click%20the-,button%20next%20to%20the%20active%20connection,IP%20address%20and%20other%20details.). Avoid using the dynamic `pi.local` address as this uses mDNS which can cause unexpected behavior in other applications.&#x20;

{% hint style="info" %}
Make sure the Pi is connected to your router to get the correct IP address.
{% endhint %}

Verify you can ping the device at its IP address from your windows machine:

1. Add the IP and host name to [/etc/hosts](https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/How-to-edit-the-Ubnutu-hosts-file-and-ping-a-domain-name-locally) or [C:\windows\system32\drivers\etc\hosts](https://kb.parallels.com/129398/) file and ping the host name

```bash
$ ping pi5-tutorial
PING pi5-tutorial (100.64.155.31) 56(84) bytes of data.
64 bytes from pi5-tutorial (100.64.155.31): icmp_seq=1 ttl=64 time=60.4 ms
64 bytes from pi5-tutorial (100.64.155.31): icmp_seq=2 ttl=64 time=6.26 ms
^C
--- pi5-tutorial ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 6.255/33.340/60.426/27.085 ms
```

OR

2. Type out the IP address each time

```bash
$ ping 100.64.155.31
PING 100.64.155.31 (100.64.155.31) 56(84) bytes of data.
64 bytes from 100.64.155.31: icmp_seq=1 ttl=64 time=81.8 ms
^C
--- 100.64.155.31 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 81.796/81.796/81.796/0.000 ms
```

I will use `pi5-tutorial` as the hostname and `pi` as the user for the rest of the tutorial.

Verify you can SSH into the Pi from your windows machine with the command `ssh user@hostname`

```bash
$ ssh pi@pi5-tutorial
The authenticity of host 'pi5-tutorial (100.64.155.31)' can't be established.
ED25519 key fingerprint is SHA256:gaErV4cIsStaQjpiTEljI1Ac2NETlUHMnipmgd6nwC0.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'pi5-tutorial' (ED25519) to the list of known hosts.
pi@pi5-tutorial's password:
Welcome to Ubuntu 24.04 LTS (GNU/Linux 6.8.0-1031-raspi aarch64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

Expanded Security Maintenance for Applications is not enabled.

295 updates can be applied immediately.
119 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

pi@pi5:~$
```

Proceed to step #4 of [Getting Started](/cyclops/getting-started#setup-steps)


# Hardware Setup

**Cyclops requires a drive** to store maps and recordings, a **camera**, and a **connection to the flight controller**. We will walk through each of those and ensure they are properly setup.

For our own systems we store everything on a single boot micro SD card. This helps save on system cost. The connection to the flight controller is a serial connection that uses mavlink to communicate. Minimizing the length of the serial cable can help eliminate noise and mavlink connection issues.

## Setting up an NVME as the storage device

You only need to follow these directions if you plan on using an NVME with cyclops. If you are satisfied the read/write speeds of the SD card, you can skip this step.

{% file src="/files/A9imv78mOM9IUeRuPAaM" %}

This [tutorial on mounting a drive on linux](https://www.wikihow.com/Linux-How-to-Mount-Drive#:~:text=To%20mount%20a%20drive%20on,to%20mount%20and%20unmount%20drives.) will work with a PCIE NVME drive or a USB SSD.\
**OR**\
you can run the above script to setup a USB SSD drive.

You may pick any name for the drive. I am using a Samsung T9 SSD, so I call it `/t9`

```bash
pi@pi5:~$ sudo Downloads/setup-storage-mount.sh /dev/sda2 /mnt/t9
Device: /dev/sda2
UUID: C443-4727
Filesystem: exfat
Mount point: /mnt/t9

Creating systemd mount unit: mnt-t9.mount
Created symlink /etc/systemd/system/multi-user.target.wants/mnt-t9.mount → /etc/systemd/system
/mnt-t9.mount.
Creating udev rule for auto-mount on plug: /etc/udev/rules.d/99-auto-mount-C443-4727.rules
Mounting...

✓ Successfully mounted /dev/sda2 to /mnt/t9
```

Verify that the drive is mounted with:

```bash
lsblk
```

<figure><img src="/files/g3e2tTAYxFnCG9y7fg7p" alt=""><figcaption></figcaption></figure>

## Setup UART Serial

Your device needs to communicate with the flight controller using a serial connection. There are many ways to set this up. You can find docs on the manufacturer's website for your specific hardware setup.

In the case of the nanopi, you can use a serial hat. In the case of the raspi 5, you can look at their provided [docs](https://www.raspberrypi.com/documentation/computers/configuration.html#configure-uarts) on configuring this.

Once you've set up the serial connection, verify you have the serial connections:

```
pi@pi5:~$ ls /dev/tty*
```

### Raspberry Pi 5 Serial Setup (Ubuntu 24)

#### Setup script

To save time, if using a new rpi 5 we recommend using this setup script after flashing the boot sd card. The script will apply some default configurations so you don't need to manually setup the serial port.

On first boot, cloud-init will:

* Set hostname: clop-$DEVICE\_NUM
* Create user 'pi' (password: pi, sudo)
* Enable SSH with password auth
* Set static IP: 192.168.218.100
* Create /home/pi/recordings
* Disable EEE on eth0 (Pi 5 link-flap fix)
* Setup pi serial port
* Reboot to apply boot config changes

{% file src="/files/oZeH01vOyNBLVrz2034s" %}

#### Manual setup of MAVLink Serial

UART0 is used for the MAVLink serial connection between the Pi 5 and the flight controller at 921600 baud.

**Pinout: (**[**Pi 5 pinout diagram**](https://vilros.com/pages/raspberry-pi-5-pinout)**)**

| Function | GPIO | Header Pin | Connects To |
| -------- | ---- | ---------- | ----------- |
| TX       | 14   | Pin 8      | FC RX       |
| RX       | 15   | Pin 10     | FC TX       |
| GND      | —    | Any GND    | FC GND      |

Both the Pi 5 and flight controller operate at 3.3V logic. No level shifting required.

**Background — Pi 5 UART naming:**

On Pi 5, GPIO 14/15 are driven by a PL011 UART in the RP1 southbridge, which shows up at MMIO `0x1f00030000`. The device node it enumerates to depends on the OS image:

* **Ubuntu 24.04 Desktop (Pi 5):** `/dev/ttyAMA0` (with a `/dev/serial0` symlink in some releases)
* **Ubuntu 24.04 Server (recommended for 2GB Pi 5):** `/dev/ttyAMA0`, no `/dev/serial0` symlink
* A second node `/dev/ttyAMA10` (at `0x107d001000`) will also appear — this is an internal SoC UART, **not** GPIO 14/15. Ignore it.

To confirm which node is GPIO 14/15, check `dmesg`:

bash

```bash
sudo dmesg | grep -iE 'pl011|107d001|1f00030'
```

The line containing `1f00030000.serial` identifies the GPIO header UART (typically `ttyAMA0`).

**Setup:**

1. Disable the serial console if active (Desktop images sometimes enable this; Server typically does not):

bash

```bash
    sudo systemctl disable serial-getty@ttyAMA0.service
    sudo systemctl stop serial-getty@ttyAMA0.service
```

2. Remove `console=ttyAMA0,115200` or `console=serial0,115200` from `/boot/firmware/cmdline.txt` if present:

bash

```bash
    sudo nano /boot/firmware/cmdline.txt
```

{% code overflow="wrap" %}

```
The file should be a single line. Remove only the `console=ttyAMA0,...` or `console=serial0,...` token (leave everything else intact). Save with `Ctrl+O`, Enter, `Ctrl+X`.
```

{% endcode %}

3\. Edit `/boot/firmware/config.txt`. Under the `[all]` section, ensure both of these are set:

```bash
    sudo nano /boot/firmware/config.txt
```

```
    enable_uart=1
    dtoverlay=uart0-pi5
```

{% code overflow="wrap" %}

```
Note: on Ubuntu for Pi 5, `enable_uart=1` alone is **not** sufficient — the `uart0-pi5` overlay is required to instantiate the PL011 on GPIO 14/15.
```

{% endcode %}

4\. Reboot. UART is available at `/dev/ttyAMA0`.

**Verification:**

Connect the Pi to the flight controller and ensure the FC's corresponding serial port is configured to output MAVLink2 at 921600 baud. Then:

bash

```bash
sudo stty -F /dev/ttyAMA0 921600 raw -echo
sudo cat /dev/ttyAMA0 | head -c 200 | xxd
```

You should see binary output with recurring `fd` bytes (MAVLink2 start byte) followed by a length byte and incrementing sequence numbers. Example:

```
00000000: fd1c 0000 d901 0121 0000 4704 1200 5d2c  .......!..G...],
00000010: 0000 18ae 0000 6a04 0000 6904 0000 0000  ......j...i.....
```

This confirms MAVLink data is flowing and the serial link is working. `Ctrl+C` to stop.

**Troubleshooting:**

* `cat: /dev/ttyAMA0: No such file or directory` → `uart0-pi5` overlay not loaded. Verify `config.txt` and reboot.
* Only `/dev/ttyAMA10` exists → same cause; the GPIO UART hasn't been instantiated.
* No `0x1f00030000.serial` line in `dmesg` → overlay didn't apply; check for typos in `config.txt`.
* Data flowing but garbled even at correct baud → check TX/RX aren't swapped, and confirm FC is set to MAVLink2 (not MAVLink1, which starts with `fe`).

## MAVLink over udp/tcp

For cyclops versions >=1.20.0, mavlink connection to the flight controller over udp/tcp is supported. The cli tool `vns-config` on the edge device is the only way to configure an ip mavlink connection.

{% code title="On the edge device" %}

```bash
vns-config add mavproxy.device <mavproxy-endpoint>
```

{% endcode %}

Do not provide a baudrate for UDP or TCP endpoints. The endpoint string is passed directly to MAVProxy, so use MAVProxy endpoint syntax for `udp`, `udpin`, `udpout`, `tcp`, `tcpin`, or `tcpout` endpoints:

| Endpoint                    | Use                                                             |
| --------------------------- | --------------------------------------------------------------- |
| `udp:192.168.1.10:14550`    | Use MAVProxy's UDP endpoint mode for `192.168.1.10:14550`       |
| `udpin:0.0.0.0:14550`       | Listen for UDP MAVLink packets on local port `14550`            |
| `udpout:192.168.1.10:14550` | Send UDP MAVLink packets to `192.168.1.10:14550`                |
| `tcp:192.168.1.10:5760`     | Connect to a TCP MAVLink server                                 |
| `tcpin:0.0.0.0:5760`        | Listen for a TCP MAVLink client                                 |
| `tcpout:192.168.1.10:5760`  | Use MAVProxy's TCP output endpoint mode for `192.168.1.10:5760` |

When you add an endpoint that already exists, `vns-config` updates that entry. Adding an existing network endpoint without a baudrate clears any baudrate from that endpoint.

Restart MAVProxy after changing endpoints:

```bash
sudo systemctl restart mavproxy
```

Then verify the active configuration:

```bash
vns-config list mavproxy.devices
journalctl -u mavproxy.service -f
```

### **Example: UDP MAVLink from a Flight Controller**

Configure the NanoPi to listen for MAVLink on UDP port `14550`:

```bash
vns-config add mavproxy.device udpin:0.0.0.0:14550
sudo systemctl restart mavproxy
```

Configure the flight controller or companion network sender to send MAVLink to the NanoPi IP address on port `14550`.

### **Example: TCP MAVLink Server**

Configure MAVProxy to connect to a TCP MAVLink server at `192.168.1.10:5760`:

```bash
vns-config add mavproxy.device tcp:192.168.1.10:5760
sudo systemctl restart mavproxy
```

### **Example: Replace Serial with Ethernet**

Remove the default serial ports and add a UDP network endpoint:

```bash
vns-config remove mavproxy.device /dev/ttyS4
vns-config remove mavproxy.device /dev/ttyS5
vns-config add mavproxy.device udpin:0.0.0.0:14550
sudo systemctl restart mavproxy
```

## USB Port Full Power (GPIO Header Power Input)

When powering the Pi 5 via the GPIO header (pins 2/4 for 5V, bypassing the USB-C PMIC), USB ports default to a 600mA total current budget. This must be overridden to provide full power to USB peripherals.

**Edit `/boot/firmware/config.txt`:**

bash

```bash
sudo nano /boot/firmware/config.txt
```

Add this line under the `[all]` section:

```
usb_max_current_enable=1
```

Save with `Ctrl+O`, Enter, `Ctrl+X`. Reboot to apply:

bash

```bash
sudo reboot
```

This raises the total USB port current budget to 1.6A.

#### Recap

We have now set up all of our hardware.&#x20;

Your addresses may vary, but we have:

```
Recording drive at /mnt/t9
Serial connections at /dev/ttySC0 and /dev/ttySC1
Video device at /dev/video0
```

Great job!


# vns-sdk Setup

In this step, we install Cyclops on the raspberry pi.

Before you start, make sure to have:

* MAVLink device port and baudrate
* Storage device
* Theseus account email and password for license activation

If you're missing any of this you can come back later and edit your configuration.

Open a shell and run the Cyclops install script on your edge device:

```bash
# download and install Cyclops
curl -fsSL https://packages.theseus.us/install.sh | sudo bash
```

> Installs all required Cyclops dependencies. Walks you through setting up MAVLink ports and storage for maps and recordings on your edge device. You will need your Theseus account credentials to activate your Cyclops license.

<figure><img src="/files/isrKuXQtnKb5qMLkookK" alt=""><figcaption></figcaption></figure>

This script installs all Cyclops dependencies as well as data and packages for Cyclops on your device. You will see this if things are working as expected:

```bash
pi@dronepi:~$ curl -fsSL https://packages.theseus.us/install.sh | sudo bash

========================================
  Theseus Cyclops Installation Script
========================================

[OK] Architecture: arm64
[OK] Ubuntu version: 24.04 (Noble)
[INFO] Adding Theseus APT repository...
[INFO] Downloading GPG key...
File '/usr/share/keyrings/theseus.gpg' exists. Overwrite? (y/N) y
[OK] GPG key installed
[INFO] Adding repository to sources.list.d...
[OK] Repository added: /etc/apt/sources.list.d/theseus.list
[INFO] Updating package lists...
[INFO] Installing cyclops and vns-sdk-data (this may take a few minutes)...
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
cyclops is already the newest version (1.12.0+dev.d6faa68).
vns-sdk-data is already the newest version (1.1.0).
0 upgraded, 0 newly installed, 0 to remove and 148 not upgraded.
[OK] Packages installed successfully!

[INFO] Installed version:
Cyclops
==================

Version:          1.12.0+dev.d6faa68
Git Branch:       dev
Git Commit:       d6faa68
Build Timestamp:  2026-01-13 22:54:32 UTC

Build Configuration
-------------------
```

You will then be prompted to configure your device (MAVProxy and storage). Enter **y** to proceed.

```bash
Would you like to configure your device now? (y/N): y

Enter recordings directory path (e.g., /mnt/nvme) [skip]: /mnt/t9
Set paths.recordings_dir = /mnt/t9
[OK] Recordings path set to: /mnt/t9
Enter MAVProxy serial device (e.g., /dev/ttyS4) [skip]: /dev/ttyS4
Enter baud rate [921600]: 
Device /dev/ttyS4 already exists, updating baudrate to 921600
[OK] Added MAVProxy device: /dev/ttyS4 @ 921600
```

{% hint style="warning" %}
Many flight controllers don't support higher baud rates like 921600. Ensure your serial baud rate is supported by the flight controller you use.
{% endhint %}

{% hint style="info" %}
Since we added both serial devices to the configuration, so either port on our serial hat will work for mavlink.
{% endhint %}

{% hint style="warning" %}
If the system clock on your device is skewed, you might have issues running the install script.

```bash
pi@NanoPi-R6C:~$ curl -fsSL https://packages.theseus.us/install.sh | sudo bash
curl: (60) SSL certificate problem: certificate is not yet valid
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
```

Just resync the system clock to fix this issue.

```bash
sudo timedatectl set-ntp true
sudo date -s "2026-02-17 12:00:00"
```

{% endhint %}

Next, the script will prompt you to activate your Cyclops license. You receive two licenses when creating your Theseus account.

```bash
========================================
  License Activation
========================================

License activation requires a Theseus account.
If you don't have one, visit: https://docs.theseus.us

Would you like to activate your license now? (y/N): 
[INFO] Skipping license activation.
[INFO] You can activate later by running:
  cyclops_exe --activate-license

```

> You can come back to activate your license later by running `cyclops_exe --activate-license`.

You should see the following message once you've gone through all the installation steps:

```bash
========================================
  Installation Complete
========================================

[OK] Cyclops has been installed!

Useful commands:
  vns-config show                    # View current configuration
  vns-config set recordings /mnt/ssd # Set recordings directory
  cyclops_exe --license-info         # View license status
  cyclops_exe --activate-license     # Activate license
  sudo systemctl start cyclops       # Start Cyclops service

Documentation: https://docs.theseus.us
Support: support@theseus.us
```

Reboot the device


# Troubleshooting

Open [Vozilla (legacy)](/gcs-software/legacy-software/vozilla-legacy) > [System](/gcs-software/legacy-software/vozilla-legacy/system) and ensure that the device URL matches your device

<figure><img src="/files/Q9iLWkOHSnwUfAfX8wWU" alt=""><figcaption></figcaption></figure>

You will see VPS Service Failed and the following error.&#x20;

<figure><img src="/files/JSudqm37VxX17MnymLa9" alt=""><figcaption><p>This error indicates a map error</p></figcaption></figure>

We need to upload and select a map [Selecting a map](/gcs-software/legacy-software/vozilla-legacy/maps/selecting-a-map)

Restart the VPS&#x20;

You'll see active and VNS-SDK Initialized in the logs.

The service will time out without a MAVLink connection

<figure><img src="/files/a4Mynbvi8doRISVkTa3Q" alt=""><figcaption></figcaption></figure>

I plug in all the devices on my desk to confirm&#x20;

<figure><img src="/files/ipq3wRzxEfYM0JMyFDgk" alt=""><figcaption></figcaption></figure>

View after restarting the VPS and viewing a camera snapshot

<figure><img src="/files/ERA6fbEjzdez0d43apGj" alt=""><figcaption></figcaption></figure>

#### Connecting to the MAV network

<figure><img src="/files/XP6g7aYNP8l7TlxgONBv" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/fSWB4QuNpSv2rdvhtTr0" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/b4f6VPcQdqdLDey1I65X" alt=""><figcaption></figcaption></figure>

#### Debug mavproxy issues

To check mavproxy

```bash
sudo systemctl status mavproxy
```

<figure><img src="/files/3RYW0aXAbyhGBu7PAtlL" alt=""><figcaption></figcaption></figure>

If you see data like this on your mavproxy terminal, check your serial connection wiring

{% embed url="<https://discuss.ardupilot.org/t/garbage-noise-characters-being-generated-over-direct-serial-connection/46371>" %}

{% embed url="<https://discuss.ardupilot.org/t/mavproxy-issue-non-readable-message-after-connexion/45704>" %}


# Pre-Flashed Cyclops Box

Use this setup tutorial to setup your pre-configured Cyclops hardware box.

If you are on this page, you have received a pre-configured cyclops box and an ArduCam USB camera module! The following steps will have you set up everything required to use the VPS on your drone.

## Hardware

The 2 things you have receieved are a pre-flashed cyclops box, with an XT30 connector & BEC and JST GH 6 pin UART connector, and an ArduCam USB camera module.

<figure><img src="/files/v6s2Jer8L9LsF5Jo7bpT" alt="" width="563"><figcaption></figcaption></figure>

## Prerequisites

* A computer running either Windows or Linux
* An Ethernet cable
* An internet connection

## Theseus Account

You should have created a Theseus account by providing one of our team members with an email to use during device hand-off. If you have not set this up yet, contact a team member to get started.

## Download Vozilla Software

We have made set up of the Cyclops device very simple with the Vozilla tool. To get more information on everything within the Vozilla app, refer to the Vozilla page [here](/gcs-software/vozilla).

Once downloaded you will sign in with your Theseus Account.

## Cyclops Setup

Because you have purchased the pre-flashed cyclops box, you have 2 trial licenses to test integration with your UAV. This means that all of the software is pre-flashed, and all you must do is configure a few parts specific for your CONOP.&#x20;

{% hint style="info" %}
We have uploaded a camera calibration with your pre-flashed box which is good for the provided ArduCam module. If you use a different camera, you will need to do your own camera calibration. Steps for calibrating the camera can be found [here](/gcs-software/vozilla/cameras/creating-a-new-camera-calibration).
{% endhint %}

### Step 1 — Power device & connect over Ethernet

Power the Pi by either using the XT30 connector, or opening the case and powering with USB-C. Then connect the Ethernet cable from the Pi to your laptop running Vozilla. The LED on the Pi will change to green when the Pi has booted, and you will see green lights on Ethernet connector while plugged in.

{% hint style="info" %}
It can take up to 45 seconds for the Pi to come online and ready for Ethernet connection. Vozilla will auto-detect and connect to the pi over Ethernet.
{% endhint %}

You can verify the connection by seeing the "Disconnected" message change to "Cyclops Stopped" message in the top right corner of Vozilla.

<figure><img src="/files/js4mTAVCxPD9jAVuulH2" alt=""><figcaption></figcaption></figure>

### Step 2 — Activate License

You will notice the diagnostics tab at the bottom of the screen says to activate your license. The best way to do this is within the Settings -> Device tab, where you can access all of the licenses on your account, and upload a license to Cyclops.

{% hint style="info" %}
We ship these boxes with the username, password, and adress preconfigured. We recommend to leave the pi address as a static IP.

```
Username: pi
Password: pi
Address: 192.168.218.100
```

{% endhint %}

{% hint style="danger" %}
There is a known bug where this feature is not working currently! Please ssh into the pi, default configuration of the pi is the following command:\
`ssh pi@192.168.218.100`

then activate the license with this command:

`cyclops_exe --activate-license`
{% endhint %}

<figure><img src="/files/2mOC1WkeOPyjCx5F1MNB" alt=""><figcaption></figcaption></figure>

### Step 3 — Create a Map

After successful activation of your device license, you will need to add a map. This step can take up to an hour depending on the size of your map, and will require internet to start the map generation and to download the map For an in depth tutorial on map creation and generation, look [here](/gcs-software/vozilla/maps).

{% hint style="warning" %}
It will take about 1 hour to generate a 1000 km^2 map. This will take about 30 minutes to download with a fast internet connection, and even longer if the internet connection is slow or spotty.
{% endhint %}

{% hint style="info" %}
You must have a map large enough for the mission. Cyclops will fail if outside of the map bounds.
{% endhint %}

### Step 4 — Create a Vehicle Config

Now you must create a vehicle configuration based on your camera location relative to your flight controller. For an in depth tutorial on creating a vehicle config, look [here](/gcs-software/vozilla/vehicles/creating-a-new-vehicle).

### Step 5 — Connect to FCU

After the setup, you can connect the provided 6 pin JST GH connector to a serial port on your flight controller. There are a few parameters you must change to ensure the system works properly. For an in depth tutorial on MAVLink and Autopilot setup, look [here](/cyclops/autopilot-integration/ardupilot/cyclops-v2.x.x).

{% hint style="info" %}
The SysID for your FCU must match the SysID on the Pi. Navigate to the MAV settings section in Vozilla to configure the SysID, if your FCU has a SysID other than 1.
{% endhint %}

### Step 6 — Cyclops Options

By default cyclops is shipped with VIO + Map Matching for planes. You can edit the platform and settings within the Vozilla Settings -> Lab.&#x20;

### Step 7 — Ready for Flight

Once you have set up the parameters, have the system connected to the FCU, and everything powered on, you should see the status change from Cyclops Stopped to Cyclops Running.

{% hint style="info" %}
There is a 60s MAVLink heartbeat timeout. If you see a message within the diagnostics tab that says "Failed to find MAVLink heartbeat" refer to the step above to properly configure your FCU.
{% endhint %}

## Determining the Status of Cyclops

We send multiple messages over MAV to GCS software so that determining the status and performance of Cyclops is easy. The main messages to look for are: \
\
`SRC=1/191:CYCLOPS: TS/CLK 1:1 SYNCING`

`SRC=1/191:CYCLOPS: HOME POSITION LOADED`

`SRC=1/191:CYCLOPS: TS/CLK 1:1 SYNCED`

After these messages appear, you can initialize the EKF via a spoof, or your preferred bootstrapping method. For more information, look [here](/cyclops/autopilot-integration/ardupilot/cyclops-v2.x.x/ekf-initialization-via-spoof).

{% hint style="info" %}
Cyclops will initialize at 20m AGL after takeoff. The VIO initial velocity is defaulting to 12 m/s. Both of these can be configured within Vozilla.
{% endhint %}

You can verify it has loaded by seeing the following messages:

`SRC=1/191:CYCLOPS: READY FOR ARMING`

`SRC=1/191:CYCLOPS: VISUAL NAVIGATION ONLINE`

`SRC=1/191:CYCLOPS: MAP MATCH CONV (x), NEXT FIX IN (x)M`

These messages will appear periodically, with the map match messages appearing every 5 seconds. Understanding the meaning behind these messages can be found here. Standard configuration applies map matches when convergence is below 50, and the distance between matches is greater than 500m.

Over the course of the flight, you will see the following message:

`SRC=1/191:CYCLOPS: MAP MATCH APPLIED +(x)M CONV (x)`

This message is self explanatory, but shows when a map match has been applied and corrected the position.

{% hint style="info" %}
Occasionally, you may see EKF LANE SWITCH, DCM ACTIVE, EKF3 ACTIVE messages. This happens when there is a map match that corrects the position over a large distance. This is healthy and means that the position has been corrected.
{% endhint %}

## Troubleshooting

Please contact a Theseus team member if you are having problems with integration, or have any questions. We are happy to help your integration needs as needed.


# Vehicle Integration

Integrating Cyclops into your UAV requires 3 considerations:

1. Properly mounting your Cyclops-compatible camera [Mechanical Installation](/cyclops/vehicle-integration/mechanical-installation)
2. Connecting and configuring Cyclops with your [Flight Controller](/cyclops/vehicle-integration/flight-controller)
3. Providing your camera and flight computer with adequate [Power](/cyclops/vehicle-integration/power)


# Mechanical Installation

There are 2 steps to mounting Cyclops on your aircraft:

1. Properly [#mounting-your-camera](#mounting-your-camera "mention")
2. Calculating your [#mounting-calibration](#mounting-calibration "mention")

## Mounting your camera

Your camera should be mounted under the wings or on the belly of your aircraft. The ideal mounting solution has the camera **parallel to the ground** during normal flight.

Our test flights have demonstrated that Cyclops can tolerate some amount of obstruction, but performance will degrade. Ideally your camera can see **as much of the ground as possible.**

{% hint style="warning" %}
Add vibration isolation to your mounting solution if you are mounting near a source of vibration or on a rigid surface.
{% endhint %}

### Example mounting solutions

{% file src="/files/MWWAKp7kanVKn6LtmX0s" %}

Once you've settled on your mounting solution, proceed to measuring the orientation and translation of the sensor relative to your flight controller.

## Mounting Calibration

Here's a calibration walk through with a camera setup on our internal test VTOL plane (named Songbird).&#x20;

Cyclops uses attitude and relative position data from the flight controller. It is crucial to calibrate the camera relative to the flight controller for Cyclops to perform as expected.

### Measurement steps

1. Measure the **translation** between the camera and the flight controller
   1. Measure the distance on the **X axis (forward/backward)**. The camera is mounted 10 cm **behind** the flight controller.
   2. Measure the distance on the **Y axis (left/right)**. The camera and flight controller are aligned, so we have 0 m of translation.
   3. Measure the distance on the **Z axis (down/up).** The camera is mounted 12 cm **below** the flight controller.
2. Measure the orientation of the camera relative to the flight controller. *Our camera is aligned with the flight controller (pointing straight down, no rotation). We have 0 degrees of pitch, roll or yaw.*

<figure><img src="/files/JLM8xlvmtsoI8P6tXQg2" alt=""><figcaption></figcaption></figure>

### Configuration steps

{% hint style="info" %}
For those using Vozilla 2.0+ refer to [Creating a new vehicle](/gcs-software/vozilla/vehicles/creating-a-new-vehicle) for instructions on how to save your custom vehicle preset.
{% endhint %}

Once I have all my measurements, I use the **Vehicle Calibration** page on Vozilla to generate the calibration file for my vehicle. Our vehicle frame is in North East Down frame.

1. Select the **Custom** preset in the Vehicle Preset drop down. This gives us a blank configuration.
2. Give a name to my vehicle configuration in the Configuration Name box: *Songbird*.
3. Configure the translation:
   1. The camera is 10 cm behind the flight controller, so I **input -0.1 in the X (Forward) box**.
   2. The camera is aligned with the FC, I leave the Y input box at 0.
   3. The camera is 12 cm below the FC, so I **input 0.12 in the Z (Down) box.**
4. Configure the rotation. *We don't have any rotation, so I leave all the rotation fields at zero.*
5. Visually inspect the position of the VPS (green) relative to the flight controller (red). The translation and rotation should match with your setup on your vehicle.
6. Once everything looks correct, **click the Save to Cloud button**.

{% hint style="info" %}
You need to save the vehicle configuration. If your vehicle is connected to Vozilla, the configuration will be uploaded automatically.&#x20;
{% endhint %}

<figure><img src="/files/kbt9fllRMdMDJZ4PDh6N" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
Before flying, verify that the correct vehicle configuration is selected in the dropdown menu.

<p align="center"><img src="/files/mZzx77MM66b9hWXSEfKx" alt=""></p>
{% endhint %}




---

[Next Page](/llms-full.txt/1)

