From 19d4bcecfbb3a6f037265089973801fc2fe9c5f6 Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Tue, 7 May 2024 15:54:32 +0100 Subject: [PATCH 01/30] Add vitepress docs --- .dockerignore | 6 + .gitignore | 6 + docs/vitepress/docs/.vitepress/config.mts | 83 + docs/vitepress/docs/LICENSE.md | 15 + docs/vitepress/docs/README.md | 74 + docs/vitepress/docs/admin-portal.md | 18 + docs/vitepress/docs/autopilot-config.md | 0 docs/vitepress/docs/deploy-for-development.md | 71 + .../docs/deploy-with-docker-compose.md | 104 + docs/vitepress/docs/glossary.md | 691 ++++ docs/vitepress/docs/index.md | 54 + docs/vitepress/docs/install-locally.md | 133 + docs/vitepress/docs/jetson-pixhawk.md | 154 + docs/vitepress/docs/markdown-examples.md | 91 + .../docs/public/gisnav_hil_fmuk66-e_setup.jpg | Bin 0 -> 78150 bytes docs/vitepress/docs/remap-ros-topics.md | 54 + docs/vitepress/docs/ros-run-node.md | 34 + docs/vitepress/docs/setup-gis-server.md | 144 + .../docs/shared/build-colcon-workspace.md | 22 + .../docs/shared/clone-to-colcon-workspace.md | 7 + .../docs/shared/compose-project-name.md | 17 + .../docs/shared/create-colcon-workspace.md | 5 + ...e-and-nvidia-container-toolkit-required.md | 4 + docs/vitepress/docs/shared/expose-x-server.md | 20 + .../docs/shared/require-install-locally.md | 1 + .../docs/shared/source-colcon-workspace.md | 22 + .../shared/warning-simulation-use-only.md | 3 + docs/vitepress/docs/system-architecture.md | 164 + docs/vitepress/docs/test-gisnav.md | 138 + docs/vitepress/docs/troubleshooting.md | 145 + docs/vitepress/package-lock.json | 3015 +++++++++++++++++ docs/vitepress/package.json | 12 + 32 files changed, 5307 insertions(+) create mode 100644 docs/vitepress/docs/.vitepress/config.mts create mode 100644 docs/vitepress/docs/LICENSE.md create mode 100644 docs/vitepress/docs/README.md create mode 100644 docs/vitepress/docs/admin-portal.md create mode 100644 docs/vitepress/docs/autopilot-config.md create mode 100644 docs/vitepress/docs/deploy-for-development.md create mode 100644 docs/vitepress/docs/deploy-with-docker-compose.md create mode 100644 docs/vitepress/docs/glossary.md create mode 100644 docs/vitepress/docs/index.md create mode 100644 docs/vitepress/docs/install-locally.md create mode 100644 docs/vitepress/docs/jetson-pixhawk.md create mode 100644 docs/vitepress/docs/markdown-examples.md create mode 100644 docs/vitepress/docs/public/gisnav_hil_fmuk66-e_setup.jpg create mode 100644 docs/vitepress/docs/remap-ros-topics.md create mode 100644 docs/vitepress/docs/ros-run-node.md create mode 100644 docs/vitepress/docs/setup-gis-server.md create mode 100644 docs/vitepress/docs/shared/build-colcon-workspace.md create mode 100644 docs/vitepress/docs/shared/clone-to-colcon-workspace.md create mode 100644 docs/vitepress/docs/shared/compose-project-name.md create mode 100644 docs/vitepress/docs/shared/create-colcon-workspace.md create mode 100644 docs/vitepress/docs/shared/docker-compose-and-nvidia-container-toolkit-required.md create mode 100644 docs/vitepress/docs/shared/expose-x-server.md create mode 100644 docs/vitepress/docs/shared/require-install-locally.md create mode 100644 docs/vitepress/docs/shared/source-colcon-workspace.md create mode 100644 docs/vitepress/docs/shared/warning-simulation-use-only.md create mode 100644 docs/vitepress/docs/system-architecture.md create mode 100644 docs/vitepress/docs/test-gisnav.md create mode 100644 docs/vitepress/docs/troubleshooting.md create mode 100644 docs/vitepress/package-lock.json create mode 100644 docs/vitepress/package.json diff --git a/.dockerignore b/.dockerignore index 2cba14a2..67d13c02 100644 --- a/.dockerignore +++ b/.dockerignore @@ -31,3 +31,9 @@ gisnav/test/sitl/ulog_analysis/.ipynb_checkpoints/ # mypy .mypy_cache + +# node +node_modules + +# vitepress +docs/vitepress/docs/.vitepress/cache/ diff --git a/.gitignore b/.gitignore index 9b51fa5c..1105f95c 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,9 @@ debian # mypy .mypy_cache + +# node +node_modules + +# vitepress +docs/vitepress/docs/.vitepress/cache/ diff --git a/docs/vitepress/docs/.vitepress/config.mts b/docs/vitepress/docs/.vitepress/config.mts new file mode 100644 index 00000000..89f113ae --- /dev/null +++ b/docs/vitepress/docs/.vitepress/config.mts @@ -0,0 +1,83 @@ +//import { defineConfig } from 'vitepress' +import { withMermaid } from 'vitepress-plugin-mermaid' + +// https://vitepress.dev/reference/site-config +//export default defineConfig({ +export default withMermaid({ + title: "GISNav", + description: "GISNav documentation", + lastUpdated: true, + cleanUrls: true, + metaChunk: true, + themeConfig: { + // https://vitepress.dev/reference/default-theme-config + nav: [ + { text: 'Home', link: '/' }, + { text: 'Guide', link: '/README' }, + { text: 'API Reference', link: '/api-reference' } + ], + outline: { + level: 'deep' + }, + sidebar: [ + { + text: 'SITL simulation', + items: [ + { text: 'Run mock GPS demo', link: '/README' }, + { text: 'Deploy with Docker Compose', link: '/deploy-with-docker-compose' }, + ] + }, + { + text: 'HIL simulation', + items: [ + { text: 'Jetson & Pixhawk', link: '/jetson-pixhawk' } + ] + }, + { + text: 'Configuration', + items: [ + { text: 'Admin portal', link: '/admin-portal' }, + { text: 'Setup GIS server', link: '/setup-gis-server' }, + ] + }, + { + text: 'Development', + items: [ + { text: 'Install locally', link: '/install-locally' }, + { text: 'Deploy for development', link: '/deploy-for-development' }, + { text: 'System architecture', link: '/system-architecture' }, + { text: 'Run ROS nodes', link: '/ros-run-node' }, + { text: 'Remap ROS topics', link: '/remap-ros-topics' }, + { text: 'Run tests', link: '/test-gisnav' }, + ] + }, + { + items: [ + { text: 'Troubleshooting', link: '/troubleshooting' }, + { text: 'Glossary', link: '/glossary' }, + { text: 'License', link: '/LICENSE' }, + ] + } + ], + + socialLinks: [ + { icon: 'github', link: 'https://github.com/hmakelin/gisnav' } + ], + + // logo: { src: '/assets/gisnav-website-favicon-color.png', width: 24, height: 24 }, + + search: { + provider: 'local' + }, + + editLink: { + text: 'Edit this page on GitHub', + pattern: 'https://github.com/hmakelin/gisnav/edit/main/docs/:path' + }, + + footer: { + message: 'Released under the MIT License.', + copyright: 'Copyright © 2022 Harri Makelin' + }, + }, +}) diff --git a/docs/vitepress/docs/LICENSE.md b/docs/vitepress/docs/LICENSE.md new file mode 100644 index 00000000..aeff5050 --- /dev/null +++ b/docs/vitepress/docs/LICENSE.md @@ -0,0 +1,15 @@ +# License + +The GISNav software including this documentation is provided under the MIT +license. Please see the text below for details. + +::: info MIT License +Copyright (c) 2022 Harri Mäkelin + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +::: diff --git a/docs/vitepress/docs/README.md b/docs/vitepress/docs/README.md new file mode 100644 index 00000000..c681bce5 --- /dev/null +++ b/docs/vitepress/docs/README.md @@ -0,0 +1,74 @@ +# Run mock GPS demo + + + + + +## Introduction + +GISNav is a ROS 2 package that accurately determines UAV global position by aligning real-time video with maps from an onboard GIS server. + +GISNav provides a precise global position by visually comparing frames from the vehicle's nadir-facing camera to a map of the UAVs approximate global position retrieved from an onboard GIS server. + +## Example walkthrough + +The below steps demonstrate how GISNav enables GNSS-free flight with PX4 Autopilot's [Mission mode][1] in a SITL simulation. + +[1]: https://docs.px4.io/main/en/flight_modes/mission.html + +### Prerequisites + +You will need to have the [Docker Compose plugin][2] and [NVIDIA Container Toolkit][3] installed. + +[2]: https://docs.docker.com/compose/install/linux/ +[3]: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html + +### Build and run SITL simulation + +Build the Docker images and create and run the containers (downloading and building everything will take a long time): + +> [!WARNING] Warning: Exposed X server +> This script will expose your X server to the Docker containers to make GUI > applications work. + +```bash +git clone https://github.com/hmakelin/gisnav.git +cd gisnav +make -C docker demo +``` + +### Upload flight plan via QGroundControl + +Once both the Gazebo and QGroundControl windows have appeared (QGroundControl should show the vehicle location near San Carlos airport), use QGroundControl to upload the sample `~/ksql_airport_px4.plan` flight plan that is included inside the Docker container, and then start the mission. + +### Simulate GPS failure + +After a while and once the vehicle has gained some altitude you should see a visualization of the GISNav-estimated field of view projected on the ground appear. You can then try disabling GPS through your [MAVLink or NSH shell][4] (accessible e.g. through QGroundControl > Analyze Tools > MAVLink Console): + +```nsh +failure gps off +``` + +The vehicle should now continue to complete its mission GNSS-free with GISNav substituting for GPS. + +You can check if PX4 is receiving the mock GPS position estimates by typing the following in the MAVLink shell: + +```nsh +listener sensor_gps +``` + +[4]: https://docs.px4.io/main/en/debug/mavlink_shell.html#qgroundcontrol + +## Documentation + +See the [GISNav home page][5] for user and developer guides and API reference. + +[5]: https://hmakelin.github.io/gisnav + +## License + +This software is released under the MIT license. See the [LICENSE.md][6] file for more information. + +[6]: https://github.com/hmakelin/gisnav/blob/master/LICENSE.md diff --git a/docs/vitepress/docs/admin-portal.md b/docs/vitepress/docs/admin-portal.md new file mode 100644 index 00000000..e7172877 --- /dev/null +++ b/docs/vitepress/docs/admin-portal.md @@ -0,0 +1,18 @@ +# Admin portal + +::: warning Warning: Experimental feature +The admin portal is very much untested and is intended for exploring how to provide ways of managing configuration parameters and onboard maps without coding or GIS expertise. + +::: + +GISNav includes a captive or self-hosted Homepage admin portal with links to relevant resources and a FileGator file server for editing configuration files without need for programming knowledge or commandline work. + +## Launch home page + +The following command should launch the home page and any required supporting services: + +``` +docker compose -p gisnav up homepage +``` + +You should then find the homepage at `localhost:3000`. diff --git a/docs/vitepress/docs/autopilot-config.md b/docs/vitepress/docs/autopilot-config.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/vitepress/docs/deploy-for-development.md b/docs/vitepress/docs/deploy-for-development.md new file mode 100644 index 00000000..2fe472fb --- /dev/null +++ b/docs/vitepress/docs/deploy-for-development.md @@ -0,0 +1,71 @@ +# Deploy for development + +The recommended way of developing GISNav is to deploy a SITL simulation and supporting services using [Docker Compose](/deploy-with-docker-compose) while deploying the GISNav ROS 2 launch configuration directly on the host. With a local non-containerized installation of GISNav it is easier re-test the software without having to rebuild a Docker image and re-deploy a container every time changes are made to the source code. + +## Prerequisites + + + +## Deploy via Makefile + +The easy way to deploy for development is via the Makefile. Use the below commands to run a local ROS 2 default launch configuration of GISNav and to deploy a supporting SITL simulation as Docker Compose services. + +```bash +cd ~/colcon_ws/src/gisnav +make -C docker dev +``` + +::: info Prompt for `sudo` access +The Docker Compose service containers are hard-coded to use their Docker DNS hostnames when communicating with each other over Docker bridge networks. The Makefile `dev` recipe will add some of these hostnames to the `/etc/hosts` file on your Docker host i.e. the development computer so that your local GISNav installation will be able to any containers it wants to talk to on the host network. You may therefore be prompted for `sudo` access when running the above command. + +Read more about the [service architecture](/system-architecture#service-architecture) for more context. + +::: + +::: tip Ctrl-C and relaunch +You can use `Ctrl-C` and then `make dev` again to kill and relaunch GISNav without killing the supporting services. This enables iterating with quick changes to the source code without having to restart the simulation. You will still have to `colcon build --packages-select gisnav` in your workspace after making changes to the GISNav source code before relaunching with `make dev`. + +::: + +## Deploy via ROS launch system + +The Makefile uses the ROS launch system under the hood to define and deploy configurations of multiple nodes with preconfigured parameters and optional launch arguments. + +### Default configuration + +The `default.launch.py` file can be used for all launches. A `dev.launch.py` configuration is provided to include additional nodes that help with development. + +::: info Todo +Merge `dev.launch.py` into `default.launch.py` by adding more launch arguments to `default.launch.py` that invoke current `dev.launch.py` functionality + +::: + +### Edit local hosts file + +TODO + +### Redirecting serial output for SITL simulation + +The `px4` SITL simulation container listens for NMEA messages at TCP port 15000. GISNav `NMEANode` writes into a serial port, and this serial port traffic must be bridged to the TCP port of the running SITl container to make the mock GPS demo work in SITL simulation. This is done using `socat` which creates a new virtual serial port for you (a pseudo-tty): + +The Makefile `dev` recipe looks up the IP addresses of the containers of the supporting services and creates a pseudo-tty (virtual serial port) using `socat` to bridge the GISNav serial port output via TCP to the Docker container running the PX4 SITL simulation. + +```bash +PX4_IP=`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' gisnav-px4-1` +socat pty,link=/tmp/gisnav-pty-link,raw,echo=0 tcp:$(PX4_IP):15000 +``` + +Assuming you have already installed the `gisnav` colcon package, you can launch with a single command. You need to provide a serial port and an optional baudrate as launch args to the default launch configuration. + +::: code-group +```bash [uORB ] +cd ~/colcon_ws +ros2 launch gisnav default.launch.py protocol:=uorb +``` + +```bash [NMEA] +cd ~/colcon_ws +ros2 launch gisnav default.launch.py protocol:=nmea port:=${PTY_PORT} baudrate:=${BAUDRATE:-9600} +``` + +::: diff --git a/docs/vitepress/docs/deploy-with-docker-compose.md b/docs/vitepress/docs/deploy-with-docker-compose.md new file mode 100644 index 00000000..50063f09 --- /dev/null +++ b/docs/vitepress/docs/deploy-with-docker-compose.md @@ -0,0 +1,104 @@ +# Deploy with Docker Compose + +GISNav utilizes several [Docker Compose](/glossary#docker-compose) services to establish its various deployment configurations. These services are orchestrated through a Makefile to improve convenience and facilitate adoption. + +This page provides details on how to build and deploy these services, allowing for customization of GISNav's deployments beyond the capabilities provided by the Makefile. + +## Prerequisites + +### Docker Compose and NVIDIA Container Toolkit + + + +### GISNav source code + + + + + +### Compose project name + + + +This step is optional since the `docker compose` example commands on this page will use the `-p gisnav` option to ensure the project name is always specified. + +## Example deployments + +The interdependencies between different services are hard-coded into the Docker Compose file using the `depends_on` key and typically you will need to start only a few services explicitly to get everything up and running. + +### Mock GPS demo + +To deploy the [mock GPS demonstration](/README) introduced locally without using the Makefile, follow the below steps to create, start, and shutdown the required containers. These steps simply repeat what the `demo` Makefile recipe would do. + +#### Build images + +```bash +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav build gisnav +``` + +#### Create containers + +```bash +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav create gisnav +``` + +#### Expose X server to containers + + + +#### Start containers + +```bash +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav start gisnav +``` + +#### Stop containers + +```bash +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav start gisnav +``` + + +### Local development + +When deploying for local development, the difference to deploying [mock GPS demo](#mock-gps-demo) is that we do not include the `gisnav` service which is assumed to be launched directly on the Docker host. + +::: tip Remember to expose X server +Remember to [expose your X server to your containers](#expose-x-server-to-containers) to ensure any GUIs are displayed properly. +::: + +::: code-group + +```bash [Build images and create containers] +cd ~/colcon_ws/src/gisnav/docker +docker compose create --build \ + px4 \ + rviz \ + qgis + +``` + +```bash [Start containers] +cd ~/colcon_ws/src/gisnav/docker +docker compose start \ + px4 \ + rviz \ + qgis + +``` + +```bash [Stop containers] +cd ~/colcon_ws/src/gisnav/docker +docker compose stop \ + px4 \ + rviz \ + qgis +``` + +::: + +After you have your supporting services deployed, you might be interested in [launching a local GISNav app](/deploy-for-development#deploy-via-ros-launch-system). diff --git a/docs/vitepress/docs/glossary.md b/docs/vitepress/docs/glossary.md new file mode 100644 index 00000000..fc55c62a --- /dev/null +++ b/docs/vitepress/docs/glossary.md @@ -0,0 +1,691 @@ +# Glossary + +::: warning Warning: Frequent revision +These definitions are frequently revised as the software is developed and therefore may not always correspond to what is currently written in the source code. + +::: + +## Terminology + +Here we provide explanations for terms used throughout this documentation that are more esoteric or represent jargon i.e. have meanings unique to the context of this documentation, deviating from their more generally understood interpretations. + +Some terms may have multiple definitions (e.g. [parameter](#parameter) or [frame](#frame)) and the correct alternative should be easily inferred from the context. Similarly, multiple terms may be used to refer to the same concept depending on the context (e.g. [robot](#robot) vs. [vehicle](#vehicle)). + +### Altitude +Altitude of the [vehicle](#vehicle) in any vertical datum or reference level. + +::: info See also +[AGL](#agl), [AMSL](#amsl) + +::: + +### Anchor +A [YAML](#yaml) anchor + +### Extension, extended functionality +- Functionality beyond GISNav [core](#core-core-functionality) functionality. For example, +[NMEANode](#nmea-node) for integrating GISNav as a mock [GPS](#gnss-gps) device over +[NMEA](#nmea-nmea-0183) +- A [Docker Compose extension](https://docs.docker.com/compose/compose-file/11-extension/) + +### Autopilot +Autopilot flight control software such as [PX4](#px4) or [ArduPilot](#ardupilot). + +### Blending +Blending of multiple [GPS](#gnss-gps) sensors by the [navigation filter](#navigation-filter). + +### Bounding box +A geographical box or rectangle, the coordinates of which are known, that bounds an +area of interest to be used as a [reference](#reference) for [pose](#pose) estimation. + +::: info Learn more +Learn more at [wiki.openstreetmap.org/wiki/Bounding_Box](https://wiki.openstreetmap.org/wiki/Bounding_Box) + +::: + +### Bucket +An [AWS](#aws) [S3](#s3) bucket + +### Camera +A monocular camera carried [onboard](#onboard) that is used by GISNav for [pose](#pose) +estimation. + +### Companion, companion computer +The [onboard](#onboard) companion computer that GISNav runs on. E.g., NVIDIA [Jetson Nano](#nano-jetson-nano). + +### Container +A [Docker](#docker) container. + +### Core, core functionality +GISNav core functionality refers to parts of GISNav code that are assumed to be needed for every kind of deployment. Opposed to [extension](#extension-extended-functionality), which are typically integrations built downstream of core functionality. + +### Coverage +- Code coverage +- [Onboard](#onboard) [GIS](#gis) embedded [orthoimagery](#orthoimagery-imagery-orthoimage-orthophoto) coverage over flight [mission](#mission) area + +### Decorator +A [Python](#python) decorator function. + +### Deploy, deployment +A GISNav deployment consisting of various configurations of [Docker Compose](#docker-compose) services. + +### Drone +Currently NOT used by GISNav. [UAV](#uav), [vehicle](#vehicle) or [robot](#robot) are used instead, depending on context. + +### Elevation +Elevation of the ground surface or [ground track](#ground-track) in any vertical datum or reference level. + +::: info See also +[DEM](#dem), [Altitude](#altitude), [AGL](#agl), [AMSL](#amsl) + +::: + +### Extra +Python package extras: [packaging.python.org/en/latest/tutorials/installing-packages/#installing-extras](https://packaging.python.org/en/latest/tutorials/installing-packages/#installing-extras). + +Used to separate GISNav [core](#core-core-functionality), [extended](#extension-extended-functionality) and development dependencies from each other. + +### Firmware +[Autopilot](#autopilot) software that is loaded onto and executed on the [FCU](#fcu-fmu). More specifically, [PX4](#px4) or [ArduPilot](#ardupilot) software running on the FCU, for example during [HIL](#hil-hitl) simulation. + +### Frame +- A spatial coordinate reference frame, especially as defined in [REP 103 and REP 105](#rep-rep-103-rep-105) +- An image frame, e.g. a single frame from a video stream from the [onboard](#onboard) [camera](#camera) + +### GetFeatureInfo +A [WMS](#wms) operation for requesting non-[raster](#raster) features from [GIS](#gis) servers. Used in earlier versions of GISNav to fetch [DEM](#dem) values for specific points but no longer used. + +### GetMap +A [WMS](#wms) operation for requesting [rasters](#raster) from [GIS](#gis) servers. Allows querying by arbitrary [bounding box](#bounding-box), as opposed to tile-based protocols such as [WMTS](#wms-wmts) + +::: info Learn more +Learn more at [opengeospatial.github.io/e-learning/wms/text/operations.html#getmap](https://opengeospatial.github.io/e-learning/wms/text/operations.html#getmap) + +::: + +### Absolute position, global position +Horizontal and vertical position in a [CRS](#srs-crs) that specifies the location of the [vehicle](#vehicle) relative to an Earth-fixed reference frame (as opposed to a local reference frame) + +::: info See also +[Relative position, local position](#relative-position) + +::: + +### Ground control, ground control software, ground control station +Ground control software that controls the [vehicle](#vehicle) through a remote radio link, using a protocol such as [MAVLink](#mavlink). + +### Ground track +The [vehicle](#vehicle) flight path projected to the ground directly below the vehicle in the direction of [nadir](#nadir). + +### Home +[Vehicle] home position into which it will typically automatically return once it's [flight mission](#mission) is complete. Not necessarily the same as the [EKF](#ekf) local origin. + +### Image +- A [Docker](#docker) image +- A single image frame from the [onboard](#onboard) [camera](#camera) + +::: warning +Not to be confused with [Orthoimage or imagery](#orthoimagery-imagery-orthoimage-orthophoto) + +::: + +### Launch, launch test +Launching using the [ROS](#ros-ros-2) launch system, ROS launch tests. + +### Relative position, local position +Horizontal and vertical position that specifies the location of the [vehicle](#vehicle) relative to +a local reference frame. + +::: info See also +[Absolute position, global position](#absolute-position-global-position) + +::: + +### Map, `map` +- One of the world-fixed [ROS](#ros-ros-2) coordinate [frames](#frame) that are defined in [REP 105](#rep-rep-103-rep-105). In GISNav the `map` frame is defined by [MAVROS](#mavros). +- A [raster](#raster) retrieved from a [GIS](#gis) server. Generic term that could e.g., mean [orthoimagery](#orthoimagery-imagery-orthoimage-orthophoto) or [DEMs](#dem) depending on context. + +### Match, matching +Keypoint matching in the context of camera [pose](#pose) estimation between two images. + +::: info See also +[PnP](#pnp-pnp) +::: + +### Message +A [ROS](#ros-ros-2) message. + +### Middleware +A software application that facilitates communication between other software applications (by transmitting data between them). More specifically, [MAVROS](#mavros), [micro-ROS Agent](#micro-ros-agent) or [GScam](#gscam). + +### Mission, mission mode +- A flight mission, typically a file uploaded to a [GCS](#gcs) which then sends the appropriate commands to the [vehicle](#vehicle) to execute. +- [PX4](#px4) Mission [mode](#mode) + +### Mode +[Autopilot](#autopilot) flight mode + +### Model +- A machine learning model or neural [network](#network). In GISNav used specifically for [camera](#camera) [pose](#pose) estimation. +- A [Gazebo][#gazebo] model, more specifically a [vehicle](#vehicle) model + +### Module +A [Python](#python) module. + +### Nadir +Direction pointing directly down from the [vehicle](#vehicle) (opposed to [zenith](#zenith)). Does not mean down relative to vehicle body but rather the direction of the force of gravity. + +### Navigation filter +An algorithm implemented by the [FMU](#fmu) that is responsible for determining the overall state of the [vehicle](#vehicle) based on fusing available sensor inputs and past states. + +::: info +[EKF](#ekf) is one commonly used algorithm and is often used interchangeably to describe the navigation filter, even if the navigation filter does not use EKF. +::: + +### Network +A neural network (a machine learning [model](#model)), such as SuperGlue or [LoFTR](#loftr) + +### Node +A [ROS](#ros-ros-2) node. + +### Notebook +A [Jupyter](#jupyter-jupyterlab) notebook. + +### Offboard +Anything that is not [onboard](#onboard). More specifically any computer (e.g., running the [GCS](#GCS)) that is not carried onboard the [vehicle](#vehicle) and therefore does not draw power from the vehicle power source. + +::: warning Warning: Conflicting definition in autopilot context +More narrow and conflicting definition that is relevant for autopilots running on [FCU](#fcu-fmu): Any software that is not running on the FCU - even if it is carried "onboard" the vehicle (e.g. "offboard control" when controlling the vehicle from software running on the [companion](#companion) computer). +::: + +### Off-vehicle +Term used by at least [PX4](#px4) in a similar way GISNav currently uses the term [offboard](#offboard). + +### Onboard +Anything carried by the [vehicle](#vehicle) that would draw power from its battery or power source, including the [FCU](#fcu-fmu) and the [companion computer](#companion-companion-computer). + +::: warning Warning: Conflicting definition in autopilot context +More narrow and conflicting definition that is relevant for autopilots running on [FCU](#fcu-fmu): Any software that is running on the FCU. + +::: + +### On-vehicle +Term used by at least [PX4](#px4) in a similar way GISNav currently uses the term [onboard](#onboard). + +### Orientation, attitude +[Vehicle](#vehicle) or [camera](#orientation) orientation (attitude) in 3D space, typically represented by a [quaternion](#quaternion). + +::: info See also +[RPY](#rpy) for Euler angle representation +::: + +### Origin + +Origin of any reference [frame](#frame) + +### Orthoimagery, imagery, orthoimage, orthophoto +- Orthorectified high-resolution geographic imagery stored in [GIS](#gis) +- An orthorectified high-resolution image of a location on Earth for which the [bounding box](#bounding-box) is known, retrieved from a GIS server. + +::: info Learn more +Learn more at [en.wikipedia.org/wiki/Orthophoto](https://en.wikipedia.org/wiki/Orthophoto) +::: + +### Query, query image +In a [pose](#pose) estimation context, the image frame from the [camera](#camera), to be compared to the [reference](#reference) image. The reference image may be an earlier image from the same camera when doing [VO](#vo), or an [orthoimage](#orthoimagery-imagery-orthoimage-orthophoto). + +### Package +- A [ROS](#ros-ros-2) (colcon) package +- A [Python](#python) package + +### Parameter +Most likely one of these: + +- A [ROS 2](#ros-ros-2) parameter +- A [PX4](#px4) parameter +- An [ArduPilot](#ardupilot) parameter + +### Perspective-n-Point, PnP +A problem in computer vision where a camera [pose](#pose) is estimated from 2D image to 3D [world](#world) coordinate point correspondences. + +::: info Learn more +Learn more at [docs.opencv.org/4.x/d5/d1f/calib3d_solvePnP.html](https://docs.opencv.org/4.x/d5/d1f/calib3d_solvePnP.html) +::: + +### Pose +A spatial pose in three dimensions including [position](#position) and [orientation](#orientation). + +### Position +- A 3D [global position](#absolute-position-global-position) +- A 3D [local position](#relative-position-local-position) + +### Publish, publisher +A [ROS](#ros-ros-2) publisher, to publish a ROS [message](#message). + +### Quaternion +A 4-tuple describing [orientation](#orientation-attitude) in 3D space. Avoids the gimbal lock problem that comes when using Euler angles. Should be in (x, y, z, w) order unless otherwise defined. + +:::info See also +[RPY](#rpy) for Euler angle representation of orientation +::: + +### Raster +A rasterized image retrieved from a [GIS](#gis) server, as opposed to a vectorized image. Used exclusively for geographical imagery, not e.g., for an image from the [camera](#camera). + +### Recipe +A [Makefile](#make-make) recipe + +### Reference, reference image, reference raster +In a pose estimation context, the [orthoimage](#orthoimagery-imagery-orthoimage-orthophoto) frame from the [GIS](#gis) server, to be compared to the [query image](#query-query-image), or an earlier frame from the same camera if doing [visual odometry](#vo). + +### Robot + +Used to refer to the [vehicle](#vehicle), typically in a [ROS](#ros-ros-2) context. In GISNav the only kind of robot is the UAV, so this is synonymous with [vehicle](#vehicle). + +### Rotation + +Most likely rotation of the [reference raster](#reference-reference-image-reference-raster) when aligning it with the [vehicle](#heading). This is done in [pose](#pose) estimation because the neural [networks](#network) are not assumed to be rotation agnostic. + + +### Service +- A [Docker Compose](#docker-compose) service +- A [ROS](#ros-ros-2) service + +### Service orchestration +Deploying and managing [Docker Compose](#docker-compose) [services](#service) that constitute a GISNav [deployment](#deploy-deployment). Currently done using [Make](#make-make) (Makefiles). + +### Subscribe, subscriber, subscription +A [ROS](#ros-ros-2) subscription, to subscribe to a ROS [topic](#topic). + +### Target +A [Makefile](#make-make) target + +### Test +Currently the following kinds of tests are recognized: + +- A unit test +- A [ROS](#ros-ros-2) launch test +- A simulation ([SITL](#sitl) or [HIL](#hil-hitl)) test + +The static analysis in the git pre-commit hooks might also sometimes be referred to as "testing". + +### Topic +A [ROS](#ros-ros-2) topic. + +### Vehicle +The [UAV](#uav) that uses GISNav for visual navigation. Can e.g., be a quadcopter or fixed-wing aircraft. This term is preferred by autopilot software like [PX4](#px4) and [ArduPilot](#ardupilot), while the more generic term [robot](#robot) is often used in the [ROS](#ros-ros-2) context. + +### World, world coordinates, world coordinate system +- In the [PNP](#pnp-pnp) problem context, the coordinate system of the [reference image](#reference-reference-image-reference-raster) including the z-axis used to represent ground [elevation](#elevation). +- A [Gazebo](#gazebo) world. + +### Zenith +Direction pointing directly up from the [vehicle](#vehicle) (opposed to [nadir](#nadir)). Does not mean up relative to vehicle body but rather the direction opposite to the force of gravity. + +## Abbreviations + +### BBox +Bounding box + +### Dev +Development + +### Coords +Coordinates + +### Diff, `diff` +Difference + +Also `git diff` and `diff` command line utilities to show differences in git repositories and files respectively. + +### Img +Image + +### Msg +Message + +### NSH, `nsh` +NuttShell, Apache NuttX shell + +### Qry +Query + +### Ref +Reference + +### Sim +Simulation + +### TTY, `tty`, `pty` +Teletypewriter, pseudo-TTY. + +Input devices on Linux. + +## Acronyms + +### AGL +Altitude or Elevation Above Ground Level + +### AMSL +Altitude or Elevation Above Mean Sea Level + +### API +Application Programming Interface + +### AWS +Amazon Web Services + +### CI +Continuous Integration + +### SRS, CRS +Spatial Reference System, Coordinate Reference System + +### CV +Computer Vision + +### DEM +Digital Elevation Model + +### DNS +Domain Name System + +### ECEF +Earth-Centered, Earth-Fixed (coordinate frame) + +E.g. `earth` frame as defined in [REP 105](#rep-rep-103-rep-105) + +### EKF +Extended Kalman Filter + +::: info See also +[Navigation filter](#navigation-filter) +::: + +### ENU +East-North-Up coordinate system + +::: info +Up in the direction of [zenith](#zenith). +::: + +### EOL +End-of-life, e.g., in context of [ROS](#ros-ros-2) distributions that are no longer officially supported. + +### FCU, FMU +Flight Control Unit / Flight Management Unit. For example, [Pixhawk](#pixhawk). + +### FOSS +Free and Open Source Software + +::: info See also +[Free and open-source software](https://en.wikipedia.org/wiki/Free_and_open-source_software) +::: + +### FoV / FOV +Field Of View + +### FRD +Front-Right-Down coordinate system. + +::: info +Down relative to the vehicle body, not [nadir](#nadir). +::: + +### GCS +Ground Control Station + +### GHCR +GitHub Container Registry + +### GIS +Geographic Information System + +### GML +Geography Markup Language + +### GNSS, GPS +Global Navigation Satellite System / Global Positioning System + +### GPU +Graphics Processing Unit + +### GUI +Graphical User Interface + +### HIL, HITL +Hardware In The Loop simulation + +### IDE +Integrated/Interactive Development Environment + +### NAIP +National Agriculture Imagery Program + +::: info See also +[USGS NAIP website](https://www.usgs.gov/centers/eros/science/usgs-eros-archive-aerial-photography-national-agriculture-imagery-program-naip) +::: + +### NED +North-East-Down coordinate system + +::: info +Down in the direction of [nadir](#nadir). +::: + +### NMEA, NMEA 0183 +Communication protocol for GPS receivers + +### OGC +Open Geospatial Consortium: [OGC](https://www.ogc.org/) + +### OS +Operating System + +### OSM +OpenStreetMap + +### PnP, PNP +[Perspective-n-Point](#perspective-n-point-pnp) problem + +### PR +Pull Request + +### QGC +QGroundControl + +### RDP +Remote Desktop Protocol + +### REP, REP 103, REP 105 +ROS Enhancement Proposal + +- [REP 103](https://www.ros.org/reps/rep-0103.html) +- [REP 105](https://www.ros.org/reps/rep-0104.html) + +### RPY +Roll, pitch, yaw. Euler angle representation of [attitude](#orientation-attitude) which suffers from gimbal lock, unlike [quaternions](#quaternion) + +### SCP, `scp` +Secure Copy Protocol + +### SITL +Software In The Loop simulation + +### SQL +Structured Query Language: [SQL](https://en.wikipedia.org/wiki/SQL) + +### TCP, TCP/IP, IP +Transmission Control Protocol/Internet Protocol + +### ToU / TOU +Terms of Use + +### UAV +Unmanned Aerial Vehicle + +### UDP +User Datagram Protocol: [User Datagram Protocol](https://en.wikipedia.org/wiki/User_Datagram_Protocol) + +### USGS +United States Geological Survey + +### VNC +Virtual Network Computing + +### VO +Visual Odometry + +### WGS, WGS 84 +A World Geodetic System coordinate system: [World Geodetic System](https://en.wikipedia.org/wiki/World_Geodetic_System) + +### WMS, WMTS +Web Map Service / Web Map Tile Service, two separate OGC developed communication protocols. WMS allows querying by arbitrary bounding box while WMTS returns pre-computed tiles in a standardized grid. + +::: info See also +- [WMS Standard](https://www.ogc.org/standards/wms) +- [WMTS Standard](https://www.ogc.org/standards/wmts) + +::: + +## Proper Names + +### ArduPilot +ArduPilot open source autopilot: [ardupilot.org](https://ardupilot.org/) + +### `colcon` +A build automation tool used by ROS 2: [colcon.readthedocs.io/en/released](https://colcon.readthedocs.io/en/released/) + +### CUDA +NVIDIA parallel computing platform: [developer.nvidia.com/cuda-zone](https://developer.nvidia.com/cuda-zone) + +### DDS / Data Distribution Service +A middleware protocol and standard: [dds-foundation.org](https://www.dds-foundation.org/) + +### Docker +Software containerization tool: [docker.com](https://www.docker.com/) + +### Docker Compose +Tool for defining and running multi-container Docker applications: [docs.docker.com/compose](https://docs.docker.com/compose/) + +### FileGator +A FOSS self-hosted file management application: [docs.filegator.io](https://docs.filegator.io/) + +### Gazebo +Simulation software: [gazebosim.org/home](https://gazebosim.org/home) + +### GDAL, Geospatial Data Abstraction Library +Software library for handling geospatial data: [gdal.org](https://gdal.org/) + +### GSCam, `gscam` +ROS GStreamer camera driver: [github.com/ros-drivers/gscam](https://github.com/ros-drivers/gscam) + +### GStreamer +Open source multimedia framework: [gstreamer.freedesktop.org](https://gstreamer.freedesktop.org/) + +### Nano, Jetson Nano +An NVIDIA Jetson Nano computer + +### Jupyter, JupyterLab +A web-based IDE: [jupyter.org](https://jupyter.org/) + +### Make, `make` +GNU Make, a build automation tool: [gnu.org/software/make](https://www.gnu.org/software/make/) + +### MapServer +Open source GIS software: [mapserver.org](https://mapserver.org/) + +### MAVLink +MAVLink (Micro Air Vehicle Link) protocol: [mavlink.io/en](https://mavlink.io/en/) + +### MAVROS +An open source MAVLink to ROS middleware: [wiki.ros.org/mavros](http://wiki.ros.org/mavros) + +### MAVSDK +MAVLink software development kit: [mavsdk.mavlink.io/main/en/index.html](https://mavsdk.mavlink.io/main/en/index.html) + +### Mermaid, `mermaid.js` +A diagram scripting language: [mermaid.js.org](https://mermaid.js.org/) + +### micro-ROS Agent +PX4-specific ROS middleware: [docs.px4.io/main/en/middleware/uxrce_dds.html](https://docs.px4.io/main/en/middleware/uxrce_dds.html) + +### OpenCV, `cv2` +Open source computer vision software library: [opencv.org](https://opencv.org/). + +`cv2` refers to the OpenCV Python bindings module. + +### OpenStreetMap +Open source map of the world: [openstreetmap.org](https://www.openstreetmap.org/) + +### Pixhawk +Hardware standard for open source autopilots: [pixhawk.org](https://pixhawk.org/) + +### PostGIS +GIS extension for Postgres: [postgis.net](https://postgis.net/) + +### Postgres +An SQL server: [postgresql.org](https://www.postgresql.org/) + +### PX4 +PX4 Autopilot: [px4.io](https://px4.io/) + +### Python +A computer programming language: [python.org](https://www.python.org/) + +### QEMU +A FOSS full-system emulator: [qemu.org](https://www.qemu.org/) + +### QGIS +A GIS client (and server): [qgis.org/en/site](https://qgis.org/en/site/) + +### QGroundControl +GCS software: [qgroundcontrol.com](http://qgroundcontrol.com/) + +### ROS, ROS 2 +Robot Operating System: [ros.org](https://www.ros.org/) + +GISNav uses ROS 2. + +### RViz +ROS 3D visualization software: [github.com/ros2/rviz](https://github.com/ros2/rviz) + +### S3 +[AWS](#aws) Simple Storage Service (S3): [aws.amazon.com/s3](https://aws.amazon.com/s3/) + +### Sphinx +Documentation generation software: [sphinx-doc.org/en/master/](https://www.sphinx-doc.org/en/master/) + +### tf2 +[ROS 2](#ros-ros-2) transformations library: [wiki.ros.org/tf2](http://wiki.ros.org/tf2) + +::: info Not Tensorflow +Tensorflow is often referred to as TF or `tf` in code but GISNav does not use it - GISNav uses +[Torch](#torch-pytorch) instead. + +::: + +### Torch, PyTorch +An open source machine learning software library: [torch.ch](http://torch.ch/) + +Python machine learning library based on Torch: [pytorch.org](https://pytorch.org/) + +### Ubuntu +A Linux distribution, only supported OS for GISNav: [ubuntu.com](https://ubuntu.com/) + +### VitePress +Static site generator software: [vitepress.dev](https://vitepress.dev/) + +### VRT +GDAL Virtual Format (file format) + +### X Server +Window system that comes with Ubuntu: [x.org/wiki](https://www.x.org/wiki/) + +### YAML +A data serialization language: [yaml.org](https://yaml.org/) + +## Other + +### KSQL +ICAO airport code for San Carlos Airport in California (used as simulation environment in GISNav development and testing). diff --git a/docs/vitepress/docs/index.md b/docs/vitepress/docs/index.md new file mode 100644 index 00000000..36b02179 --- /dev/null +++ b/docs/vitepress/docs/index.md @@ -0,0 +1,54 @@ +--- +# https://vitepress.dev/reference/default-theme-home-page +layout: home + +hero: + name: "GISNav" + text: "Optical terrain-matching for UAVs" + tagline: "A ROS 2 package that determines UAV global position by aligning real-time video with maps from an onboard GIS server." + actions: + - theme: brand + text: "Demo" + link: "/README" + - theme: alt + text: "Guide" + link: "/README" + - theme: alt + text: "API Reference" + link: "/api-examples" + +features: + - title: "GNSS-free navigation" + details: "Operates independent of GNSS systems such as GPS, providing secondary navigation in environments where GNSS signals are weak or unavailable." + + - title: "Offline navigation" + details: "Designed to function without any external connections, ensuring continuous operation even in remote or network-restricted areas." + + - title: "Optical terrain-matching" + details: "Provides a precise global position by visually comparing frames from the vehicle's nadir-facing camera to a map of the UAVs approximate global position retrieved from an onboard GIS server." + + - title: "FOSS with MIT License" + details: "Open source under the permissive MIT license, allowing for free use, modification, and distribution, fostering a community of innovation and improvement." + + - title: "Monocular Camera Compatibility" + details: "Compatible with any standard monocular camera, facilitating easy adoption and integration with existing equipment, without requiring specialized hardware." + + - title: "MAVLink, NMEA and uORB Protocols" + details: "Supports integration with popular autopilot systems like PX4 and ArduPilot through MAVLink, NMEA and uORB protocols, enhancing interoperability and control." + + - title: "Secondary GPS Over Serial Port (NMEA)" + details: "Functions as a reliable secondary GPS, easily integrating over serial connections without the need for firmware modifications, enhancing navigational redundancy and safety." + + - title: "Simulation with Gazebo" + details: "Includes support for Gazebo simulations, enabling developers to test and refine drone operations in a fully controlled virtual environment, accelerating development cycles and reducing field testing risks." + + - title: "ROS 2 Integration" + details: "Seamlessly integrates with the ROS 2 ecosystem, providing robust middleware solutions that enhance the functionality and scalability of UAV operations." + + - title: "Live Navigation Updates" + details: "Offers live navigation updates, ensuring UAVs respond promptly to environmental changes and mission updates, crucial for dynamic and unpredictable operating conditions." + + +--- + + diff --git a/docs/vitepress/docs/install-locally.md b/docs/vitepress/docs/install-locally.md new file mode 100644 index 00000000..aba09c39 --- /dev/null +++ b/docs/vitepress/docs/install-locally.md @@ -0,0 +1,133 @@ +# Install locally + +This page describes how to install GISNav locally on a development computer. With a local non-containerized installation of GISNav it is easier re-test the software without having to rebuild a Docker image and re-deploy a container every time changes are made to the source code. + +::: tip Deploy with Docker Compose +If you are only interested in running and not developing GISNav, you may want to look at the [Docker Compose services](/deploy-with-docker-compose) instead. + +::: + +## System requirements + +These system requirements are intended for SITL simulation on a standalone development computer. + +### Operating system + +The development computer should be running **Ubuntu 22.04 (Jammy Jellyfish)**. Other Ubuntu and Linux releases may also work with some modifications, but are currently untested and therefore unsupported. + +### Hardware + +It is strongly recommended that the development computer have an **NVIDIA GPU with CUDA Toolkit and latest drivers** installed. + +::: info No hard dependency on NVIDIA +The GISNav ROS 2 package itself depends on [Torch](/glossary#torch-pytorch) and should not have a direct dependency on NVIDIA products. GPUs from other vendors may work but also have not been tested. Some of the GISNav Docker Compose services on the other hand may have a direct dependency on NVIDIA. + +::: + +You can inspect your NVIDIA driver and CUDA versions with the `nvidia-smi` command line utility. If you don't have it installed, follow the [NVIDIA CUDA Installation Guide for Linux](https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html). The output of the `nvidia-smi` command should look something like below: + +```console +hmakelin@hmakelin-MS-7D48:~/colcon_ws$ nvidia-smi +Sat May 4 08:37:42 2024 ++---------------------------------------------------------------------------------------+ +| NVIDIA-SMI 535.171.04 Driver Version: 535.171.04 CUDA Version: 12.2 | +|-----------------------------------------+----------------------+----------------------+ +| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | +| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | +| | | MIG M. | +|=========================================+======================+======================| +| 0 NVIDIA GeForce RTX 4060 Off | 00000000:01:00.0 On | N/A | +| 0% 42C P8 N/A / 115W | 506MiB / 8188MiB | 23% Default | +| | | N/A | ++-----------------------------------------+----------------------+----------------------+ + ++---------------------------------------------------------------------------------------+ +| Processes: | +| GPU GI CI PID Type Process name GPU Memory | +| ID ID Usage | +|=======================================================================================| +| 0 N/A N/A 1918 G /usr/lib/xorg/Xorg 228MiB | +| 0 N/A N/A 2320 G /usr/bin/gnome-shell 35MiB | +| 0 N/A N/A 4528 G ...irefox/4173/usr/lib/firefox/firefox 162MiB | +| 0 N/A N/A 118428 G ...erProcess --variations-seed-version 47MiB | +| 0 N/A N/A 148095 G ...erProcess --variations-seed-version 20MiB | ++---------------------------------------------------------------------------------------+ +``` + +## Install ROS 2 Humble + +GISNav is a ROS 2 package, and the supported version is ROS 2 Humble. Install ROS 2 Humble by following the official [install instructions](https://docs.ros.org/en/humble/Installation.html). + +## Setup `colcon` workspace + +Colcon is a ROS build tool and needed to build GISNav and some of its dependencies from source code. + +### Create workspace + + + +### Clone GISNav and dependencies + + + +### Source workspace + + + +## Install system dependencies + +Install system dependencies for your workspace with the following commands: + +```bash +cd ~/colcon_ws/src +rosdep update +rosdep install --from-paths . -y -r --ignore-src +``` + +::: tip ROS EOL distros +If you want to use a ROS distribution that has reached end-of-life like Foxy, you can provide the `--include-eol-distros` option to `rosdep update`. + +::: + +## Install Python dependencies + +::: warning Warning: Python virtual environments +If you want to use a Python virtual environment for your Python +dependencies, check out [this issue](https://github.com/ros2/ros2/issues/1094) first. We do not provide instructions here +as the workspace might not play nicely with the virtual environment. + +::: + +GISNav's Python dependencies are divided into [core](./glossary#core-core-functionality), [extended](./glossary#extension-extended-functionality), and development dependencies. You must at least install the core dependencies. + +- If you know you are not going to use a specific extension such as `QGISNode`, you can skip installing the corresponding Python [extra](/glossary#extra). `NMEANode` is required for the [mock GPS demo](/README) and enables downstream integration of GISNav as a secondary GPS device via the NMEA protocol. + +- The development dependencies are required for various development tasks such as generating documentation and running tests. You do not need to install them if you do not plan to do any development work on GISNav. + +Install the required and optional Python dependencies with the following commands: + +::: code-group + +```bash [Core] +cd ~/colcon_ws/src/gisnav +pip3 install ./gisnav +``` + +```bash [Extended ] +cd ~/colcon_ws/src/gisnav +pip3 install ./gisnav[nmea_node] +pip3 install ./gisnav[qgis_node] +``` + +```bash [Development ] +cd ~/colcon_ws/src/gisnav +pip3 install ./gisnav[dev] +``` + +::: + +## Build workspace + + + +Once GISNav is installed, you can try [deploying the development services](/deploy-development-services). diff --git a/docs/vitepress/docs/jetson-pixhawk.md b/docs/vitepress/docs/jetson-pixhawk.md new file mode 100644 index 00000000..82105249 --- /dev/null +++ b/docs/vitepress/docs/jetson-pixhawk.md @@ -0,0 +1,154 @@ +# Pixhawk & Jetson Nano HIL + +::: info NVIDIA Jetson Nano discontinued +This article was created for an older version of GISNav and since its publication, NVIDIA Jetson Nano has been discontinued. This article is provided for reference until an updated article on HIL simulation is written. + +::: + +::: info Todo +GISNav `v0.65.0` now comes with `arm64` base images. Update section on QEMU. + +::: + +This section provides an example on how to run GISNav on a Jetson Nano in a PX4 HIL simulation on a Pixhawk FMU. This example uses the [NXP FMUK66-E board](https://docs.px4.io/main/en/flight_controller/nxp_rddrone_fmuk66.html) as an example but any [PX4 supported board](https://px4.io/autopilots/) should work. + +::: warning Warning: Propellers off +Keep the propellers **off** your drone throughout the HIL simulation. + +::: + +### Prerequisites + +[Install GISNav locally](./install-locally) + +### Connect Jetson Nano and Pixhawk + +In this example, we will power the Pixhawk from the development computer via USB and the Jetson Nano from a wall socket via a DC adapter to avoid having to handle LiPo batteries. In a more realistic setup, you would supply power to both boards from the onboard battery. + +Follow the below steps and diagram to setup your HIL simulation hardware: + +- **Install a bootloader on your Pixhawk board if your board does not yet have one.** See your board manufacturer's instructions on how to load one onto your specific board. +- Connect your Jetson Nano to your development computer via Ethernet cable (see Jetson Nano SITL for more information). +- Connect your Jetson Nano to a wall socket using a micro-USB power adapter. +- Connect your Pixhawk board to your development computer via micro-USB cable. +- Connect your Pixhawk board to your Jetson Nano via TELEM1 (set PX4 `XRCE_DDS_0_CFG` parameter value to `101`) using a USB to UART converter. + +#### Diagram + +```mermaid +graph TB + subgraph "FMUK66-E (FMU)" + subgraph "TELEM1" + FMU_TELEM1_RX[RX] + FMU_TELEM1_TX[TX] + FMU_TELEM1_GND[GND] + end + FMU_USB[micro-USB Port] + end + subgraph "Development computer" + Laptop_ETH[Ethernet Port] + Laptop_USB[USB Port] + end + subgraph "Jetson Nano" + Nano_USB[USB Port x4] + Nano_micro_USB[Micro USB Port] + Nano_HDMI[HDMI Port] + Nano_ETH[Ethernet] + end + subgraph "USB to UART Converter" + Converter_RX[RX] + Converter_TX[TX] + Converter_GND[GND] + Converter_USB[USB] + end + Socket[Wall Socket] + subgraph "Optional (can also use RDP or VNC)" + Display[External Display] + Mouse[USB Mouse] + Keyboard[USB Keyboard] + end + FMU_TELEM1_TX ---|To UART RX| Converter_RX + FMU_TELEM1_RX ---|To UART TX| Converter_TX + FMU_TELEM1_GND ---|To UART GND| Converter_GND + FMU_USB ---|To Dev Computer USB| Laptop_USB + Converter_USB ---|To Nano USB| Nano_USB + Nano_micro_USB ---|Micro USB Power| Socket + Nano_HDMI ---|HDMI| Display + Nano_USB ---|USB| Mouse + Nano_USB ---|USB| Keyboard + Nano_ETH ---|To Dev Computer ETH| Laptop_ETH +``` + +#### Picture + +![NXP FMUK66-E Setup](/gisnav_hil_fmuk66-e_setup.jpg) + +* NXP FMUK66-E (FMU) board connected to laptop via micro-USB and to Jetson Nano via TELEM1. +* FMU draws power from laptop via micro-USB, and Jetson Nano from wall socket via dedicated micro-USB DC adapter, so no LiPo batteries needed. +* Connection from FMU to Jetson Nano via TELEM1 serial port using USB to UART converter. See [FMUK66-E revision C pin layout](https://nxp.gitbook.io/hovergames/rddrone-fmuk66/connectors/telemetry-1) for how to wire the TELEM1 JST-GH connector (only GND, RX and TX used here). +* Other wires as per [manufacturer's instructions](https://nxp.gitbook.io/hovergames/userguide/assembly/connecting-all-fmu-wires), except for missing telemetry radio. + +::: info TX to RX to TX +The TX from one board connects to the RX of the other board and vice versa. + +::: + +### Install QEMU emulators + +Install QEMU emulators on your Jetson Nano to make `linux/amd64` images run on the `linux/arm64` Jetson Nano: + +```bash +docker run --privileged --rm tonistiigi/binfmt --install all +``` + +[QEMU Documentation](https://docs.docker.com/build/building/multi-platform/#building-multi-platform-images) + +### Upload PX4 firmware + +See the [PX4 uploading firmware instructions](https://docs.px4.io/main/en/dev_setup/building_px4.html#uploading-firmware-flashing-the-board) for how to upload your development version of PX4 onto your Pixhawk board. To find the `make` target for your specific board, list all options with the `make list_config_targets` command: + +```bash +cd ~/colcon_ws/src/gisnav/docker +export COMPOSE_PROJECT_NAME=gisnav +docker compose run px4 make list_config_targets +``` + +Then choose your appropriate board for the following examples. We are going to choose `nxp_fmuk66-e_default` for this example: + +```bash +export COMPOSE_PROJECT_NAME=gisnav +docker compose run px4 make distclean +docker compose run px4 make nxp_fmuk66-e_default upload +``` + +### Deploy offboard services + +The following steps to deploy the offboard services are based on the [PX4 HIL simulation instructions](https://docs.px4.io/main/en/simulation/hitl.html). The `px4` Docker compose service has a custom `iris_hitl` model and a `hitl_iris_ksql_airport.world` Gazebo world that we are going to use in this example: + +```bash +export COMPOSE_PROJECT_NAME=gisnav +docker compose run -e DONT_RUN=1 px4 make px4_sitl_default gazebo-classic +docker compose run px4 source Tools/simulation/gazebo-classic/setup_gazebo.bash $(pwd) $(pwd)/build/px4_sitl_default +docker compose run px4 gazebo Tools/simulation/gazebo-classic/sitl_gazebo-classic/worlds/hitl_iris_ksql_airport.world + +# Important: Start QGroundControl last +docker compose up qgc +``` + +After deploying the HIL simulation, adjust the settings via the QGC application as follows: + +- Precisely match the `COM_RC_IN_MODE` parameter setting if mentioned in the instructions. +- Ensure that you have HITL enabled in QGC Safety settings. +- Ensure you have the virtual joystick enabled in QGC General settings. + +### Deploy onboard services + +Once you have the HIL simulation running, login to your Jetson Nano and deploy the onboard services: + +```bash +mkdir -p ~/colcon_ws/src +cd ~/colcon_ws/src +git clone https://github.com/hmakelin/gisnav.git +cd ~/colcon_ws/src/gisnav +make -C docker up-onboard-hil-px4 +``` diff --git a/docs/vitepress/docs/markdown-examples.md b/docs/vitepress/docs/markdown-examples.md new file mode 100644 index 00000000..e01df27c --- /dev/null +++ b/docs/vitepress/docs/markdown-examples.md @@ -0,0 +1,91 @@ +# Markdown Extension Examples + +::: warning Simulation use only + +GISNav is untested and has only been demonstrated in a simulation environment. Do not attempt to use GISNav on real flights. + +::: + +This page demonstrates some of the built-in markdown extensions provided by VitePress. + +## Syntax Highlighting + +VitePress provides Syntax Highlighting powered by [Shiki](https://github.com/shikijs/shiki), with additional features like line-highlighting: + +**Input** + +````md +```js{4} +export default { + data () { + return { + msg: 'Highlighted!' + } + } +} +``` +```` + +**Output** + +```js{4} +export default { + data () { + return { + msg: 'Highlighted!' + } + } +} +``` + +## Custom Containers + +**Input** + +```md +::: info +This is an info box. +::: + +::: tip +This is a tip. +::: + +::: warning +This is a warning. +::: + +::: danger +This is a dangerous warning. +::: + +::: details +This is a details block. +::: +``` + +**Output** + +::: info +This is an info box. +::: + +::: tip +This is a tip. +::: + +::: warning +This is a warning. +::: + +::: danger +This is a dangerous warning. +::: + +::: details +This is a details block. +::: + +## More + +Check out the documentation for the [full list of markdown extensions](https://vitepress.dev/guide/markdown). diff --git a/docs/vitepress/docs/public/gisnav_hil_fmuk66-e_setup.jpg b/docs/vitepress/docs/public/gisnav_hil_fmuk66-e_setup.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e6bdb54c4d4b49852e533cc146a9db98a4697811 GIT binary patch literal 78150 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 literal 0 HcmV?d00001 diff --git a/docs/vitepress/docs/remap-ros-topics.md b/docs/vitepress/docs/remap-ros-topics.md new file mode 100644 index 00000000..11a8ff37 --- /dev/null +++ b/docs/vitepress/docs/remap-ros-topics.md @@ -0,0 +1,54 @@ +# Remap ROS topics + +The `gisnav` ROS 2 package depends on ROS for both external and internal communication, and ROS is also the natural way to integrate GISNav with other systems. To integrate GISNav with another ROS system, some topic name remapping may be required. + +::: tip Remapping GISNav output +The most interesting ROS message is most likely the filtered camera pose and twist published as `nav_msgs/Odometry` by the `ekf_localization_node` from the `robot_localization` package (a dependency of `gisnav`), or the raw camera pose published as `geometry_msgs/PoseWithCovarianceStamped` by `pose_node` from the `gisnav` package. + +The `gisnav_msgs` package includes message definitions that GISNav uses internally which are probably not interesting from an integration perspective. + +See the [ROS topography](/system-architecture#ros-topography) diagram for more context. + +::: + +## Prerequisites + + + +## Example commands + +See the examples below on how to launch and run GISNav ROS nodes with remapped topic names. + +### ros2 launch + +The below Python launch file diff shows an example remapping of the camera topics for `StereoNode`: + +```python +ld.add_action( + Node( + package="gisnav", + name="stereo_node", + namespace="gisnav", + executable="stereo_node", + parameters=[ + os.path.join(package_share_dir, "launch/params/stereo_node.yaml") + ], + remappings=[ # [!code ++] + ("camera/camera_info", "camera_info"), # [!code ++] + ("camera/image_raw", "image"), # [!code ++] + ], # [!code ++] + ) + ) +``` + +### ros2 run + +The below example shows a remapping of camera topics for `StereoNode` on the command line: + +```bash +cd ~/colcon_ws +ros2 run gisnav stereo_node --ros-args \ + --params-file src/gisnav/launch/params/stereo_node.yaml \ + -r camera/camera_info:=camera_info \ + -r camera/image_raw:=image +``` diff --git a/docs/vitepress/docs/ros-run-node.md b/docs/vitepress/docs/ros-run-node.md new file mode 100644 index 00000000..a9901ccf --- /dev/null +++ b/docs/vitepress/docs/ros-run-node.md @@ -0,0 +1,34 @@ +# Run individual ROS nodes + +This page provides examples for running individual ROS nodes with custom options and custom values for the ROS parameters from the YAML files in the `launch/params/` folder. + +The commands here are mostly useful for debugging and development, while [Docker Compose](/deploy-with-docker-compose) and the [Makefile and ROS launch system](/deploy-for-development) are recommended for launching larger configurations of nodes. + +## Prerequisites + + + +## Custom log level + +Change the log level with the `--log-level` argument: + +```bash +cd ~/colcon_ws +ros2 run gisnav gis_node --ros-args \ + --log-level info +``` + +## Custom parameter values + +Provide custom ROS parameter values at launch in a YAML file using the `--params-file` argument: + +```bash +cd ~/colcon_ws +ros2 run gisnav gis_node --ros-args \ + --params-file src/gisnav/gisnav/launch/params/gis_node.yaml +``` + +::: tip Configure via admin portal +The [Admin portal](/admin-portal) home page has a link to a captive FileGator file server that allows you to edit these ROS parameter YAML files via a web browser. + +::: diff --git a/docs/vitepress/docs/setup-gis-server.md b/docs/vitepress/docs/setup-gis-server.md new file mode 100644 index 00000000..1d8f97ab --- /dev/null +++ b/docs/vitepress/docs/setup-gis-server.md @@ -0,0 +1,144 @@ +# Setup GIS Server + +GISNav requires access to a GIS server that serves high-resolution orthoimagery for the approximate global position of the vehicle. The orthoimagery consisting of orthophotos and optional DEM (Digital Elevation Model) rasters are requested from a WMS (Web Map Service) which allows querying rasters by an arbitrary bounding box, and DEM elevation values by an arbitrary global position via its GetMap requests. + +The DEM is optionally used to input ground elevation z-coordinates to the PnP (Perspective-n-Point) problem solved by GISNav's pose estimation algorithm in `PoseNode`. If a DEM is not available, GISNav simply assumes a planar ground elevation, which may be sufficient when flying at higher altitudes where an isometric perspective does not significantly distort the perceived image. + +## Example Setups + +You are encouraged to self-host an onboard GIS server with public domain orthoimagery because in a realistic scenario, the GIS should be embedded onboard and not depend on an internet connection. For development, it may sometimes be more convenient to proxy an existing commercial tile-based endpoint. + +::: info Tile-based endpoints +Commercial web-based map services are often [tile-based](https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames) because it is more efficient to serve pre-rendered tiles than to render unique rasters for each individual requested bounding box. You will need a WMS proxy if you decide to go with a tile-based endpoint. + +::: + +::: warning: Warning: Caching of map tiles +Many commercial services explicitly prohibit the caching of map tiles in their Terms of Use (ToU), especially if their business model is based on billing API requests. This is mainly to prevent disintermediation in case their tiles are redistributed to a large number of end users. + +While caching tiles onboard your own drone is likely not the kind of misuse targeted by such clauses, you should still make sure you understand the ToU of the service you are using and that it fits your planned use case. + +::: + +If you are fine with using maps for the KSQL airport area only, then you can use the [provided Docker Compose mapserver service](#overview-of-services). See [Managing onboard map rasters](#managing-onboard-map-rasters) for how to add more maps for the mapserver service. + +Otherwise, follow these instructions to self-host a MapServer instance: + +::: info FOSS GIS software +See [GIS software](#gis-software) for free and open-source software (FOSS) alternatives for MapServer. + +::: + +To follow these instructions you will need: + +- An AWS account and AWS CLI, **or alternatively**, an [EarthExplorer](https://earthexplorer.usgs.gov) account +- GDAL installed + +In this example, we will download NAIP imagery and host it using the [MapServer docker image](https://hub.docker.com/r/camptocamp/mapserver) from Docker Hub. You can download the GeoTIFF imagery from EarthExplorer, or from the Esri-maintained [AWS S3 bucket](https://registry.opendata.aws/naip/): + +::: warning Warning: Requester pays +This is a **Requester Pays** bucket, and the files can be very large, so download only what you need. + +::: + +```bash +cd ~/gisnav-docker +mkdir -p mapfiles/ +aws s3 cp \ + --request-payer requester \ + s3://naip-source/ca/2020/60cm/rgbir_cog/37122/m_3712230_se_10_060_20200524.tif \ + mapfiles/ +``` + +::: info EROS account required +NAIP imagery is in the public domain. However, you must create an EROS account to download the rasters from EarthExplorer, or use secondary sources such as the AWS S3 bucket mentioned above. + +You do not need an account to browse for product IDs with EarthExplorer. An account is only needed if you want to download products. + +::: + +Once you have the imagery, use GDAL to make a `naip.vrt` VRT file out of your downloaded GeoTIFFs: + +```bash +cd mapfiles/ +gdalbuildvrt naip.vrt *.tif +``` + +Once you have your .tif and .vrt files, you can run and host them through a MapServer container: + +```bash +export MAPSERVER_PATH=/etc/mapserver +docker run \ + -p 80:80 \ + -v $PWD/mapfiles/:$MAPSERVER_PATH/:ro \ + camptocamp/mapserver +``` + +Test your MapServer WMS service by opening the capabilities XML in your browser: + +```bash +firefox "http://localhost:80/?map=/etc/mapserver/wms.map&service=WMS&request=GetCapabilities" +``` + +## GIS Software + +If you want to run your own GIS server or WMS proxy, you may want to consider these FOSS options: + +- MapServer +- [GeoServer](https://geoserver.org) (full-fledged OGC-compliant GIS server + +) +- [Mapnik](https://mapnik.org) and [MapProxy](https://mapproxy.org) + +## Orthoimagery and DEMs + +If you do not want to use commercial (=not free) high-resolution imagery, various national agencies often provide country-specific aerial imagery in the public domain or with public-domain-like licensing terms. You should look for imagery available in GDAL-supported formats with coverage for your flight mission region. These may be provided as downloadable products or through OGC-compliant web services such as WMS or WMTS. + +Below are just a few examples of national agencies providing high-resolution orthoimagery that should be suitable for use with GISNav: + +- [USGS High Resolution Orthoimagery](https://www.usgs.gov/centers/eros/science/usgs-eros-archive-aerial-photography-high-resolution-orthoimagery-hro) (USA) +- [Environment Agency Vertical Aerial Photography](https://www.data.gov.uk/dataset/4921f8a1-d47e-458b-873b-2a489b1c8165/vertical-aerial-photography) (United Kingdom) +- [NLS orthophotos](https://www.maanmittauslaitos.fi/en/maps-and-spatial-data/expert-users/product-descriptions/orthophotos) (Finland) + +::: info Photogrammetry software +If you have a drone, you can also use readily available [photogrammetry](https://en.wikipedia.org/wiki/Photogrammetry) software to create your own maps for your local region of interest. + +::: + +## Rasterizing Vector Data + +In some cases, useful map data is not directly provided in raster but in vector format. The GISNav `mapserver` service uses vector-format elevation data from [OSM Buildings](https://osmbuildings.org/) to determine building heights in the simulation area to improve the accuracy of pose estimates especially at lower flight altitudes where the perceived planarity of the terrain is lower. For an example of how the vector data is rasterized using GDAL, see this [old mapserver service Dockerfile](https://github.com/hmakelin/gisnav/blob/v0.65.0/docker/mapserver/Dockerfile). + +::: info Demo quirks +The GISNav SITL demo simulation does not actually benefit from the building height data because the simulated KSQL Airport model buildings are all featureless black blocks. See [SITL simulation quirks](#sitl-simulation-quirks) for more information. + +::: + +## SITL Simulation Quirks with DEMs + +The KSQL Gazebo world buildings in the SITL simulation demo are featureless grey blocks, so any pose estimation model will most likely not use them for matching. This means any building elevation data (see [Rasterizing vector data](#rasterizing-vector-data)) will not technically be used to improve pose estimates in the SITL simulation. The below figure illustrates how LoFTR finds keypoints at an even density throughout the simulated vehicle's field of view except on the featureless buildings. + +![LoFTR does not find keypoints on featureless buildings or terrain (SITL simulation)](../../../_static/img/gisnav_sitl_featureless_buildings.jpg) + +## Managing Onboard Map Rasters + +A shared volume is used to provide a way for external file manager services to add and delete maps onboard. The MapServer static mapfile points to a VRT file which is automatically regenerated whenever a change is detected on the shared volume which contains the source raster files. + +A sample FileGator-based `fileserver` service is defined in the `docker/docker-compose.yaml` for managing maps on the shared volume. The application is not necessary - e.g., `scp` could also be used instead. + +```mermaid +graph TB + subgraph mapserver + note1[inotify automatically extracts GDAL supported +formats and regenerates VRT file to which +the default mapfile points] + subgraph SharedVolume[Shared volume] + /etc/mapserver/maps/imagery + /etc/mapserver/maps/dem + end + note2[mapfile and VRT file not on shared volume. +Only external user managed map rasters.] + end + fileserver[External file manager] -->|add/delete| SharedVolume + GDAL[GDAL supported raster formats] -->|.zip, .jp2, .tif, .tiff, .ecw, etc.| SharedVolume +``` diff --git a/docs/vitepress/docs/shared/build-colcon-workspace.md b/docs/vitepress/docs/shared/build-colcon-workspace.md new file mode 100644 index 00000000..c4ea62f4 --- /dev/null +++ b/docs/vitepress/docs/shared/build-colcon-workspace.md @@ -0,0 +1,22 @@ +Build the colcon workspace. If you have previously built your workspace and only made changes to GISNav, you may want to only re-build GISNav: + +::: code-group + +```bash [Entire workspace] +cd ~/colcon_ws +colcon build +``` + +```bash [GISNav only] +cd ~/colcon_ws +colcon build --packages-select gisnav gisnav_msgs +``` + +::: + +If it's your first time building the workspace, remember to also source the `install/setup.bash` script to ensure all your newly built executables and libraries are on the path: + +```bash +cd ~/colcon_ws +source install/setup.bash +``` diff --git a/docs/vitepress/docs/shared/clone-to-colcon-workspace.md b/docs/vitepress/docs/shared/clone-to-colcon-workspace.md new file mode 100644 index 00000000..fb56acfc --- /dev/null +++ b/docs/vitepress/docs/shared/clone-to-colcon-workspace.md @@ -0,0 +1,7 @@ +Clone latest version of GISNav and dependencies to colcon workspace: + +```bash +cd ~/colcon_ws/src +git clone --branch v0.66.0 https://github.com/hmakelin/gisnav.git +git clone --branch gimbal-protocol-v2-plugin https://github.com/adinkra-labs/mavros_feature_gimbal-protocol-v2-plugin.git mavros +``` diff --git a/docs/vitepress/docs/shared/compose-project-name.md b/docs/vitepress/docs/shared/compose-project-name.md new file mode 100644 index 00000000..4d2d2cc2 --- /dev/null +++ b/docs/vitepress/docs/shared/compose-project-name.md @@ -0,0 +1,17 @@ +You must set the `COMPOSE_PROJECT_NAME` environment variable to have the value `gisnav` at minimum for the shell that you are running `docker compose` commands from. It is important that all Docker image and container names have the string `gisnav` in them since for example the `expose-xhost` Makefile recipe depends on this string being present to expose the X server only to the appropriate containers. + +::: code-group + + ```bash [Persistent ] + echo "export COMPOSE_PROJECT_NAME=gisnav" >> ~/.bashrc + source ~/.bashrc + ``` + ```bash [Current session] + export COMPOSE_PROJECT_NAME=gisnav + ``` + + ```bash [Current shell only] + COMPOSE_PROJECT_NAME=gisnav + ``` + +::: diff --git a/docs/vitepress/docs/shared/create-colcon-workspace.md b/docs/vitepress/docs/shared/create-colcon-workspace.md new file mode 100644 index 00000000..40e2f842 --- /dev/null +++ b/docs/vitepress/docs/shared/create-colcon-workspace.md @@ -0,0 +1,5 @@ +Create a colcon workspace: + +```bash +mkdir -p ~/colcon_ws/src +``` diff --git a/docs/vitepress/docs/shared/docker-compose-and-nvidia-container-toolkit-required.md b/docs/vitepress/docs/shared/docker-compose-and-nvidia-container-toolkit-required.md new file mode 100644 index 00000000..8e6a3968 --- /dev/null +++ b/docs/vitepress/docs/shared/docker-compose-and-nvidia-container-toolkit-required.md @@ -0,0 +1,4 @@ +You will need to have the [Docker Compose plugin][2] and [NVIDIA Container Toolkit][3] installed. + +[2]: https://docs.docker.com/compose/install/linux/ +[3]: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html diff --git a/docs/vitepress/docs/shared/expose-x-server.md b/docs/vitepress/docs/shared/expose-x-server.md new file mode 100644 index 00000000..d14300f0 --- /dev/null +++ b/docs/vitepress/docs/shared/expose-x-server.md @@ -0,0 +1,20 @@ +This method is more secure as it only exposes the X server to containers with names containing "gisnav": + +```bash +for containerId in $(docker ps -f name=gisnav -aq); do + xhost +local:$(docker inspect --format='{{ .Config.Hostname }}' $containerId) +done +``` + +A recipe to expose the X server is also included in the Makefile `expose-xhost` target: + +```bash +cd ~/colcon_ws/src/gisnav/docker +make expose-xhost +``` + +This method is easier but less secure as it exposes the X server to any client: + +```bash +xhost + +``` diff --git a/docs/vitepress/docs/shared/require-install-locally.md b/docs/vitepress/docs/shared/require-install-locally.md new file mode 100644 index 00000000..882672d0 --- /dev/null +++ b/docs/vitepress/docs/shared/require-install-locally.md @@ -0,0 +1 @@ +These instructions assume you have [installed GISNav locally](/install-locally). diff --git a/docs/vitepress/docs/shared/source-colcon-workspace.md b/docs/vitepress/docs/shared/source-colcon-workspace.md new file mode 100644 index 00000000..3a62bcfe --- /dev/null +++ b/docs/vitepress/docs/shared/source-colcon-workspace.md @@ -0,0 +1,22 @@ +For convenience, source your workspace in your bash profile if you do a lot of development work: + +```bash +echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc +echo "source ~/colcon_ws/install/setup.bash" >> ~/.bashrc +source ~/.bashrc +``` + +Alternatively, simply source it in your current shell: + +```bash +source /opt/ros/humble/setup.bash +source ~/colcon_ws/install/setup.bash +``` + +Test that you have sourced your workspace by listing available packages: + +```bash +ros2 pkg list +``` + +If you see a list of packages, your workspace is probably active. diff --git a/docs/vitepress/docs/shared/warning-simulation-use-only.md b/docs/vitepress/docs/shared/warning-simulation-use-only.md new file mode 100644 index 00000000..7eec91e9 --- /dev/null +++ b/docs/vitepress/docs/shared/warning-simulation-use-only.md @@ -0,0 +1,3 @@ + +> [!WARNING] Warning: Simulation use only +> Do not use this software for real flight missions. GISNav is untested and has only been demonstrated in a simulation environment. diff --git a/docs/vitepress/docs/system-architecture.md b/docs/vitepress/docs/system-architecture.md new file mode 100644 index 00000000..2c8d10d7 --- /dev/null +++ b/docs/vitepress/docs/system-architecture.md @@ -0,0 +1,164 @@ +# System architecture + +The GISNav ROS 2 package receives upstream inputs from the autopilot via MAVLink and transforms them in multiple sequential steps to downstream outputs, the most important of which are the NMEA mock GPS messages. In GISNav this data processing algorithm is expressed as a distributed unidirectional network of ROS nodes. + +The GISNav ROS application also has many external interfaces and effective development requires a simulation environment. GISNav defines and [deploys that simulation environment with Docker Compose](/deploy-with-docker-compose). + +This page provides an overview of both the topography of the GISNav ROS 2 package itself as well as how these different Docker Compose services that consitute the simulation environment relate to each other. + +## ROS topography + +The core ROS topography diagram below depicts how ROS messages flow through GISNav. The [API reference](/api-reference) has more detailed information on the purpose and design of each ROS node. + +::: info `MonocularStereoImage` currently disabled +This diagram depicts partially implemented but disabled visual odometry (VO) functionality (`MonocularStereoImage`). +::: + +::: info Todo +- From BBoxNode, publish map to `base_link` and `base_link` to `camera` transformations separately to simplify implementation and reduce amount of maintained code. +- Implement REP 105 properly (currently only partially implemented). +- Include default topic names in diagram. +::: + +```mermaid +graph TB + + subgraph ros_middleware["ROS middleware"] + MAVROS + gscam + end + + MAVROS -->|"sensor_msgs/NavSatFix"| BBoxNode + MAVROS -->|"mavros_msgs/GimbalDeviceAttitudeStatus"| BBoxNode + gscam ---->|"sensor_msgs/Image"| StereoNode + + subgraph core["GISNav core nodes"] + BBoxNode -->|"geographic_msgs/BoundingBox"| GISNode + GISNode -->|"gisnav_msgs/OrthoImage"| StereoNode + StereoNode -->|"gisnav_msgs/MonocularStereoImage"| PoseNode + StereoNode -->|"gisnav_msgs/OrthoStereoImage"| PoseNode + end + + subgraph extension["GISNav extension nodes"] + NMEANode["NMEANode"] + UORBNode["UORBNode"] + end + + subgraph robot_localization["robot_localization ROS package"] + ekf["ekf_localization_node"] -->|"nav_msgs/Odometry"| UORBNode + ekf["ekf_localization_node"] -->|"nav_msgs/Odometry"| NMEANode + end + + PoseNode -->|"geometry_msgs/PoseWithCovarianceStamped"| ekf + UORBNode -->|"px4_msgs/SensorGps"| micro_ros_agent[" "] + + style micro_ros_agent fill-opacity:0, stroke-opacity:0; + +``` + +## Service architecture + +The `docker/docker-compose.yaml` file defines the Docker Compose services that support GISNav deployments. The diagram below describes the system architecture through the external interfaces between the services. + +### Docker build contexts + +In the `docker/` folder, you can find a collection of directories roughly corresponding to Docker build contexts, and a number of Docker Compose files defining services that use these build contexts. In case of multi-stage builds, multiple service images can be created from the same build context. The `docker/Makefile` may define additional phony targets for commonly needed tasks such as exposing the X server to containers that have a GUI component. + +### Network isolation + +Isolation of groups of directly unrelated services is provided by allocating dedicated Docker bridge networks to groups of related services. Docker bridge networks have in-built DNS which means the container names resolve to their respective IP addresses on the bridge networks. The container name will equal the service name prefixed with `gisnav-` and suffixed with `-1`. For example, deploying the `mapserver` service using the Compose file will thereby start a container that can be found by any other container on the same network by its hostname `gisnav-mapserver-1`. + +### Shared volumes + +Configuration files, orthoimagery and DEM rasters and other data that is intended to be managed by end users are bind-mounted via separate volumes that are exposed to end-user administration tools. + +### Service topography + +The diagram below depicts the GISNav services topography for SITL simulation that is implemented by Docker Compose. + +Some notes on the service topography: + +- The Application services `gisnav` and `autoheal` have access to both `gis` and `mavlink` networks. +- The Application services, Simulation services, Middleware services, GIS services, and Data services terms are an attempt to identify and give descriptive names to layers that have naturally emerged in the architecture. They have no further description. +- `socat` could also be considered a middleware service but runs non-containerized on the Docker host so it is not included in the Middleware services in the diagram. +- The deployed topography will slightly vary if Docker Compose overrides are used, for example when deploying for HIL instead of SITL simulation. +- GISnav uses `gscam` to publish the ROS `sensor_msgs.msg.CameraInfo` and `sensor_msgs.msg.Image` messages. The camera topics are not published over the MAVROS middleware. + +::: info Todo +- In SITL simulation the serial output from `gisnav` is bridged to `PX4` via TCP on the `mavlink` network so the `mavlink` name is not completely descriptive of the nature of the network (--> change name). With Docker containers, communicating over the network is more convenient and potentially more secure than trying to bridge the serial communication between `px4` and `gisnav` e.g. via binding Docker host virtual serial ports (pseudo-ttys) to the containers. +- Potentially split `mavlink` network into `mavlink` and `ros` networks. For ROS the intention is to use the shared memory device instead of serializing and going through the network stack since we will be passing a lot of (pointers to) images around. +- `docs-volume` not yet implemented, but is intended to contain static documentation (may need a server). +::: + +```mermaid +graph TD + subgraph mavlink ["mavlink"] + mavlink_qgc[qgc] + subgraph simulation ["Simulation Services"] + simulation_px4[px4] + simulation_ardupilot[ardupilot] + end + subgraph middleware ["Middleware Services"] + middleware_mavros[mavros] + middleware_micro_ros_agent[micro-ros-agent] + middleware_gscam[gscam] + end + end + simulation_px4 -->|"/dev/ttyS4 (px4 GPS 2)\ntcp:15000 (socat bridge)\n/dev/ttyS1 (gisnav NMEA)"| application_gisnav + + subgraph gis_mavlink ["gis & mavlink"] + subgraph application ["Application Services"] + application_gisnav[gisnav] + application_autoheal[autoheal] + end + end + subgraph gis ["gis"] + subgraph gis_services ["GIS Services"] + gis_mapserver[mapserver] + gis_qgis[qgis] + end + subgraph data_services ["Data Services"] + gis_postgres[postgres] + end + end + + subgraph admin ["admin"] + homepage[homepage] + fileserver[fileserver] + end + + subgraph volumes ["User managed\nshared volumes"] + gscam_volume[gscam-volume] + gis_maps_volume[maps-volume] + application_gisnav_volume[gisnav-volume] + end + application_docs_volume[docs-volume] + + mavlink_qgc -->|14550/udp\nMAVLink| simulation_px4 + simulation_px4 -->|14540/udp\nMAVLink| middleware_mavros + simulation_px4 -->|8888/udp\nDDS-XRCE | middleware_micro_ros_agent + simulation_px4 -->|5600/udp\nRTP H.264 Video| middleware_gscam + middleware_mavros -->|/dev/shm\nROS 2 Fast DDS| application_gisnav + middleware_micro_ros_agent -->|/dev/shm\nROS 2 Fast DDS| application_gisnav + middleware_gscam -->|/dev/shm\nROS 2 Fast DDS| application_gisnav + application_gisnav -->|5432/tcp| gis_postgres + + application_gisnav -->|80/tcp\nHTTP WMS| gis_mapserver + gis_mapserver -->|80/tcp\nHTTP WMS| gis_qgis + gis_qgis -->|5432/tcp| gis_postgres + gis_mapserver ---|/etc/mapserver| gis_maps_volume + application_gisnav_volume ---|/etc/gisnav| application_gisnav + application_docs_volume ---|/path/to/built/docs| application_gisnav + homepage ---|3000/tcp| fileserver + fileserver ---|"/var/www/filegator/"| volumes + gscam_volume ---|/etc/gscam| middleware_gscam + + application_docs_volume ---|/path/to/docs:ro| homepage + + classDef network fill:transparent,stroke-dasharray:5 5; + class mavlink,gis,gis_mavlink,admin,admin_gis,host network +``` + +### Service descriptions + +The Homepage labels in the `docker/docker-compose.yaml` file have brief descriptions of the purpose of each individual service. These labels are picked up by the `homepage` service and displayed on the [admin portal](/admin-portal). diff --git a/docs/vitepress/docs/test-gisnav.md b/docs/vitepress/docs/test-gisnav.md new file mode 100644 index 00000000..64e779d5 --- /dev/null +++ b/docs/vitepress/docs/test-gisnav.md @@ -0,0 +1,138 @@ +# Test GISNav + +This page describes how you run some of the test suites included with GISNav. Tests can be run locally which is usefule for development, or in a container which is useful as part of a CI pipeline. + +## Prerequisites + +The prerequisites depend on whether you are running the tests for a local installation or on a Docker container: + +### Local + + + +::: tip Development dependencies required +Remember to also install the development dependencies as instructed in the [Python dependencies](/install-locally#install-python-dependencies) section. +::: + +### Docker + +Follow the [Docker Compose deployment instructions](/deploy-with-docker-compose) to build the `gisnav` Docker image and to create the container. + +## Static analysis + +Static analysis runs the pre-commit configuration in the repository root directory on all files, not just the ones staged for commit: + +::: code-group + +```bash [Local] +cd ~/colcon_ws/src/gisnav +make test-static +``` + +```bash [Docker] +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav run gisnav make test-static +``` + +::: + +## Launch tests + +Launch tests using the [ROS launch_testing package](https://index.ros.org/p/launch_testing/) are provided for smoke testing launch files for common launch configurations in the `test.launch` package. They are quick tests that would typically reveal basic issues with the nodes, like a node not starting properly or crashing soon after startup. + + +::: code-group + +```bash [Local] +cd ~/colcon_ws/src/gisnav +make test-launch +``` + +```bash [Docker] +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav run gisnav make test-launch +``` + +::: + +You can also try running only specific launch tests with commands like below: + +::: code-group + +```bash [Local] +cd ~/colcon_ws/src/gisnav +launch_test src/gisnav/gisnav/test/launch/test_default_launch.py +``` + +```bash [Docker] +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav run gisnav launch_test src/gisnav/gisnav/test/launch/test_default_launch.py +``` + +::: +## Unit tests + +::: info Todo +Implement a basic set of unit tests that is useful. + +::: + +## SITL tests + +The SITL tests are powerful automated test suites that simulate GISNav in an end-to-end loop with the autopilot inside a simulated world with simulated but realistic sensor data. + +### Additional prerequisites + +The SITL tests require some additional supporting Docker Compose services to be built and created: + +```bash +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav build px4 +docker compose -p gisnav create px4 +make expose-xhost +``` + +### Run SITL tests + +SITL tests are under the `gisnav/test/sitl` folder. Use the below commands to run the SITL test: + +::: code-group + +```bash [Local] +cd ~/colcon_ws/src/gisnav/gisnav +make test-sitl +``` + +```bash [Docker] +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav run gisnav make test-sitl +``` + +::: + +::: info SITL tests not included in CI +The SITL tests are not run in CI since they would realistically require a GPU-enabled runner, which is inconvenient. +::: + +### Flight log analysis + +The flight logs generated by the SITL tests can be analyzed e.g. with the help of the example code snippets in the Jupyter notebooks in the `test/ulog_analysis` folder. You must first start `jupyter-notebook`: + +```bash +cd ~/colcon_ws/src/gisnav/gisnav/test/sitl/ulog_analysis +jupyter-notebook +``` + +The notebook documents the analysis and displays the results. Download the example ULog files from Google Drive [here](https://drive.google.com/drive/folders/1SmcOV11IJG4qL7Of77mpNICeiLP_9fH7?usp=sharing). + +## Code coverage reports + +To generate and inspect code coverage you can use `coverage.py`. See the [official instructions](https://coverage.readthedocs.io/en/6.4.1/source.html) on how to configure what source files to measure. + +Use the below command to run and inspect a code coverage report for the launch tests for the default launch configuration: + +```bash +cd ~/colcon_ws +python3 -m coverage run --branch --include *gisnav* src/gisnav/gisnav/test/launch/test_default_launch.py +python3 -m coverage report +``` diff --git a/docs/vitepress/docs/troubleshooting.md b/docs/vitepress/docs/troubleshooting.md new file mode 100644 index 00000000..48cefd7b --- /dev/null +++ b/docs/vitepress/docs/troubleshooting.md @@ -0,0 +1,145 @@ +# Troubleshooting + +On this page you will find tips for solving some common problems with the software. + +## X window or display or GUI not appearing + +This might be accompanied by error messages such as the ones below: + +```console +px4-1 | [Err] [RenderEngine.cc:749] Can't open display: :1 +px4-1 | [Wrn] [RenderEngine.cc:89] Unable to create X window. Rendering will be disabled +px4-1 | [Wrn] [RenderEngine.cc:292] Cannot initialize render engine since render path type is NONE. Ignore this warning if rendering has been turned off on purpose. +px4-1 | Authorization required, but no authorization protocol specified +px4-1 | [Wrn] [GuiIface.cc:114] could not connect to display :1 +px4-1 | [Msg] Could not load the Qt platform plugin "xcb" in "" even though it was found. +px4-1 | [Err] [GuiIface.cc:118] This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem. +``` + +### Expose X server + +If the Gazebo, QGroundControl, RViz, or QGIS windows do not appear on your screen soon after [deploying your Docker Compose services](#deploy-with-docker-compose), you may need to expose your X server to your containers. + +::: info `gisnav` required in container names +The scripts here look for containers that have the string `gisnav` in their names. It is important that you use the `-p gisnav` option or `COMPOSE_PROJECT_NAME=gisnav` environment variable when building and creating your containers. + +::: + + + +## Simulation is slow + +### Headless mode + +When developing on a lower performance system or when doing automated testing (e.g., with MAVSDK), you may want to run the Gazebo simulation in headless mode to increase performance: + +::: info Building Compose services +See [Deploy with Docker Compose](/deploy-with-docker-compose) for more information on how to build the Docker Compose services used in the below example. + +::: + +::: code-group + +```bash [PX4] +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav -f docker-compose.yaml -f docker-compose.headless.yaml up px4 +``` + +```bash [ArduPilot] +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav -f docker-compose.yaml -f docker-compose.headless.yaml up ardupilot +``` +::: + +### GPU drivers not available + +Your system might not be using the GPU. Check that CUDA is available: + +```bash +hmakelin@hmakelin-MS-7D48:~/colcon_ws/src/gisnav$ python3 +Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux +>>> import torch +>>> torch.cuda.is_available() +True +``` + +Sometimes this command will not return `True` and possibly raises an error. Try updating your drivers and/or restarting your computer. + +### GPU temperature + +Check your GPU temperature to ensure it's not overheating and therefore being throttled. Overheating might be a more common problem on laptops with less efficient heat discharge. + +## Keypoint match visualization not appearing + +Here we assume the SITL or HIL simulation is working but GISNav itself does not appear to be working since the keypoint match visualization does not appear. + +### Disable SharedMemory for Fast DDS + +If you are not able to establish ROS communication between the `mavros` container and the host, or receive the above error when using the `--network host` option, try disabling SharedMemory for Fast DDS on your host. You can do so by creating an XML configuration and restarting the ROS daemon with the new configuration. You can do so by creating an XML +configuration (e.g., ``disable_shared_memory.xml``) as described in [this comment](https://github.com/eProsima/Fast-DDS/issues/1698#issuecomment-778039676) +or [discussion here](https://stackoverflow.com/questions/65900201/troubles-communicating-with-ros2-node-in-docker-container) and restarting the ROS daemon with the new configuration: + +```bash +export FASTRTPS_DEFAULT_PROFILES_FILE=disable_shared_memory.xml +ros2 daemon stop +ros2 daemon start +``` + +## ArduPilot simulation not working + +::: info Todo +Currently, ArduPilot support is broken, and the simulation is not expected to work. Use PX4 instead. + +::: + +### Disable AppArmor + +::: warning Security implications +Consider the security implications to your system before trying this out. + +::: + +Possibly needed if using `--network host`: If QGroundControl or Gazebo do not seem to be starting when running the containers, you may need to run them image with `--security-opt apparmor:unconfined` or `--privileged` options. + +## Simulator is not receiving NMEA messages + +During SITL simulation, the simulator might not receive the NMEA messages via the virtual serial port (pseudo-tty). The examples here assume you are using the `px4` service for simulation. + +### Check TCP port + +```bash +hmakelin@hmakelin-MS-7D48:~/colcon_ws/src/gisnav/docker$ docker compose -p gisnav exec -it px4 bash +root@669b94309b51:/PX4-Autopilot# tcpdump port 15000 and '(tcp-syn|tcp-ack)!=0' +``` + +### Check serial port + +```bash +hmakelin@hmakelin-MS-7D48:~/colcon_ws/src/gisnav/docker$ docker compose -p gisnav exec -it px4 bash +root@669b94309b51:/PX4-Autopilot# cat /dev/ttyS4 +$GPGGA,090425,3731.4157,N,12215.3002,W,1,12,0.00,45.5,M,0.0,M,,*68 +``` + +## General debugging + +### Run shell inside container + +If you need to do debugging on your [Docker Compose images](/deploy-with-docker-compose) with GUI applications (e.g., Gazebo inside the `px4` service), run bash inside your service container using the following command: + +```bash +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav run px4 bash +``` + +If the container is already running, you must use `exec` instead: + +```bash +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav exec px4 bash +``` + +::: tip `docker` vs `docker compose` +You will probably want to use `docker compose` here instead of `docker` for the GUI applications to work properly, but `docker` will also work +for basic debugging that does not require launching GUI apps. + +::: diff --git a/docs/vitepress/package-lock.json b/docs/vitepress/package-lock.json new file mode 100644 index 00000000..97e7783e --- /dev/null +++ b/docs/vitepress/package-lock.json @@ -0,0 +1,3015 @@ +{ + "name": "gisnav-docs", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "mermaid": "^10.9.0", + "vitepress": "^1.1.4", + "vitepress-plugin-mermaid": "^2.0.16" + } + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", + "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", + "dev": true, + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", + "@algolia/autocomplete-shared": "1.9.3" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", + "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", + "dev": true, + "dependencies": { + "@algolia/autocomplete-shared": "1.9.3" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", + "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", + "dev": true, + "dependencies": { + "@algolia/autocomplete-shared": "1.9.3" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", + "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", + "dev": true, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/cache-browser-local-storage": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.23.3.tgz", + "integrity": "sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg==", + "dev": true, + "dependencies": { + "@algolia/cache-common": "4.23.3" + } + }, + "node_modules/@algolia/cache-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.23.3.tgz", + "integrity": "sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==", + "dev": true + }, + "node_modules/@algolia/cache-in-memory": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.23.3.tgz", + "integrity": "sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==", + "dev": true, + "dependencies": { + "@algolia/cache-common": "4.23.3" + } + }, + "node_modules/@algolia/client-account": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.23.3.tgz", + "integrity": "sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==", + "dev": true, + "dependencies": { + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.23.3.tgz", + "integrity": "sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==", + "dev": true, + "dependencies": { + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/client-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.23.3.tgz", + "integrity": "sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==", + "dev": true, + "dependencies": { + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.23.3.tgz", + "integrity": "sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==", + "dev": true, + "dependencies": { + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/client-search": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.23.3.tgz", + "integrity": "sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==", + "dev": true, + "dependencies": { + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/logger-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.23.3.tgz", + "integrity": "sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==", + "dev": true + }, + "node_modules/@algolia/logger-console": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.23.3.tgz", + "integrity": "sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==", + "dev": true, + "dependencies": { + "@algolia/logger-common": "4.23.3" + } + }, + "node_modules/@algolia/recommend": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.23.3.tgz", + "integrity": "sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==", + "dev": true, + "dependencies": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.23.3.tgz", + "integrity": "sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==", + "dev": true, + "dependencies": { + "@algolia/requester-common": "4.23.3" + } + }, + "node_modules/@algolia/requester-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.23.3.tgz", + "integrity": "sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==", + "dev": true + }, + "node_modules/@algolia/requester-node-http": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.23.3.tgz", + "integrity": "sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==", + "dev": true, + "dependencies": { + "@algolia/requester-common": "4.23.3" + } + }, + "node_modules/@algolia/transporter": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.23.3.tgz", + "integrity": "sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==", + "dev": true, + "dependencies": { + "@algolia/cache-common": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/requester-common": "4.23.3" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@braintree/sanitize-url": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz", + "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==", + "dev": true + }, + "node_modules/@docsearch/css": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.0.tgz", + "integrity": "sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==", + "dev": true + }, + "node_modules/@docsearch/js": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.6.0.tgz", + "integrity": "sha512-QujhqINEElrkIfKwyyyTfbsfMAYCkylInLYMRqHy7PHc8xTBQCow73tlo/Kc7oIwBrCLf0P3YhjlOeV4v8hevQ==", + "dev": true, + "dependencies": { + "@docsearch/react": "3.6.0", + "preact": "^10.0.0" + } + }, + "node_modules/@docsearch/react": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.0.tgz", + "integrity": "sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==", + "dev": true, + "dependencies": { + "@algolia/autocomplete-core": "1.9.3", + "@algolia/autocomplete-preset-algolia": "1.9.3", + "@docsearch/css": "3.6.0", + "algoliasearch": "^4.19.1" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0", + "search-insights": ">= 1 < 3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "optional": true + } + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@mermaid-js/mermaid-mindmap": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/mermaid-mindmap/-/mermaid-mindmap-9.3.0.tgz", + "integrity": "sha512-IhtYSVBBRYviH1Ehu8gk69pMDF8DSRqXBRDMWrEfHoaMruHeaP2DXA3PBnuwsMaCdPQhlUUcy/7DBLAEIXvCAw==", + "dev": true, + "optional": true, + "dependencies": { + "@braintree/sanitize-url": "^6.0.0", + "cytoscape": "^3.23.0", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.1.0", + "d3": "^7.0.0", + "khroma": "^2.0.0", + "non-layered-tidy-tree-layout": "^2.0.2" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", + "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz", + "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", + "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz", + "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz", + "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz", + "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz", + "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz", + "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz", + "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz", + "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz", + "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz", + "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz", + "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz", + "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz", + "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz", + "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.4.0.tgz", + "integrity": "sha512-CxpKLntAi64h3j+TwWqVIQObPTED0FyXLHTTh3MKXtqiQNn2JGcMQQ362LftDbc9kYbDtrksNMNoVmVXzKFYUQ==", + "dev": true + }, + "node_modules/@shikijs/transformers": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.4.0.tgz", + "integrity": "sha512-kzvlWmWYYSeaLKRce/kgmFFORUtBtFahfXRKndor0b60ocYiXufBQM6d6w1PlMuUkdk55aor9xLvy9wy7hTEJg==", + "dev": true, + "dependencies": { + "shiki": "1.4.0" + } + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dev": true, + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", + "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==", + "dev": true + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==", + "dev": true + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true + }, + "node_modules/@types/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==", + "dev": true, + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "dev": true, + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true + }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true + }, + "node_modules/@types/unist": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", + "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==", + "dev": true + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", + "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", + "dev": true + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz", + "integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==", + "dev": true, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.26.tgz", + "integrity": "sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.24.4", + "@vue/shared": "3.4.26", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.26.tgz", + "integrity": "sha512-4CWbR5vR9fMg23YqFOhr6t6WB1Fjt62d6xdFPyj8pxrYub7d+OgZaObMsoxaF9yBUHPMiPFK303v61PwAuGvZA==", + "dev": true, + "dependencies": { + "@vue/compiler-core": "3.4.26", + "@vue/shared": "3.4.26" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.26.tgz", + "integrity": "sha512-It1dp+FAOCgluYSVYlDn5DtZBxk1NCiJJfu2mlQqa/b+k8GL6NG/3/zRbJnHdhV2VhxFghaDq5L4K+1dakW6cw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.24.4", + "@vue/compiler-core": "3.4.26", + "@vue/compiler-dom": "3.4.26", + "@vue/compiler-ssr": "3.4.26", + "@vue/shared": "3.4.26", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.10", + "postcss": "^8.4.38", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.26.tgz", + "integrity": "sha512-FNwLfk7LlEPRY/g+nw2VqiDKcnDTVdCfBREekF8X74cPLiWHUX6oldktf/Vx28yh4STNy7t+/yuLoMBBF7YDiQ==", + "dev": true, + "dependencies": { + "@vue/compiler-dom": "3.4.26", + "@vue/shared": "3.4.26" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.1.3.tgz", + "integrity": "sha512-W8IwFJ/o5iUk78jpqhvScbgCsPiOp2uileDVC0NDtW38gCWhsnu9SeBTjcdu3lbwLdsjc+H1c5Msd/x9ApbcFA==", + "dev": true, + "dependencies": { + "@vue/devtools-kit": "^7.1.3" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.1.3.tgz", + "integrity": "sha512-NFskFSJMVCBXTkByuk2llzI3KD3Blcm7WqiRorWjD6nClHPgkH5BobDH08rfulqq5ocRt5xV+3qOT1Q9FXJrwQ==", + "dev": true, + "dependencies": { + "@vue/devtools-shared": "^7.1.3", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.1.3.tgz", + "integrity": "sha512-KJ3AfgjTn3tJz/XKF+BlVShNPecim3G21oHRue+YQOsooW+0s+qXvm09U09aO7yBza5SivL1QgxSrzAbiKWjhQ==", + "dev": true, + "dependencies": { + "rfdc": "^1.3.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.26.tgz", + "integrity": "sha512-E/ynEAu/pw0yotJeLdvZEsp5Olmxt+9/WqzvKff0gE67tw73gmbx6tRkiagE/eH0UCubzSlGRebCbidB1CpqZQ==", + "dev": true, + "dependencies": { + "@vue/shared": "3.4.26" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.26.tgz", + "integrity": "sha512-AFJDLpZvhT4ujUgZSIL9pdNcO23qVFh7zWCsNdGQBw8ecLNxOOnPcK9wTTIYCmBJnuPHpukOwo62a2PPivihqw==", + "dev": true, + "dependencies": { + "@vue/reactivity": "3.4.26", + "@vue/shared": "3.4.26" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.26.tgz", + "integrity": "sha512-UftYA2hUXR2UOZD/Fc3IndZuCOOJgFxJsWOxDkhfVcwLbsfh2CdXE2tG4jWxBZuDAs9J9PzRTUFt1PgydEtItw==", + "dev": true, + "dependencies": { + "@vue/runtime-core": "3.4.26", + "@vue/shared": "3.4.26", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.26.tgz", + "integrity": "sha512-xoGAqSjYDPGAeRWxeoYwqJFD/gw7mpgzOvSxEmjWaFO2rE6qpbD1PC172YRpvKhrihkyHJkNDADFXTfCyVGhKw==", + "dev": true, + "dependencies": { + "@vue/compiler-ssr": "3.4.26", + "@vue/shared": "3.4.26" + }, + "peerDependencies": { + "vue": "3.4.26" + } + }, + "node_modules/@vue/shared": { + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.26.tgz", + "integrity": "sha512-Fg4zwR0GNnjzodMt3KRy2AWGMKQXByl56+4HjN87soxLNU9P5xcJkstAlIeEF3cU6UYOzmJl1tV0dVPGIljCnQ==", + "dev": true + }, + "node_modules/@vueuse/core": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.9.0.tgz", + "integrity": "sha512-/1vjTol8SXnx6xewDEKfS0Ra//ncg4Hb0DaZiwKf7drgfMsKFExQ+FnnENcN6efPen+1kIzhLQoGSy0eDUVOMg==", + "dev": true, + "dependencies": { + "@types/web-bluetooth": "^0.0.20", + "@vueuse/metadata": "10.9.0", + "@vueuse/shared": "10.9.0", + "vue-demi": ">=0.14.7" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", + "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/integrations": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-10.9.0.tgz", + "integrity": "sha512-acK+A01AYdWSvL4BZmCoJAcyHJ6EqhmkQEXbQLwev1MY7NBnS+hcEMx/BzVoR9zKI+UqEPMD9u6PsyAuiTRT4Q==", + "dev": true, + "dependencies": { + "@vueuse/core": "10.9.0", + "@vueuse/shared": "10.9.0", + "vue-demi": ">=0.14.7" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "*", + "axios": "*", + "change-case": "*", + "drauu": "*", + "focus-trap": "*", + "fuse.js": "*", + "idb-keyval": "*", + "jwt-decode": "*", + "nprogress": "*", + "qrcode": "*", + "sortablejs": "*", + "universal-cookie": "*" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "optional": true + } + } + }, + "node_modules/@vueuse/integrations/node_modules/vue-demi": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", + "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.9.0.tgz", + "integrity": "sha512-iddNbg3yZM0X7qFY2sAotomgdHK7YJ6sKUvQqbvwnf7TmaVPxS4EJydcNsVejNdS8iWCtDk+fYXr7E32nyTnGA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.9.0.tgz", + "integrity": "sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw==", + "dev": true, + "dependencies": { + "vue-demi": ">=0.14.7" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared/node_modules/vue-demi": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", + "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/algoliasearch": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.23.3.tgz", + "integrity": "sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==", + "dev": true, + "dependencies": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-account": "4.23.3", + "@algolia/client-analytics": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-personalization": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/recommend": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "dev": true, + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, + "node_modules/cytoscape": { + "version": "3.29.2", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.29.2.tgz", + "integrity": "sha512-2G1ycU28Nh7OHT9rkXRLpCDP30MKH1dXJORZuBhtEhEW7pKwgPi77ImqlCWinouyE1PNepIOGZBOrE84DG7LyQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "dev": true, + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "dev": true, + "optional": true, + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "dev": true, + "optional": true, + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "dev": true, + "optional": true + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "dev": true, + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dev": true, + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dev": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dev": true, + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dev": true, + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dev": true, + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dev": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dev": true, + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dev": true, + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dev": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dev": true, + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dev": true, + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "dev": true, + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dev": true, + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "dev": true + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dev": true, + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "dev": true + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dev": true, + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "dev": true, + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dev": true, + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dev": true, + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dev": true, + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dev": true, + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dev": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz", + "integrity": "sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==", + "dev": true, + "dependencies": { + "d3": "^7.8.2", + "lodash-es": "^4.17.21" + } + }, + "node_modules/dayjs": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz", + "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dev": true, + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "dev": true, + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dompurify": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.2.tgz", + "integrity": "sha512-hLGGBI1tw5N8qTELr3blKjAML/LY4ANxksbS612UiJyDfyf/2D092Pvm+S7pmeTGJRqvlJkFzBoHBQKgQlOQVg==", + "dev": true + }, + "node_modules/elkjs": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz", + "integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==", + "dev": true + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/focus-trap": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz", + "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==", + "dev": true, + "dependencies": { + "tabbable": "^6.2.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/katex": { + "version": "0.16.10", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz", + "integrity": "sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==", + "dev": true + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "dev": true + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true + }, + "node_modules/magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true + }, + "node_modules/mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mermaid": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.9.0.tgz", + "integrity": "sha512-swZju0hFox/B/qoLKK0rOxxgh8Cf7rJSfAUc1u8fezVihYMvrJAS45GzAxTVf4Q+xn9uMgitBcmWk7nWGXOs/g==", + "dev": true, + "dependencies": { + "@braintree/sanitize-url": "^6.0.1", + "@types/d3-scale": "^4.0.3", + "@types/d3-scale-chromatic": "^3.0.0", + "cytoscape": "^3.28.1", + "cytoscape-cose-bilkent": "^4.1.0", + "d3": "^7.4.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.10", + "dayjs": "^1.11.7", + "dompurify": "^3.0.5", + "elkjs": "^0.9.0", + "katex": "^0.16.9", + "khroma": "^2.0.0", + "lodash-es": "^4.17.21", + "mdast-util-from-markdown": "^1.3.0", + "non-layered-tidy-tree-layout": "^2.0.2", + "stylis": "^4.1.3", + "ts-dedent": "^2.2.0", + "uuid": "^9.0.0", + "web-worker": "^1.2.0" + } + }, + "node_modules/micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/minisearch": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-6.3.0.tgz", + "integrity": "sha512-ihFnidEeU8iXzcVHy74dhkxh/dn8Dc08ERl0xwoMMGqp4+LvRSCgicb+zGqWthVokQKvCSxITlh3P08OzdTYCQ==", + "dev": true + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/non-layered-tidy-tree-layout": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", + "integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==", + "dev": true + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/preact": { + "version": "10.21.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.21.0.tgz", + "integrity": "sha512-aQAIxtzWEwH8ou+OovWVSVNlFImL7xUCwJX3YMqA3U8iKCNC34999fFOnWjYNsylgfPgMexpbk7WYOLtKr/mxg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/rfdc": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", + "dev": true + }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "dev": true + }, + "node_modules/rollup": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", + "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.17.2", + "@rollup/rollup-android-arm64": "4.17.2", + "@rollup/rollup-darwin-arm64": "4.17.2", + "@rollup/rollup-darwin-x64": "4.17.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.17.2", + "@rollup/rollup-linux-arm-musleabihf": "4.17.2", + "@rollup/rollup-linux-arm64-gnu": "4.17.2", + "@rollup/rollup-linux-arm64-musl": "4.17.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2", + "@rollup/rollup-linux-riscv64-gnu": "4.17.2", + "@rollup/rollup-linux-s390x-gnu": "4.17.2", + "@rollup/rollup-linux-x64-gnu": "4.17.2", + "@rollup/rollup-linux-x64-musl": "4.17.2", + "@rollup/rollup-win32-arm64-msvc": "4.17.2", + "@rollup/rollup-win32-ia32-msvc": "4.17.2", + "@rollup/rollup-win32-x64-msvc": "4.17.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "dev": true + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/search-insights": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.13.0.tgz", + "integrity": "sha512-Orrsjf9trHHxFRuo9/rzm0KIWmgzE8RMlZMzuhZOJ01Rnz3D0YBAe+V6473t6/H6c7irs6Lt48brULAiRWb3Vw==", + "dev": true, + "peer": true + }, + "node_modules/shiki": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.4.0.tgz", + "integrity": "sha512-5WIn0OL8PWm7JhnTwRWXniy6eEDY234mRrERVlFa646V2ErQqwIFd2UML7e0Pq9eqSKLoMa3Ke+xbsF+DAuy+Q==", + "dev": true, + "dependencies": { + "@shikijs/core": "1.4.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==", + "dev": true + }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "dev": true + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "dev": true, + "engines": { + "node": ">=6.10" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "dev": true, + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/vite": { + "version": "5.2.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz", + "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==", + "dev": true, + "dependencies": { + "esbuild": "^0.20.1", + "postcss": "^8.4.38", + "rollup": "^4.13.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitepress": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.1.4.tgz", + "integrity": "sha512-bWIzFZXpPB6NIDBuWnS20aMADH+FcFKDfQNYFvbOWij03PR29eImTceQHIzCKordjXYBhM/TjE5VKFTUJ3EheA==", + "dev": true, + "dependencies": { + "@docsearch/css": "^3.6.0", + "@docsearch/js": "^3.6.0", + "@shikijs/core": "^1.3.0", + "@shikijs/transformers": "^1.3.0", + "@types/markdown-it": "^14.0.1", + "@vitejs/plugin-vue": "^5.0.4", + "@vue/devtools-api": "^7.0.27", + "@vueuse/core": "^10.9.0", + "@vueuse/integrations": "^10.9.0", + "focus-trap": "^7.5.4", + "mark.js": "8.11.1", + "minisearch": "^6.3.0", + "shiki": "^1.3.0", + "vite": "^5.2.10", + "vue": "^3.4.25" + }, + "bin": { + "vitepress": "bin/vitepress.js" + }, + "peerDependencies": { + "markdown-it-mathjax3": "^4", + "postcss": "^8" + }, + "peerDependenciesMeta": { + "markdown-it-mathjax3": { + "optional": true + }, + "postcss": { + "optional": true + } + } + }, + "node_modules/vitepress-plugin-mermaid": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/vitepress-plugin-mermaid/-/vitepress-plugin-mermaid-2.0.16.tgz", + "integrity": "sha512-sW0Eu4+1EzRdwZBMGjzwKDsbQiuJIxCy8BlMw7Ur88p9fXalrFYKqZ3wYWLxsFTBipeooFIeanef/xw1P+v7vQ==", + "dev": true, + "optionalDependencies": { + "@mermaid-js/mermaid-mindmap": "^9.3.0" + }, + "peerDependencies": { + "mermaid": "10", + "vitepress": "^1.0.0 || ^1.0.0-alpha" + } + }, + "node_modules/vue": { + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.26.tgz", + "integrity": "sha512-bUIq/p+VB+0xrJubaemrfhk1/FiW9iX+pDV+62I/XJ6EkspAO9/DXEjbDFoe8pIfOZBqfk45i9BMc41ptP/uRg==", + "dev": true, + "dependencies": { + "@vue/compiler-dom": "3.4.26", + "@vue/compiler-sfc": "3.4.26", + "@vue/runtime-dom": "3.4.26", + "@vue/server-renderer": "3.4.26", + "@vue/shared": "3.4.26" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/web-worker": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.3.0.tgz", + "integrity": "sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==", + "dev": true + } + } +} diff --git a/docs/vitepress/package.json b/docs/vitepress/package.json new file mode 100644 index 00000000..f7755cb3 --- /dev/null +++ b/docs/vitepress/package.json @@ -0,0 +1,12 @@ +{ + "scripts": { + "docs:dev": "vitepress dev docs", + "docs:build": "vitepress build docs", + "docs:preview": "vitepress preview docs" + }, + "devDependencies": { + "mermaid": "^10.9.0", + "vitepress": "^1.1.4", + "vitepress-plugin-mermaid": "^2.0.16" + } +} From af220f83b6e1a067f38ee68fa18541f812beef2d Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Tue, 7 May 2024 21:24:21 +0100 Subject: [PATCH 02/30] Update vitepress docs --- docs/vitepress/docs/.vitepress/config.mts | 1 + docs/vitepress/docs/deploy-for-development.md | 75 ++++++++++++++++--- docs/vitepress/docs/generate-documentation.md | 45 +++++++++++ docs/vitepress/docs/jetson-pixhawk.md | 2 +- .../shared/run-in-container-prerequisites.md | 13 ++++ docs/vitepress/docs/test-gisnav.md | 14 +--- 6 files changed, 127 insertions(+), 23 deletions(-) create mode 100644 docs/vitepress/docs/generate-documentation.md create mode 100644 docs/vitepress/docs/shared/run-in-container-prerequisites.md diff --git a/docs/vitepress/docs/.vitepress/config.mts b/docs/vitepress/docs/.vitepress/config.mts index 89f113ae..934e818b 100644 --- a/docs/vitepress/docs/.vitepress/config.mts +++ b/docs/vitepress/docs/.vitepress/config.mts @@ -49,6 +49,7 @@ export default withMermaid({ { text: 'Run ROS nodes', link: '/ros-run-node' }, { text: 'Remap ROS topics', link: '/remap-ros-topics' }, { text: 'Run tests', link: '/test-gisnav' }, + { text: 'Generate documentation', link: '/generate-documentation' }, ] }, { diff --git a/docs/vitepress/docs/deploy-for-development.md b/docs/vitepress/docs/deploy-for-development.md index 2fe472fb..b6600578 100644 --- a/docs/vitepress/docs/deploy-for-development.md +++ b/docs/vitepress/docs/deploy-for-development.md @@ -10,11 +10,31 @@ The recommended way of developing GISNav is to deploy a SITL simulation and supp The easy way to deploy for development is via the Makefile. Use the below commands to run a local ROS 2 default launch configuration of GISNav and to deploy a supporting SITL simulation as Docker Compose services. -```bash +Two Makefile targets are provided for uORB and NMEA output: + +- The `dev-uorb` target uses the `micro-ros-agent` middleware service and `UORBNode` and plays more nicely with PX4 than the NMEA target. +- The `dev-nmea` target uses `socat` running on the Docker host to bridge the serial port output from the `NMEANode` via TCP to the `px4` SITL simulation container. + +::: code-group + +```bash [uORB ] cd ~/colcon_ws/src/gisnav -make -C docker dev +make -C docker dev-uorb ``` +```bash [NMEA] +cd ~/colcon_ws/src/gisnav +make -C docker dev-nmea +``` +::: + +::: tip Use uORB if possible +The PX4 NMEA driver and the NMEA 0183 protocol itself does not support sub-second precision for timestamps, while the PX4 (v1.14) GPS driver seems to have hard-coded a 5 Hz / 200 ms frequency requirement for GPS messages even when using the NMEA protocol. This leads to the secondary NMEA mock GPS often being flagged as unhealthy. For context, see the driver code lines [gps.cpp#L985](https://github.com/PX4/PX4-Autopilot/blob/v1.14.2/src/drivers/gps/gps.cpp#L985), [gps.cpp#L994](https://github.com/PX4/PX4-Autopilot/blob/v1.14.2/src/drivers/gps/gps.cpp#L994) and [nmea.cpp#L545-L553](https://github.com/PX4/PX4-GPSDrivers/blob/release/1.14/src/nmea.cpp#L545-L553). + +The PX4 NMEA driver also hard-codes some variables like the `s_variance_m_s` speed variance to 0 which may lead to failsafes triggering (unverified) if only relying on GISNav for velocity estimates (e.g. when [simulating failure of the primary GPS](/README#simulate-gps-failure)). + +::: + ::: info Prompt for `sudo` access The Docker Compose service containers are hard-coded to use their Docker DNS hostnames when communicating with each other over Docker bridge networks. The Makefile `dev` recipe will add some of these hostnames to the `/etc/hosts` file on your Docker host i.e. the development computer so that your local GISNav installation will be able to any containers it wants to talk to on the host network. You may therefore be prompted for `sudo` access when running the above command. @@ -36,26 +56,62 @@ The Makefile uses the ROS launch system under the hood to define and deploy conf The `default.launch.py` file can be used for all launches. A `dev.launch.py` configuration is provided to include additional nodes that help with development. ::: info Todo -Merge `dev.launch.py` into `default.launch.py` by adding more launch arguments to `default.launch.py` that invoke current `dev.launch.py` functionality +Merge `dev.launch.py` into `default.launch.py` by adding more launch arguments to `default.launch.py`. ::: +To see what launch arguments the launch file accepts, type in the following command: + +```bash +cd ~/colcon_ws +ros2 launch gisnav default.launch.py --show-args +``` + ### Edit local hosts file -TODO +With a local installation of GISNav we cannot rely on the Docker DNS and must find our containers manually. -### Redirecting serial output for SITL simulation +The `dev-base-setup` Makefile target automates finding the relevant container IP addresses and adding them to your `/etc/hosts` file: -The `px4` SITL simulation container listens for NMEA messages at TCP port 15000. GISNav `NMEANode` writes into a serial port, and this serial port traffic must be bridged to the TCP port of the running SITl container to make the mock GPS demo work in SITL simulation. This is done using `socat` which creates a new virtual serial port for you (a pseudo-tty): +```bash +cd ~/colcon_ws/src/gisnav/docker +make dev-base-setup +``` -The Makefile `dev` recipe looks up the IP addresses of the containers of the supporting services and creates a pseudo-tty (virtual serial port) using `socat` to bridge the GISNav serial port output via TCP to the Docker container running the PX4 SITL simulation. +Alternatively, following the below steps will do the same thing. + +`GISNode` needs to know where `gisnav-mapserver-1` is, and `QGISNode` needs to know where `gisnav-postgres-1` is. You can find them with the following commands if the `mapserver` and `postgres` services are running: ```bash -PX4_IP=`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' gisnav-px4-1` +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav start postgres mapserver +POSTGRES_IP=`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' gisnav-postgres-1` +MAPSERVER_IP=`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' gisnav-mapserver-1` +``` + +If these are not yet in your `/etc/hosts` file, add them: + +```bash +echo "$(POSTGRES_IP) gisnav-postgres-1" | sudo tee -a /etc/hosts +echo "$(MAPSERVER_IP) gisnav-mapserver-1" | sudo tee -a /etc/hosts +``` + +### Redirecting serial output for SITL simulation + +The `px4` SITL simulation container listens for NMEA messages at TCP port 15000. GISNav `NMEANode` writes into a serial port, and this serial port traffic must be bridged to the TCP port of the running `px4` container to make the mock GPS demo work in SITL simulation. + +The Makefile `dev-nmea` recipe looks up the IP address of the running `px4` container creates a pseudo-tty (virtual serial port) using `socat` to bridge the GISNav serial port output via TCP to the Docker container running the PX4 SITL simulation. + +```bash +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav start px4 +PX4_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' gisnav-px4-1) socat pty,link=/tmp/gisnav-pty-link,raw,echo=0 tcp:$(PX4_IP):15000 ``` -Assuming you have already installed the `gisnav` colcon package, you can launch with a single command. You need to provide a serial port and an optional baudrate as launch args to the default launch configuration. +### Launch + +You are now ready to launch GISNav via the ROS launch system. ::: code-group ```bash [uORB ] @@ -65,6 +121,7 @@ ros2 launch gisnav default.launch.py protocol:=uorb ```bash [NMEA] cd ~/colcon_ws +PTY_PORT=$(readlink /tmp/gisnav-pty-link) ros2 launch gisnav default.launch.py protocol:=nmea port:=${PTY_PORT} baudrate:=${BAUDRATE:-9600} ``` diff --git a/docs/vitepress/docs/generate-documentation.md b/docs/vitepress/docs/generate-documentation.md new file mode 100644 index 00000000..0b41e82e --- /dev/null +++ b/docs/vitepress/docs/generate-documentation.md @@ -0,0 +1,45 @@ +# Generate documentation + +GISNav uses a two-stage process to generate its documentation. Sphinx is used to first build Markdown from the reStructuredText docstrings in the Python source code, and then VitePress is used to create the final static documentation from the Markdown files. + +## Prerequisites + +### Node.js + +Install Node v18+ on your system by following the [official instructions](https://nodejs.org/en/download). + +### Install GISNav + + + +## Make docs + +::: code-group + +```bash [Local] +cd ~/colcon_ws/src/gisnav +make docs +``` + +```bash [Docker] +cd ~/colcon_ws/src/gisnav +make docs +``` + +::: + +## Build Sphinx documentation + +```bash +cd ~/colcon_ws/src/gisnav/docs +make html +``` + +The HTML documentation will appear in the `~/colcon_ws/src/gisnav/docs/_build/` folder. + +## Serve VitePress documentation + +```bash +cd ~/colcon_ws/src/gisnav/docs/vitepress +npm run docs:dev +``` diff --git a/docs/vitepress/docs/jetson-pixhawk.md b/docs/vitepress/docs/jetson-pixhawk.md index 82105249..cb431b19 100644 --- a/docs/vitepress/docs/jetson-pixhawk.md +++ b/docs/vitepress/docs/jetson-pixhawk.md @@ -1,6 +1,6 @@ # Pixhawk & Jetson Nano HIL -::: info NVIDIA Jetson Nano discontinued +::: tip NVIDIA Jetson Nano discontinued This article was created for an older version of GISNav and since its publication, NVIDIA Jetson Nano has been discontinued. This article is provided for reference until an updated article on HIL simulation is written. ::: diff --git a/docs/vitepress/docs/shared/run-in-container-prerequisites.md b/docs/vitepress/docs/shared/run-in-container-prerequisites.md new file mode 100644 index 00000000..1f02031d --- /dev/null +++ b/docs/vitepress/docs/shared/run-in-container-prerequisites.md @@ -0,0 +1,13 @@ +The prerequisites depend on whether you are running a local installation or using a Docker container. + +**Local** + + + +::: tip Development dependencies required +Remember to also install the development dependencies as instructed in the [Python dependencies](/install-locally#install-python-dependencies) section. +::: + +**Docker** + +Follow the [Docker Compose deployment instructions](/deploy-with-docker-compose) to build the `gisnav` Docker image and to create the container. diff --git a/docs/vitepress/docs/test-gisnav.md b/docs/vitepress/docs/test-gisnav.md index 64e779d5..8e73f6dc 100644 --- a/docs/vitepress/docs/test-gisnav.md +++ b/docs/vitepress/docs/test-gisnav.md @@ -4,19 +4,7 @@ This page describes how you run some of the test suites included with GISNav. T ## Prerequisites -The prerequisites depend on whether you are running the tests for a local installation or on a Docker container: - -### Local - - - -::: tip Development dependencies required -Remember to also install the development dependencies as instructed in the [Python dependencies](/install-locally#install-python-dependencies) section. -::: - -### Docker - -Follow the [Docker Compose deployment instructions](/deploy-with-docker-compose) to build the `gisnav` Docker image and to create the container. + ## Static analysis From be3b1fabdb2925c8b510bd5b2e574ade1b116e06 Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Tue, 7 May 2024 21:27:37 +0100 Subject: [PATCH 03/30] Make front page features descriptive and tone down the unnecessary marketing --- docs/vitepress/docs/index.md | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/docs/vitepress/docs/index.md b/docs/vitepress/docs/index.md index 36b02179..130e8a47 100644 --- a/docs/vitepress/docs/index.md +++ b/docs/vitepress/docs/index.md @@ -28,26 +28,22 @@ features: details: "Provides a precise global position by visually comparing frames from the vehicle's nadir-facing camera to a map of the UAVs approximate global position retrieved from an onboard GIS server." - title: "FOSS with MIT License" - details: "Open source under the permissive MIT license, allowing for free use, modification, and distribution, fostering a community of innovation and improvement." + details: "Open source under the permissive MIT license, allowing for free use, modification, and distribution." - title: "Monocular Camera Compatibility" details: "Compatible with any standard monocular camera, facilitating easy adoption and integration with existing equipment, without requiring specialized hardware." - title: "MAVLink, NMEA and uORB Protocols" - details: "Supports integration with popular autopilot systems like PX4 and ArduPilot through MAVLink, NMEA and uORB protocols, enhancing interoperability and control." + details: "Supports integration with popular autopilot systems like PX4 and ArduPilot through MAVLink, NMEA and uORB protocols." - title: "Secondary GPS Over Serial Port (NMEA)" - details: "Functions as a reliable secondary GPS, easily integrating over serial connections without the need for firmware modifications, enhancing navigational redundancy and safety." + details: "Functions as a reliable secondary GPS, easily integrating over serial connections without the need for firmware modifications." - title: "Simulation with Gazebo" - details: "Includes support for Gazebo simulations, enabling developers to test and refine drone operations in a fully controlled virtual environment, accelerating development cycles and reducing field testing risks." + details: "Includes support for Gazebo simulations, enabling developers to test and refine drone operations in a fully controlled virtual environment." - title: "ROS 2 Integration" - details: "Seamlessly integrates with the ROS 2 ecosystem, providing robust middleware solutions that enhance the functionality and scalability of UAV operations." - - - title: "Live Navigation Updates" - details: "Offers live navigation updates, ensuring UAVs respond promptly to environmental changes and mission updates, crucial for dynamic and unpredictable operating conditions." - + details: "Integrates with the ROS 2 ecosystem, providing easy extensibility." --- From cd460ae47ce070f5e78f0037d7d0bf3b4381be4b Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Tue, 7 May 2024 22:20:15 +0100 Subject: [PATCH 04/30] Build Sphinx generated markdown API reference with VitePress --- .dockerignore | 2 + .gitignore | 2 + docs/_static/css/style.css | 115 --------- docs/_static/fonts/Poppins Medium.ttf | Bin 156480 -> 0 bytes docs/_static/fonts/Poppins Regular.ttf | Bin 158240 -> 0 bytes .../_static/img/gisnav_hil_fmuk66-e_setup.jpg | Bin 78150 -> 0 bytes .../img/gisnav_hil_jetson_nano_setup.jpg | Bin 49233 -> 0 bytes .../img/gisnav_sitl_featureless_buildings.jpg | Bin 112239 -> 0 bytes docs/_static/js/custom.js | 54 ----- .../png/gisnav-website-favicon-color.png | Bin 780 -> 0 bytes docs/_static/svg/logo-no-background-white.svg | 1 - docs/_static/svg/logo-no-background.svg | 1 - docs/index.rst | 51 ---- .../_shared/build_colcon_workspace.rst | 20 -- .../_shared/docker_compose_shutdown.rst | 7 - .../developer_guide/_shared/expose_xhost.rst | 22 -- .../launch_gisnav_with_ros2_launch.rst | 5 - .../build_or_pull_gisnav_docker.rst | 22 -- .../compose_project_name_env_variable.rst | 34 --- .../_shared/prerequisites/docker.rst | 5 - .../_shared/prerequisites/gisnav.rst | 9 - .../_shared/prerequisites/install_locally.rst | 1 - .../_shared/prerequisites/ros.rst | 1 - .../prerequisites/source_workspace.rst | 37 --- .../development/install_gisnav.rst | 103 -------- .../development/system_requirements.rst | 39 ---- .../development/test_gisnav.rst | 163 ------------- docs/pages/developer_guide/index.rst | 38 --- .../integration/gis_server.rst | 219 ------------------ .../integration/modify_params.rst | 27 --- .../integration/ros_messaging.rst | 91 -------- .../offboard/docker_compose.rst | 90 ------- .../offboard/ros2_launch_system.rst | 30 --- .../offboard/run_individual_node.rst | 42 ---- .../offboard/service_orchestration.rst | 190 --------------- .../offboard/troubleshooting.rst | 137 ----------- .../onboard/companion_computer.rst | 98 -------- .../onboard/jetson_pixhawk.rst | 199 ---------------- docs/pages/get_started.rst | 12 - docs/{ => sphinx}/Makefile | 0 docs/{ => sphinx}/conf.py | 7 +- .../api_documentation => sphinx}/index.rst | 2 +- docs/{ => sphinx}/make.bat | 0 .../private/decorators.rst | 0 .../private/transformations.rst} | 0 .../public/bbox_node.rst | 0 .../public/constants.rst | 0 .../public/gis_node.rst | 0 .../public/gisnav.rst | 0 .../public/nmea_node.rst | 0 .../public/pose_node.rst | 0 .../public/qgis_node.rst | 0 .../public/stereo_node.rst | 0 .../public/uorb_node.rst | 0 .../test/launch.rst | 0 .../test/unit.rst | 0 docs/vitepress/docs/.vitepress/config.mts | 126 ++++++---- docs/vitepress/docs/generate-documentation.md | 10 + docs/vitepress/docs/index.md | 2 +- gisnav/setup.py | 1 + 60 files changed, 106 insertions(+), 1909 deletions(-) delete mode 100644 docs/_static/css/style.css delete mode 100644 docs/_static/fonts/Poppins Medium.ttf delete mode 100644 docs/_static/fonts/Poppins Regular.ttf delete mode 100644 docs/_static/img/gisnav_hil_fmuk66-e_setup.jpg delete mode 100644 docs/_static/img/gisnav_hil_jetson_nano_setup.jpg delete mode 100644 docs/_static/img/gisnav_sitl_featureless_buildings.jpg delete mode 100644 docs/_static/js/custom.js delete mode 100644 docs/_static/png/gisnav-website-favicon-color.png delete mode 100644 docs/_static/svg/logo-no-background-white.svg delete mode 100644 docs/_static/svg/logo-no-background.svg delete mode 100644 docs/index.rst delete mode 100644 docs/pages/developer_guide/_shared/build_colcon_workspace.rst delete mode 100644 docs/pages/developer_guide/_shared/docker_compose_shutdown.rst delete mode 100644 docs/pages/developer_guide/_shared/expose_xhost.rst delete mode 100644 docs/pages/developer_guide/_shared/launch_gisnav_with_ros2_launch.rst delete mode 100644 docs/pages/developer_guide/_shared/prerequisites/build_or_pull_gisnav_docker.rst delete mode 100644 docs/pages/developer_guide/_shared/prerequisites/compose_project_name_env_variable.rst delete mode 100644 docs/pages/developer_guide/_shared/prerequisites/docker.rst delete mode 100644 docs/pages/developer_guide/_shared/prerequisites/gisnav.rst delete mode 100644 docs/pages/developer_guide/_shared/prerequisites/install_locally.rst delete mode 100644 docs/pages/developer_guide/_shared/prerequisites/ros.rst delete mode 100644 docs/pages/developer_guide/_shared/prerequisites/source_workspace.rst delete mode 100644 docs/pages/developer_guide/development/install_gisnav.rst delete mode 100644 docs/pages/developer_guide/development/system_requirements.rst delete mode 100644 docs/pages/developer_guide/development/test_gisnav.rst delete mode 100644 docs/pages/developer_guide/index.rst delete mode 100644 docs/pages/developer_guide/integration/gis_server.rst delete mode 100644 docs/pages/developer_guide/integration/modify_params.rst delete mode 100644 docs/pages/developer_guide/integration/ros_messaging.rst delete mode 100644 docs/pages/developer_guide/offboard/docker_compose.rst delete mode 100644 docs/pages/developer_guide/offboard/ros2_launch_system.rst delete mode 100644 docs/pages/developer_guide/offboard/run_individual_node.rst delete mode 100644 docs/pages/developer_guide/offboard/service_orchestration.rst delete mode 100644 docs/pages/developer_guide/offboard/troubleshooting.rst delete mode 100644 docs/pages/developer_guide/onboard/companion_computer.rst delete mode 100644 docs/pages/developer_guide/onboard/jetson_pixhawk.rst delete mode 100644 docs/pages/get_started.rst rename docs/{ => sphinx}/Makefile (100%) rename docs/{ => sphinx}/conf.py (97%) rename docs/{pages/api_documentation => sphinx}/index.rst (97%) rename docs/{ => sphinx}/make.bat (100%) rename docs/{pages/api_documentation => sphinx}/private/decorators.rst (100%) rename docs/{pages/api_documentation/private/messaging.rst => sphinx/private/transformations.rst} (100%) rename docs/{pages/api_documentation => sphinx}/public/bbox_node.rst (100%) rename docs/{pages/api_documentation => sphinx}/public/constants.rst (100%) rename docs/{pages/api_documentation => sphinx}/public/gis_node.rst (100%) rename docs/{pages/api_documentation => sphinx}/public/gisnav.rst (100%) rename docs/{pages/api_documentation => sphinx}/public/nmea_node.rst (100%) rename docs/{pages/api_documentation => sphinx}/public/pose_node.rst (100%) rename docs/{pages/api_documentation => sphinx}/public/qgis_node.rst (100%) rename docs/{pages/api_documentation => sphinx}/public/stereo_node.rst (100%) rename docs/{pages/api_documentation => sphinx}/public/uorb_node.rst (100%) rename docs/{pages/api_documentation => sphinx}/test/launch.rst (100%) rename docs/{pages/api_documentation => sphinx}/test/unit.rst (100%) diff --git a/.dockerignore b/.dockerignore index 67d13c02..f93a95c6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -17,6 +17,8 @@ log/ # Built Sphinx documentation docs/_build/ +docs/sphinx/_build/ +docs/vitepress/docs/_build/ # coverage.py files .coverage* diff --git a/.gitignore b/.gitignore index 1105f95c..cb1490cb 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,8 @@ log/ # Built Sphinx documentation docs/_build/ +docs/sphinx/_build/ +docs/vitepress/docs/_build/ # coverage.py files .coverage* diff --git a/docs/_static/css/style.css b/docs/_static/css/style.css deleted file mode 100644 index 6bf9c9a9..00000000 --- a/docs/_static/css/style.css +++ /dev/null @@ -1,115 +0,0 @@ -/* fonts and text colors */ -@font-face { - font-family: "Poppins"; - src: url('../fonts/Poppins Regular.ttf') format('truetype'); - font-weight: normal; - font-style: normal; -} - -@font-face { - font-family: "PoppinsMedium"; - src: url('../fonts/Poppins Medium.ttf') format('truetype'); - font-weight: normal; - font-style: normal; -} - -:root { - --pst-color-primary: #ff4136; -} - -h1, h2, h3, h4, h5, h6, h7 { - font-family: "PoppinsMedium", sans-serif; -} - -/* theme revert primary color for h1 and h2 */ -h1, h2 { - color: var(--pst-color-text); -} - -body { - font-family: "Poppins", sans-serif; -} - -:root { - --md-text-font: "Poppins"; -} - -img.logo__image { - padding: 6px -} - -html[data-theme="light"] { - --pst-color-primary: #ff4136; - --pst-color-h1: var(--pst-color-text-base); - --pst-color-h2: var(--pst-color-text-base); - - .xref { - --pst-color-primary: none; - } -} - -html[data-theme="dark"] { - --pst-color-primary: #ff4136; - --pst-color-h1: var(--pst-color-text-base); - --pst-color-h2: var(--pst-color-text-base); -} - - -/* navbar box shadow */ -.bd-header { - box-shadow: none; - transition: box-shadow 0.1s ease-in-out; -} - -.bd-header-shadow { - box-shadow: 0 0.125rem 0.25rem 0 var(--pst-color-shadow); -} - -.bd-header-no-shadow { - box-shadow: none; -} - - -/* make sidebar border go away */ -.bd-sidebar-primary { - border-right: none; -} - -/* sidebar scrollbar */ -.bd-sidebar-primary::-webkit-scrollbar { - width: 8px; -} - -.bd-sidebar-primary::-webkit-scrollbar-track { - background: #f1f1f1; -} - -.bd-sidebar-primary::-webkit-scrollbar-thumb { - background: #888; -} - -.bd-sidebar-primary::-webkit-scrollbar-thumb:hover { - background: #555; -} - -@keyframes bounce { - 0% { transform: translateY(0); } - 30% { transform: translateY(20px); } - 100% { transform: translateY(0); } -} - -body.bounce { - animation: bounce 0.5s ease; -} - -/* New bounce animation for the navbar */ -@keyframes navbarBounce { - 0% { transform: translateY(0); } - 30% { transform: translateY(-20px); } - 100% { transform: translateY(0); } -} - -/* Add the bounce animation to the navbar when body has 'bounce' class */ -body.bounce .navbar { - animation: navbarBounce 0.5s ease; -} diff --git a/docs/_static/fonts/Poppins Medium.ttf b/docs/_static/fonts/Poppins Medium.ttf deleted file mode 100644 index e90e87ed69a7ebb8d965ec248fb86286423f103f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 156480 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 diff --git a/docs/_static/img/gisnav_hil_jetson_nano_setup.jpg b/docs/_static/img/gisnav_hil_jetson_nano_setup.jpg deleted file mode 100644 index 9473d5ac32907956b2d1253b8ac0fc67a811e124..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49233 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 Baf<)| diff --git a/docs/_static/img/gisnav_sitl_featureless_buildings.jpg b/docs/_static/img/gisnav_sitl_featureless_buildings.jpg deleted file mode 100644 index 07281f9ba248adaaf5482050005f044dd09cbe4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112239 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 KMNUMnLSTZ62xzMS diff --git a/docs/_static/svg/logo-no-background-white.svg b/docs/_static/svg/logo-no-background-white.svg deleted file mode 100644 index 90465130..00000000 --- a/docs/_static/svg/logo-no-background-white.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/_static/svg/logo-no-background.svg b/docs/_static/svg/logo-no-background.svg deleted file mode 100644 index 48a62236..00000000 --- a/docs/_static/svg/logo-no-background.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index c9128f77..00000000 --- a/docs/index.rst +++ /dev/null @@ -1,51 +0,0 @@ -##################################### -GISNav developer documentation -##################################### -Welcome to GISNav's developer documentation! - -.. card:: Get started - :link: pages/get_started.html - - A quick demonstration of GNSS-free visual navigation with GISNav - -.. card:: Developer guide - :link: pages/developer_guide/index.html - - Instructions on how to integrate GISNav with your own project and how to extend its functionality - -.. card:: API documentation - :link: pages/api_documentation/index.html - - GISNav public API reference for developers - - -Generate documentation -_________________________________________ -To build this Sphinx documentation yourself, first :ref:`Install locally` including -the development dependencies and then run: - -.. code-block:: bash - :caption: Build Sphinx documentation - - cd ~/colcon_ws/src/gisnav - make docs - -The HTML documentation will then appear in the ``~/colcon_ws/src/gisnav/docs/_build/`` folder. - -.. seealso:: - - GISNav is built on ROS 2 and supports PX4 and ArduPilot. You may also be interested in their - documentation: - - * `ROS 2 Documentation `_ - * `PX4 Autopilot User Guide `_ - * `ArduPilot Documentation `_ - -.. toctree:: - :maxdepth: 2 - :hidden: - - pages/get_started - pages/developer_guide/index - pages/api_documentation/index - pages/glossary diff --git a/docs/pages/developer_guide/_shared/build_colcon_workspace.rst b/docs/pages/developer_guide/_shared/build_colcon_workspace.rst deleted file mode 100644 index 4cda05e7..00000000 --- a/docs/pages/developer_guide/_shared/build_colcon_workspace.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. tab-set:: - - .. tab-item:: Entire workspace - :selected: - - .. code-block:: bash - :caption: Build entire workspace - - cd ~/colcon_ws - colcon build - source install/setup.bash - - .. tab-item:: GISNav only - - .. code-block:: bash - :caption: Build GISNav package - - cd ~/colcon_ws - colcon build --packages-select gisnav - source install/setup.bash diff --git a/docs/pages/developer_guide/_shared/docker_compose_shutdown.rst b/docs/pages/developer_guide/_shared/docker_compose_shutdown.rst deleted file mode 100644 index 1d32ec5b..00000000 --- a/docs/pages/developer_guide/_shared/docker_compose_shutdown.rst +++ /dev/null @@ -1,7 +0,0 @@ -To shutdown the services run the following command: - -.. code-block:: bash - :caption: Shutdown all services - - cd ~/colcon_ws/src/gisnav/docker - docker compose -p gisnav down diff --git a/docs/pages/developer_guide/_shared/expose_xhost.rst b/docs/pages/developer_guide/_shared/expose_xhost.rst deleted file mode 100644 index 6d435318..00000000 --- a/docs/pages/developer_guide/_shared/expose_xhost.rst +++ /dev/null @@ -1,22 +0,0 @@ -Expose your :term:`X server` to your Docker containers (see e.g. -`ROS GUI Tutorial `_) with the -following command: - -.. tab-set:: - - .. tab-item:: Recommended - :selected: - - .. code-block:: bash - :caption: Expose X server to containers with names containing ``gisnav`` only - - for containerId in $(docker ps -f name=gisnav -aq); do - xhost +local:$(docker inspect --format='{{ .Config.Hostname }}' $containerId) - done - - .. tab-item:: Easy but less safe - - .. code-block:: bash - :caption: Expose X server to any client - - xhost + diff --git a/docs/pages/developer_guide/_shared/launch_gisnav_with_ros2_launch.rst b/docs/pages/developer_guide/_shared/launch_gisnav_with_ros2_launch.rst deleted file mode 100644 index d7e08dd1..00000000 --- a/docs/pages/developer_guide/_shared/launch_gisnav_with_ros2_launch.rst +++ /dev/null @@ -1,5 +0,0 @@ -.. code-block:: bash - :caption: Launch development configuration - - cd ~/colcon_ws - ros2 launch gisnav dev.launch.py diff --git a/docs/pages/developer_guide/_shared/prerequisites/build_or_pull_gisnav_docker.rst b/docs/pages/developer_guide/_shared/prerequisites/build_or_pull_gisnav_docker.rst deleted file mode 100644 index 5ac51572..00000000 --- a/docs/pages/developer_guide/_shared/prerequisites/build_or_pull_gisnav_docker.rst +++ /dev/null @@ -1,22 +0,0 @@ -* You must have either built the ``gisnav`` Docker image from local sources, or - pulled it it from the container registry: - - .. tab-set:: - - .. tab-item:: Build - :selected: - - .. code-block:: bash - :caption: Build GISNav Docker image - - cd ~/colcon_ws/src/gisnav/docker - docker compose -p gisnav build gisnav - - .. tab-item:: Pull - :selected: - - .. code-block:: bash - :caption: Pull GISNav Docker image - - cd ~/colcon_ws/src/gisnav/docker - docker compose -p gisnav pull gisnav diff --git a/docs/pages/developer_guide/_shared/prerequisites/compose_project_name_env_variable.rst b/docs/pages/developer_guide/_shared/prerequisites/compose_project_name_env_variable.rst deleted file mode 100644 index ad8bfddb..00000000 --- a/docs/pages/developer_guide/_shared/prerequisites/compose_project_name_env_variable.rst +++ /dev/null @@ -1,34 +0,0 @@ -* You must set the ``COMPOSE_PROJECT_NAME`` environment variable to have the - value ``gisnav`` at minimum for the shell that you are running - ``docker compose`` commands from: - - .. tab-set:: - - .. tab-item:: Current shell only - :selected: - - .. code-block:: bash - :caption: Set ``COMPOSE_PROJECT_NAME`` environment variable - - COMPOSE_PROJECT_NAME=gisnav - - .. tab-item:: Current session - - .. code-block:: bash - :caption: Set ``COMPOSE_PROJECT_NAME`` environment variable - - export COMPOSE_PROJECT_NAME=gisnav - - .. tab-item:: Persistent - - .. code-block:: bash - :caption: Set ``COMPOSE_PROJECT_NAME`` environment variable - - echo "export COMPOSE_PROJECT_NAME=gisnav" >> ~/.bashrc - source ~/.bashrc - - .. note:: - The ``COMPOSE_PROJECT_NAME`` environment variable is set to prefix the - service containers with the name ``gisnav``. This is needed by the - script that exposes the X server to find and expose X server to GISNav - containers only (for better security). diff --git a/docs/pages/developer_guide/_shared/prerequisites/docker.rst b/docs/pages/developer_guide/_shared/prerequisites/docker.rst deleted file mode 100644 index 2508c426..00000000 --- a/docs/pages/developer_guide/_shared/prerequisites/docker.rst +++ /dev/null @@ -1,5 +0,0 @@ -* You will need to have the `Docker Compose plugin`_ and `NVIDIA Container Toolkit`_ - installed. - -.. _Docker Compose plugin: https://docs.docker.com/compose/install/linux/ -.. _NVIDIA Container Toolkit: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html diff --git a/docs/pages/developer_guide/_shared/prerequisites/gisnav.rst b/docs/pages/developer_guide/_shared/prerequisites/gisnav.rst deleted file mode 100644 index 904339e2..00000000 --- a/docs/pages/developer_guide/_shared/prerequisites/gisnav.rst +++ /dev/null @@ -1,9 +0,0 @@ -* You will need to have cloned the GISNav source code into your :term:`ROS` - colcon workspace: - - .. code-block:: bash - :caption: Clone GISNav source code into colcon workspace - :substitutions: - - cd ~/colcon_ws/src - git clone --branch |vversion| https://github.com/hmakelin/gisnav.git diff --git a/docs/pages/developer_guide/_shared/prerequisites/install_locally.rst b/docs/pages/developer_guide/_shared/prerequisites/install_locally.rst deleted file mode 100644 index af016fd5..00000000 --- a/docs/pages/developer_guide/_shared/prerequisites/install_locally.rst +++ /dev/null @@ -1 +0,0 @@ -* You must have :ref:`installed GISNav locally `. diff --git a/docs/pages/developer_guide/_shared/prerequisites/ros.rst b/docs/pages/developer_guide/_shared/prerequisites/ros.rst deleted file mode 100644 index 7b40b092..00000000 --- a/docs/pages/developer_guide/_shared/prerequisites/ros.rst +++ /dev/null @@ -1 +0,0 @@ -* Install ROS 2 |ros_version_capitalized| by following the official |ROS 2 install instructions|_ diff --git a/docs/pages/developer_guide/_shared/prerequisites/source_workspace.rst b/docs/pages/developer_guide/_shared/prerequisites/source_workspace.rst deleted file mode 100644 index 3384f9bc..00000000 --- a/docs/pages/developer_guide/_shared/prerequisites/source_workspace.rst +++ /dev/null @@ -1,37 +0,0 @@ -* Setup and source a ROS 2 colcon workspace. To setup the workspace run the - below command: - - .. code-block:: bash - :caption: Create a workspace - - mkdir -p ~/colcon_ws/src/gisnav - - For convenience you might want to source your workspace in your bash profile - if you do a lot of development: - - .. code-block:: bash - :caption: Source workspace in bash profile - :substitutions: - - echo "source /opt/ros/|ros_version|/setup.bash" >> ~/.bashrc - echo "source ~/colcon_ws/install/setup.bash" >> ~/.bashrc - source ~/.bashrc - - If you already have setup a workspace, you can simply source it in your current - shell: - - .. code-block:: bash - :caption: Source workspace in current shell - :substitutions: - - source /opt/ros/|ros_version|/setup.bash - source ~/colcon_ws/install/setup.bash - - Test that you have sourced your workspace by listing available packages: - - .. code-block:: bash - :caption: List packages in workspace - - ros2 pkg list - - If you see a list of packages, your workspace is probably active. diff --git a/docs/pages/developer_guide/development/install_gisnav.rst b/docs/pages/developer_guide/development/install_gisnav.rst deleted file mode 100644 index ba6c23fa..00000000 --- a/docs/pages/developer_guide/development/install_gisnav.rst +++ /dev/null @@ -1,103 +0,0 @@ -Install locally -____________________________________________________ - -This page describes how to install GISNav locally on your development computer. -With a local installation of GISNav it is easy to make changes and re-test the -software without having to rebuild a :term:`Docker` image. - -If you are only interested in running but not necessarily developing GISNav, you -will probably want to use the :ref:`Docker Compose services -` instead of installing locally. - -Prerequisites -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. include:: ../_shared/prerequisites/ros.rst - -.. include:: ../_shared/prerequisites/source_workspace.rst - -Install system dependencies -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Install GISNav system dependencies with the following commands: - -.. code-block:: bash - :caption: Install GISNav system dependencies - :substitutions: - - cd ~/colcon_ws/src - git clone --branch |vversion| https://github.com/hmakelin/gisnav.git - git clone \ - --branch gimbal-protocol-v2-plugin \ - https://github.com/adinkra-labs/mavros_feature_gimbal-protocol-v2-plugin.git \ - mavros - rosdep update - rosdep install --from-paths . -y -r --ignore-src - -.. note:: - If you want to use a ROS distribution that has reached end-of-life (EOL) - like Foxy, you can add the ``--include-eol-distros`` option to - ``rosdep update``. - -Install Python dependencies -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You must install at least the :term:`core` dependencies. If you know you are not -e.g. going to use the :class:`.NMEANode` :term:`extension`, you can skip -installing the extended dependencies. - -The development dependencies are required for e.g. :ref:`generating documentation -` and running :ref:`Launch tests`. They are highly recommended -if you are doing development work on GISNav. - -.. note:: - If you want to use a Python virtual environment for your GISNav Python - dependencies, see `this issue`_ first. We do not provide instructions here - as the workspace might not play nicely with the Python virtual environment. - - .. _this issue: https://github.com/ros2/ros2/issues/1094 - -Install the required and optional Python dependencies with the following commands: - -.. tab-set:: - - .. tab-item:: Core - :selected: - - .. code-block:: bash - :caption: Install GISNav core Python dependencies - - cd ~/colcon_ws/src/gisnav - pip3 install ./gisnav - - .. tab-item:: Extended (optional) - - .. code-block:: bash - :caption: Install GISNav extension Python dependencies - - cd ~/colcon_ws/src/gisnav - pip3 install ./gisnav[nmea_node] - pip3 install ./gisnav[qgis_node] - - You can install the dependencies for any :term:`extension` node - by using its name as a package extra. - - .. tab-item:: Development (optional) - - .. code-block:: bash - :caption: Install GISNav development Python dependencies - - cd ~/colcon_ws/src/gisnav - pip3 install ./gisnav[dev] - -Build colcon workspace -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Build the GISNav package along with other dependencies you have in your colcon -workspace. If you have already built the other dependencies earlier you may -want to skip rebuilding them and build GISNav only to save time: - -.. include:: ../_shared/build_colcon_workspace.rst - -Once GISNav is installed, you can for example try :ref:`launching with the ROS 2 -launch system `. diff --git a/docs/pages/developer_guide/development/system_requirements.rst b/docs/pages/developer_guide/development/system_requirements.rst deleted file mode 100644 index b2a6fbae..00000000 --- a/docs/pages/developer_guide/development/system_requirements.rst +++ /dev/null @@ -1,39 +0,0 @@ -System requirements -____________________________________________________ - -These instructions assume you are running **Ubuntu 22.04 (Jammy Jellyfish)** -on your development machine. Other Ubuntu and Linux releases might also work -with some modifications but are currently unsupported. - -It is strongly recommended that your development machine have an -**NVIDIA GPU with CUDA Toolkit and latest drivers** installed. You can inspect -your NVIDIA driver and CUDA versions with the ``nvidia-smi`` command line utility. -If you don't have it installed, follow the `NVIDIA CUDA Installation Guide for Linux -`_. -The output of the ``nvidia-smi`` command should look something like below: - -.. code-block:: text - :caption: Example output of nvidia-smi command - - hmakelin@hmakelin-Nitro-AN515-54:~$ nvidia-smi - Thu Dec 15 12:00:12 2022 - +-----------------------------------------------------------------------------+ - | NVIDIA-SMI 520.61.05 Driver Version: 520.61.05 CUDA Version: 11.8 | - |-------------------------------+----------------------+----------------------+ - | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | - | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | - | | | MIG M. | - |===============================+======================+======================| - | 0 NVIDIA GeForce ... On | 00000000:01:00.0 On | N/A | - | N/A 73C P8 7W / N/A | 69MiB / 6144MiB | 23% Default | - | | | N/A | - +-------------------------------+----------------------+----------------------+ - - +-----------------------------------------------------------------------------+ - | Processes: | - | GPU GI CI PID Type Process name GPU Memory | - | ID ID Usage | - |=============================================================================| - | 0 N/A N/A 1353 G /usr/lib/xorg/Xorg 22MiB | - | 0 N/A N/A 2210 G /usr/lib/xorg/Xorg 45MiB | - +-----------------------------------------------------------------------------+ diff --git a/docs/pages/developer_guide/development/test_gisnav.rst b/docs/pages/developer_guide/development/test_gisnav.rst deleted file mode 100644 index 4a8905ce..00000000 --- a/docs/pages/developer_guide/development/test_gisnav.rst +++ /dev/null @@ -1,163 +0,0 @@ -Test GISNav -____________________________________________________ - -This section describes how you run some of the test suites included with GISNav. -The instructions here are useful mainly if you are doing development work on -GISNav. - -Prerequisites -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The prerequisites depend on whether you are running the tests for a local -installation or on a Docker container: - -.. tab-set:: - - .. tab-item:: Local - :selected: - - .. include:: ../_shared/prerequisites/install_locally.rst - - .. tab-item:: Docker - - .. include:: ../_shared/prerequisites/gisnav.rst - - .. include:: ../_shared/prerequisites/build_or_pull_gisnav_docker.rst - -Static analysis -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Static analysis runs the pre-commit configuration in the repository root -directory on all files, not just the ones staged for commit: - -.. tab-set:: - - .. tab-item:: Local - :selected: - - .. code-block:: bash - :caption: Run static analysis - - cd ~/colcon_ws/src/gisnav - make test-static - - .. tab-item:: Docker - - .. code-block:: bash - :caption: Run static analysis - - cd ~/colcon_ws/src/gisnav/docker - docker compose -p gisnav run gisnav make test-static - -Launch tests -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Luanch tests using the `ROS launch_testing package -`_ are provided for smoke testing -launch files for common launch configurations in the :py:mod:`test.launch` -package. They are quick tests that would typically reveal basic issues with -the nodes, like a node not starting properly or crashing soon after startup. - -.. tab-set:: - - .. tab-item:: Local - :selected: - - .. code-block:: bash - :caption: Run launch tests - - cd ~/colcon_ws/src/gisnav - make test-launch - - .. tab-item:: Docker - - .. code-block:: bash - :caption: Run launch tests - - cd ~/colcon_ws/src/gisnav/docker - docker compose -p gisnav run gisnav make test-launch - -You can also try running only specific launch tests with commands like below: - -.. code-block:: bash - :caption: Run ROS specific launch tests - - cd ~/colcon_ws/src/gisnav - launch_test src/gisnav/gisnav/test/launch/test_default_launch.py - - -Unit tests -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. todo:: - Implement a basic set of unit tests that is useful - -SITL tests -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The SITL tests are powerful automated test suites that simulate GISNav in an -end-to-end loop with the autopilot inside a simulated world with simulated but -realistic sensor data. - -Additional prerequisites -**************************************************** - -SITL tests require a number of supporting services to successfully complete: - -* You must have made the ``create-offboard-sitl-dev-px4`` Make target: - - .. code-block:: bash - :caption: Create containers for supporting services - - cd ~/colcon_ws/src/gisnav/docker - make build-offboard-sitl-dev-px4 - make create-offboard-sitl-dev-px4 - make expose-xhost - - .. seealso:: - For more information see :ref:`Building, creating and running services` - -Run SITL tests -**************************************************** - -SITL tests are under the ``gisnav/test/sitl`` folder. Use the below ``make`` -command to run the SITL test: - -.. code-block:: bash - - cd ~/colcon_ws/src/gisnav/gisnav - make test-sitl - -.. note:: - The script assumes you have already built the services defined in the - ``docker-compose.yaml`` file. - -Flight Log Analysis -**************************************************** - -The flight log generated by the SITL test can be analyzed e.g. with the help of the example code snippets in the -Jupyter notebooks in the ``test/ulog_analysis`` folder. You must first start ``jupyter-notebook``: - -.. code-block:: bash - - cd ~/colcon_ws/src/gisnav/gisnav/test/sitl/ulog_analysis - jupyter-notebook - -The notebook documents the analysis and displays the results. Download the example ULog files from Google Drive `here -`_. - -Generate code coverage reports -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -To generate and inspect code coverage you can use ``coverage.py``. See the -`official instructions `_ -on how to configure what source files to measure. Use the below command to run -and inspect a code coverage report for the launch tests for the default launch -configuration: - -.. code-block:: bash - :caption: Run and inspect code coverage report - - cd ~/colcon_ws - python3 -m coverage run --branch --include *gisnav* src/gisnav/gisnav/test/launch/test_default_launch.py - python3 -m coverage report diff --git a/docs/pages/developer_guide/index.rst b/docs/pages/developer_guide/index.rst deleted file mode 100644 index 4b7ee358..00000000 --- a/docs/pages/developer_guide/index.rst +++ /dev/null @@ -1,38 +0,0 @@ -************************************************** -User guide -************************************************** -This section provides instruction on how you can integrate GISNav with your project as well as configure and extend -it to match your use case. - -.. warning:: - Do not use this software for real drone flights. GISNav is untested and has only been demonstrated in a - software-in-the-loop (SITL) simulation environment. - -.. toctree:: - :caption: Development - - development/system_requirements - development/install_gisnav - development/test_gisnav - -.. toctree:: - :caption: Off-vehicle simulation - - offboard/service_orchestration - offboard/docker_compose - offboard/ros2_launch_system - offboard/run_individual_node - offboard/troubleshooting - -.. toctree:: - :caption: On-vehicle simulation - - onboard/companion_computer - onboard/jetson_pixhawk - -.. toctree:: - :caption: Integration and configuration - - integration/gis_server - integration/modify_params - integration/ros_messaging diff --git a/docs/pages/developer_guide/integration/gis_server.rst b/docs/pages/developer_guide/integration/gis_server.rst deleted file mode 100644 index b003f1c0..00000000 --- a/docs/pages/developer_guide/integration/gis_server.rst +++ /dev/null @@ -1,219 +0,0 @@ -Setup GIS server -______________________________________________________ - -GISNav requires access to a :term:`GIS` server that serves high resolution -:term:`orthoimagery ` for the approximate :term:`global position` -of the :term:`vehicle`. The orthoimagery consisting of :term:`orthophoto` and -optional :term:`DEM` rasters are requested from a :term:`WMS` service which -allows querying rasters by an arbitrary :term:`bounding box`, and DEM elevation -values by an arbitrary global position via its :term:`GetMap` requests. - -The DEM is optionally used to input ground elevation z-coordinates to the -:term:`PnP` problem solved by GISNav's :term:`pose` estimation algorithm in -:class:`.PoseNode`. If a DEM is not available GISNav simply assumes a planar -ground elevation, which may be sufficient when flying at higher altitudes where -an isometric perspective does not significantly distort the perceived image. - -Example setups -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You are encouraged to self-host an :term:`onboard` GIS server with public domain -orthoimagery because in a realistic scenario the GIS should be embedded onboard -and not depend on an internet connection. For development it may sometimes be -more convenient to proxy an existing commercial tile-based endpoint. - -.. note:: - Commercial web-based map services are often `tile-based`_ (as opposed to WMS) - because it is more efficient to serve pre-rendered tiles than to render unique - rasters for each individual requested bounding box. You will need a WMS proxy - if you decide to go with a tile-based endpoint. - - .. _tile-based: https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames - -.. warning:: - Many commercial services explicitly prohibit the caching of map tiles in - their Terms of Use (ToU), especially if their business model is based on - billing API requests. This is mainly to prevent disintermediation in case - their tiles are redistributed to a large number of end users. - - While caching tiles onboard your own drone is likely not the kind of misuse - targeted by such clauses, you should still make sure you understand the ToU - of the service you are using and that it fits your planned use case. - -If you are fine with using maps for the :term:`KSQL` airport area only, then you -can use the :ref:`provided Docker Compose mapserver service `. -See :ref:`Managing onboard map rasters` for how to add more maps for ``mapserver`` -service. - -Otherwise follow these instructions to self-host a :term:`MapServer` instance: - -.. seealso:: - See :ref:`GIS software` for :term:`free and open-source software (FOSS) - ` alternatives for MapServer - -To follow these instructions you will need: - -* An :term:`AWS` account and AWS CLI, **or alternatively**, an `EarthExplorer`_ - account -* :term:`GDAL` installed - -.. _EarthExplorer: https://earthexplorer.usgs.gov - -In this example we will download :term:`NAIP` imagery and host it using -the `MapServer docker image`_ from Docker Hub. You can download the -GeoTIFF imagery from EarthExplorer, or from the Esri-maintained `AWS S3 bucket`_ -if you already have AWS CLI set up: - -.. _MapServer docker image: https://hub.docker.com/r/camptocamp/mapserver -.. _AWS S3 bucket: https://registry.opendata.aws/naip/ - -.. warning:: - This is a **Requester Pays** bucket and the files can be very large so - download only what you need. - -.. code-block:: bash - :caption: Download a NAIP imagery product from the AWS S3 bucket - - cd ~/gisnav-docker - mkdir -p mapfiles/ - aws s3 cp \ - --request-payer requester \ - s3://naip-source/ca/2020/60cm/rgbir_cog/37122/m_3712230_se_10_060_20200524.tif \ - mapfiles/ - -.. note:: - NAIP imagery is in the public domain. However, you must create an EROS - account to download the rasters from EarthExplorer, or use secondary sources - such as the AWS S3 bucket mentioned above. - - You do not need an account to browse for product IDs with EarthExplorer. - An account is only needed if you want to download products. - -Once you have the imagery, use GDAL to make a ``naip.vrt`` VRT file out of your -downloaded GeoTIFFs: - -.. code-block:: bash - :caption: Use GDAL to create a VRT from TIFF files - - cd mapfiles/ - gdalbuildvrt naip.vrt *.tif - -Once you have your .tif and .vrt files, you can run host them through a MapServer -container: - -.. code-block:: bash - :caption: Serve the map layer using the MapServer Docker image - - export MAPSERVER_PATH=/etc/mapserver - docker run \ - -p 80:80 \ - -v $PWD/mapfiles/:$MAPSERVER_PATH/:ro \ - camptocamp/mapserver - -Test your MapServer WMS service by opening the capabilities XML in your browser: - -.. code-block:: bash - :caption: Launch a WMS GetCapabilities request in Firefox - - firefox "http://localhost:80/?map=/etc/mapserver/wms.map&service=WMS&request=GetCapabilities" - -GIS software -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If you want to run your own GIS server or WMS proxy, you may want to consider -e.g. these :term:`FOSS` options: - -* :term:`MapServer` - -* `GeoServer`_ (full-fledged OGC-compliant GIS server) - -* `Mapnik`_ and `MapProxy`_ - -.. _GeoServer: https://geoserver.org -.. _Mapnik: https://mapnik.org -.. _MapProxy: https://mapproxy.org - -Orthoimagery and DEMs -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If you do not want to use commercial (=not free) high-resolution imagery, various -national agencies often provide country-specific aerial imagery in the public -domain or with public-domain-like licensing terms. You should look for imagery -available in :term:`GDAL` supported formats with coverage for your flight mission -region. These may be provided as downloadable products or through -:term:`OGC`-compliant web services such as :term:`WMS` or :term:`WMTS`. - -Below are just a few examples of national agencies providing high-resolution -orthoimagery that should be suitable for use with GISNav: - -* `USGS High Resolution Orthoimagery`_ (USA) -* `Environment Agency Vertical Aerial Photography`_ (United Kingdom) -* `NLS orthophotos`_ (Finland) - -.. _USGS High Resolution Orthoimagery: https://www.usgs.gov/centers/eros/science/usgs-eros-archive-aerial-photography-high-resolution-orthoimagery-hro -.. _Environment Agency Vertical Aerial Photography: https://www.data.gov.uk/dataset/4921f8a1-d47e-458b-873b-2a489b1c8165/vertical-aerial-photography -.. _NLS orthophotos: https://www.maanmittauslaitos.fi/en/maps-and-spatial-data/expert-users/product-descriptions/orthophotos - -.. note:: - If you have a drone, you can also use readily available `photogrammetry`_ - software to create your own maps for your local region of interest. - -.. _photogrammetry: https://en.wikipedia.org/wiki/Photogrammetry - -Rasterizing vector data -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In some cases useful map data is not directly provided in raster but in vector -format. The GISNav ``mapserver`` service uses vector-format elevation data from -`OSM Buildings`_ to determine building heights in the simulation area to improve -accuracy* of pose estimates especially at lower flight altitudes where the -perceived planarity of the terrain is lower. For an example on how the vector -data is rasterized using GDAL, see this `old mapserver service Dockerfile`_. - -.. note:: - \*The GISNav SITL demo simulation does not actually benefit from the building - height data because the simulated KSQL Airport model buildings are all - featureless black blocks. See :ref:`SITL simulation quirks` for more - information. - -.. _OSM Buildings: https://osmbuildings.org/ -.. _mapserver service Dockerfile: https://github.com/hmakelin/gisnav/blob/v0.65.0/docker/mapserver/Dockerfile - -SITL simulation quirks with DEMs -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The :term:`KSQL` :term:`Gazebo` world buildings in the SITL simulation demo are -featureless grey blocks, so any pose estimation model will most likely not use -them for matching. This means any building elevation data (see :ref:`Rasterizing -vector data`) will not technically be used to improve pose estimates in the -SITL simulation. The below figure illustrates how :term:`LoFTR` finds keypoints -at an even density throughout the simulated vehicle's field of view except on the -featureless buildings. - -.. figure:: ../../../_static/img/gisnav_sitl_featureless_buildings.jpg - - LoFTR does not find keypoints on featureless buildings or terrain (SITL - simulation) - -Managing onboard map rasters -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -A shared volume is used to provide a way for external file manager services -to add and delete maps onboard. The MapServer static mapfile points to a VRT -file which is automatically regenerated whenever a change is detected on the -shared volume which contains the source raster files. - -A sample :term:`FileGator` based ``fileserver`` service is defined in the -``docker/docker-compose.yaml`` for managing maps on the shared volume. The -application is not necessary - e.g. ``scp`` could also be used instead. - -.. mermaid:: - - graph TB - subgraph mapserver - note1[inotify automatically extracts GDAL supported\nformats and regenerates VRT file to which\nthe default mapfile points] - subgraph SharedVolume[Shared volume] - /etc/mapserver/maps/imagery - /etc/mapserver/maps/dem - end - note2[mapfile and VRT file not on shared volume.\nOnly external user managed map rasters.] - end - fileserver[External file manager] -->|add/delete| SharedVolume - GDAL[GDAL supported raster formats] -->|.zip, .jp2, .tif, .tiff, .ecw, etc.| SharedVolume diff --git a/docs/pages/developer_guide/integration/modify_params.rst b/docs/pages/developer_guide/integration/modify_params.rst deleted file mode 100644 index a32e543f..00000000 --- a/docs/pages/developer_guide/integration/modify_params.rst +++ /dev/null @@ -1,27 +0,0 @@ -Modify ROS parameters -____________________________________________________ - -When deploying GISNav :ref:`with Make `, -:ref:`with Docker Compose `, :ref:`using the -ROS 2 launch system `, or :ref:`when running individual -ROS nodes `, you can provide overrides for the ROS -parameters at launch by modifying the node-specific YAML files in the -``gisnav/launch/params/`` folder. - -For example, to provide parameter overrides to -:class:`.GISNode` at launch, you would modify the ``gisnav/launch/params/gis_node.yaml`` -file: - -.. literalinclude:: ../../../../gisnav/launch/params/gis_node.yaml - :caption: :class:`.GISNode` ROS parameter configuration at launch - :language: yaml - -See the :class:`.GISNode` API reference for the -hard-coded default values and for an exhaustive list of all ROS parameters -declared by the node (not everything is necessarily included in the YAML -file). - -.. note:: - You should at least configure your :ref:`GIS server` WMS endpoint ``wms_url`` - in the ``launch/params/gis_node.yaml`` file unless you are using the ``mapserver`` - Docker Compose service, in which case the defaults should work. diff --git a/docs/pages/developer_guide/integration/ros_messaging.rst b/docs/pages/developer_guide/integration/ros_messaging.rst deleted file mode 100644 index c3a35a0a..00000000 --- a/docs/pages/developer_guide/integration/ros_messaging.rst +++ /dev/null @@ -1,91 +0,0 @@ -Remap ROS topics -____________________________________________________ -The natural way to integrate GISNav with other systems is via `ROS 2 -`_. GISNav depends on ROS 2 for both external and -internal communication. If you have a ROS 2 node on the same network, you can -talk to GISNav. - -Core data flow graph -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Take a look at the :term:`core` node ROS topography diagram to understand -how the ROS messages flow through the application: - -.. mermaid:: - :caption: Nodes and published messages - - graph TB - MAVROS -->|"NavSatFix"| BBoxNode - MAVROS -->|"GimbalDeviceAttitudeStatus"| BBoxNode - - subgraph core["GISNav core nodes"] - BBoxNode -->|"BoundingBox"| GISNode - GISNode -->|"OrthoImage"| StereoNode - StereoNode -->|"MonocularStereoImage"| PoseNode - StereoNode -->|"OrthoStereoImage"| PoseNode - end - - subgraph extension["GISNav extension nodes"] - NMEANode["NMEANode"] - end - - subgraph robot_localization - ekf["ekf_localization_node"] -->|"Odometry"| NMEANode - end - - gscam ---->|"Image"| StereoNode - - PoseNode -->|"PoseStamped"| ekf - - -.. todo:: - - * From BBoxNode, publish map to ``base_link`` and ``base_link`` to ``camera`` - transformations separately to simplify implementation and reduce amount - of maintained code. - * Implement :term:`REP 105` properly (currently only partially implemented). - -Remapping ROS 2 topics -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -To integrate GISNav with your own :term:`ROS` system, you will likely have to do -some topic name remapping. See the examples below on how to :ref:`launch -` and :ref:`run ` GISNav ROS -nodes with remapped topic names: - -.. tab-set:: - - .. tab-item:: ros2 launch - :selected: - - The below diff is an example remapping for the camera topics for :class:`.StereoNode`: - - .. literalinclude:: ../../../../gisnav/launch/examples/base_camera_topic_remap.launch.py - :diff: ../../../../gisnav/launch/base.launch.py - :caption: Camera topic name remap in a launch file - :language: python - - To launch the example base configuration with remapped topics: - - .. code-block:: bash - :caption: Launch topic name remap configuration - - ros2 launch gisnav examples/base_camera_topic_remap.launch.py - - .. tab-item:: ros2 run - - The below command launches camera topics for :class:`.StereoNode`: - - .. code-block:: bash - :caption: Camera topic name remapping example using ``ros2 run`` - - cd ~/colcon_ws - ros2 run gisnav transform_node --ros-args --log-level info \ - --params-file src/gisnav/launch/params/transform_node.yaml \ - -r camera/camera_info:=camera_info \ - -r camera/image_raw:=image - -Note on camera topics -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -:term:`GSCam` is in GISNav to publish the :class:`sensor_msgs.msg.CameraInfo` -and :class:`sensor_msgs.msg.Image` messages. The camera topics are not published -over the :term:`MAVROS` middleware. diff --git a/docs/pages/developer_guide/offboard/docker_compose.rst b/docs/pages/developer_guide/offboard/docker_compose.rst deleted file mode 100644 index be6f1c0a..00000000 --- a/docs/pages/developer_guide/offboard/docker_compose.rst +++ /dev/null @@ -1,90 +0,0 @@ -Deploy with Docker Compose -____________________________________________________ - -GISNav uses :term:`Docker Compose` to define the services that constitute its -different deployment configurations. The services are -:ref:`orchestrated by a Makefile ` for convenience and -for increased ease of adoption. - -This page describes how these services are built and deployed individually to -help you customize GISNav's deployments beyond what the Makefile offers. - -Prerequisites -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. include:: ../_shared/prerequisites/docker.rst - -.. include:: ../_shared/prerequisites/gisnav.rst - -.. include:: ../_shared/prerequisites/compose_project_name_env_variable.rst - - -.. include:: ../../../../docker/DOCKER.rst - -Example deployments -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The interdependencies between different services are hard-coded into the -|Docker Compose file|_ and typically you will need to explicitly start -only a few services unless you also want to start optional services. - -Mock GPS demo -******************************************** - -To deploy the mock :term:`GPS` demonstration introduced on the :ref:`Get Started` -page locally, ensure your development system satisfies all the :ref:`prerequisites -` and then follow the below steps to create, start, and shutdown -the required containers. - - -.. code-block:: bash - :caption: Build images and create containers - - cd ~/colcon_ws/src/gisnav/docker - docker compose -p gisnav create --build gisnav - -.. include:: ../_shared/expose_xhost.rst - -.. code-block:: bash - :caption: Start containers - - cd ~/colcon_ws/src/gisnav/docker - docker compose -p gisnav start gisnav - -.. include:: ../_shared/docker_compose_shutdown.rst - -Local development -******************************************** - -When deploying for local development, the difference to -:ref:`deploying the Get Started demonstration ` is that -we do not include the ``gisnav`` service which is assumed to be -:ref:`launched separately from a local development version `: - -.. code-block:: bash - :caption: Build images and create containers - - cd ~/colcon_ws/src/gisnav/docker - docker compose create --build \ - px4 \ - rviz \ - qgis - -.. include:: ../_shared/expose_xhost.rst - -.. code-block:: bash - :caption: Start containers - - cd ~/colcon_ws/src/gisnav/docker - docker compose start \ - px4 \ - rviz \ - qgis - -After you have your supporting services deployed you would typically -:ref:`use the ROS 2 launch system ` to launch your -locally installed development version of GISNav: - -.. include:: ../_shared/launch_gisnav_with_ros2_launch.rst - -.. include:: ../_shared/docker_compose_shutdown.rst diff --git a/docs/pages/developer_guide/offboard/ros2_launch_system.rst b/docs/pages/developer_guide/offboard/ros2_launch_system.rst deleted file mode 100644 index b7e56ac3..00000000 --- a/docs/pages/developer_guide/offboard/ros2_launch_system.rst +++ /dev/null @@ -1,30 +0,0 @@ -Use ROS 2 launch system -____________________________________________________ - -:term:`ROS 2` provides a launch system for deploying applications consisting of -multiple nodes with preconfigured parameters. - -This page describes how GISNav uses the launch system to help you when -developing locally or when making custom deployments. - -Prerequisites -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. include:: ../_shared/prerequisites/install_locally.rst - -Build colcon workspace -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. include:: ../_shared/build_colcon_workspace.rst - -Launch GISNav for local development -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Assuming you have already installed the ``gisnav`` colcon package, you can launch with a single command: - -.. include:: ../_shared/launch_gisnav_with_ros2_launch.rst - -.. seealso:: - See the `Creating Launch Files`_ tutorial for more information on the ROS 2 launch system - - .. _Creating Launch Files: https://docs.ros.org/en/foxy/Tutorials/Intermediate/Launch/Creating-Launch-Files.html diff --git a/docs/pages/developer_guide/offboard/run_individual_node.rst b/docs/pages/developer_guide/offboard/run_individual_node.rst deleted file mode 100644 index 6ae34dec..00000000 --- a/docs/pages/developer_guide/offboard/run_individual_node.rst +++ /dev/null @@ -1,42 +0,0 @@ -Run individual ROS nodes -____________________________________________________ - -You can run individual :term:`ROS` nodes and optionally provide values for -the ROS parameters from the :term:`YAML` files in the ``launch/params/`` -folder with the example commands presented here. - -The commands here are useful for debugging and development only. For complete -functional deployments you should look into deploying GISNav -:ref:`with Make `, :ref:`with Docker Compose -`, or :ref:`using the ROS 2 launch system -`. - -Prerequisites -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. include:: ../_shared/prerequisites/install_locally.rst - -Run node with custom log level -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can change the log level with the ``--log-level`` argument: - -.. code-block:: bash - :caption: Run :class:`.GISNode` with ``info`` log level - - cd ~/colcon_ws - ros2 run gisnav gis_node --ros-args \ - --log-level info - -Run node with custom parameter values -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can provide your custom ROS parameter values at launch in a :term:`YAML` -file using the ``--params-file`` argument: - -.. code-block:: bash - :caption: Run :class:`.GISNode` with ROS parameter file - - cd ~/colcon_ws - ros2 run gisnav gis_node --ros-args \ - --params-file src/gisnav/gisnav/launch/params/gis_node.yaml diff --git a/docs/pages/developer_guide/offboard/service_orchestration.rst b/docs/pages/developer_guide/offboard/service_orchestration.rst deleted file mode 100644 index 5d0f9950..00000000 --- a/docs/pages/developer_guide/offboard/service_orchestration.rst +++ /dev/null @@ -1,190 +0,0 @@ -Deploy using Makefile -____________________________________________________ -The easiest way to deploy GISNav and its :ref:`supporting Docker Compose services -` is using :term:`Make`. A :ref:`Target naming scheme` -has been devised to describe the different types of available deployments. Read -below how the scheme works and try out some of the example commands to understand -how GISNav's supporting services are orchestrated. - -Prerequisites -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. include:: ../_shared/prerequisites/docker.rst - -.. include:: ../_shared/prerequisites/gisnav.rst - -Target naming scheme -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The ``docker/Makefile`` defines a number of phony Make targets corresponding to GISNav -deployment configurations. Generally, the make targets are named according to the -following scheme: - -.. code-block:: bash - :caption: Make target naming scheme - - cd ~/colcon_ws/src/gisnav - make -C docker ----- - -In the naming scheme, the various qualifiers determine the type of deployment: - -.. list-table:: - :widths: 25 75 - :header-rows: 1 - - * - Qualifier - - Description - * - ``build/create/up`` - - Designates what Docker Compose operation should be applied to the services - * - ``onboard/offboard`` - - Designates whether the target is intended for :term:`onboard` or :term:`offboard` use - * - ``sitl/hil`` - - Designates whether the target is for :term:`SITL` or :term:`HIL` simulation - * - ``middleware`` - - *Optional:* Indicates that the target is an intermediate target consisting of middleware - * - ``dev/test`` - - *Optional:* Indicates that the target is intended for development or testing - * - ``px4/ardupilot`` - - Designates whether the target is intended to be used with :term:`PX4` or - :term:`ArduPilot` firmware - -There are some constraints: for example the qualifier ``offboard`` can only occur -together with the ``sitl`` qualifier. For an exhaustive list of supported -combinations of qualifiers, see the source code: - -.. dropdown:: See Makefile source code - :icon: code - - .. literalinclude:: ../../../../docker/Makefile - :caption: Docker Compose service orchestration with Make - :language: makefile - -.. warning:: - If your :term:`offboard` computer is ``arm64`` based, the ``Makefile`` targets - and the ``mapserver`` Docker image especially may not work. For now, you - should look at the :ref:`Deploy with Docker Compose` page if you are - interested in offboard simulation on ``arm64`` systems. - -Building, creating and running services -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The deployments consist of multiple services that can take a long time to build. -Therefore it is recommended to do the building of the images and creating of the -containers separately from the running so that all services start at the same -time: - -.. code-block:: bash - :caption: Build images - - cd ~/colcon_ws/src/gisnav - make -C docker build-offboard-sitl-test-dev-px4 - -.. code-block:: bash - :caption: Create containers - - make -C docker create-offboard-sitl-test-dev-px4 - -You will need to expose your X server to your GUI containers. The ``expose-xhost`` -recipe will expose your X server only to the containers that need access: - -.. code-block:: bash - :caption: Expose X server to containers - - make -C docker expose-xhost - -Once you have successfully run a container once, subsequent starts of the same -container will be much faster, especially for the SITL simulation services like -``px4``. You should therefore probably not create new containers with ``up`` and -``down``, but rather restart the existing ones to have all services start very -quickly: - -.. code-block:: bash - :caption: Start containers - - make start - -.. code-block:: bash - :caption: Stop containers - - make stop - - -Example deployments -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This section lists some of commonly needed deployments that you can use to get -started. - -Mock GPS demo -******************************************** -To deploy the :ref:`Get Started` demonstration locally, run the following command. - -.. code-block:: bash - :caption: Deploy GISNav mock GPS demo - - cd ~/colcon_ws/src/gisnav - make -C docker demo - -.. note:: - Building all the constituent services can take a very long time, and some - services may already start before others have finished building on the - first attempt. Instead of using the one-liner above, you might want to try - :ref:`Building, creating and running services` successively. - -Local development -******************************************** -Use the following command to deploy GISNav supporting services when developing -locally (i.e. running all supporting services but not GISNav itself inside a -Docker container): - -.. tab-set:: - - .. tab-item:: PX4 - :selected: - - .. code-block:: bash - :caption: Deploy GISNav supporting services for local development - - cd ~/colcon_ws/src/gisnav - make -C docker up-offboard-sitl-dev-px4 - - .. tab-item:: ArduPilot - - .. code-block:: bash - :caption: Deploy GISNav supporting services for local development - - cd ~/colcon_ws/src/gisnav - make -C docker up-offboard-sitl-dev-ardupilot - -After the supporting services are running, you will probably want to try -running your :ref:`local installation ` of GISNav to -test your latest changes to the source code: - -.. include:: ../_shared/launch_gisnav_with_ros2_launch.rst - -Offboard SITL -******************************************** -Use the following command to run GISNav supporting services :term:`offboard` -in headless mode for automated :term:`SITL` testing. - -.. tab-set:: - - .. tab-item:: PX4 - :selected: - - .. code-block:: bash - :caption: Deploy GISNav supporting services for automated SITL testing - - cd ~/colcon_ws/src/gisnav - make -C docker up-offboard-sitl-test-px4 - - .. tab-item:: ArduPilot - - .. code-block:: bash - :caption: Deploy GISNav supporting services for automated SITL testing - - cd ~/colcon_ws/src/gisnav - make -C docker up-offboard-sitl-test-ardupilot - -.. note:: - In headless mode you will not see a :term:`GCS` :term:`GUI` so you will - need e.g. :term:`MAVSDK` to control the :term:`vehicle`. diff --git a/docs/pages/developer_guide/offboard/troubleshooting.rst b/docs/pages/developer_guide/offboard/troubleshooting.rst deleted file mode 100644 index 8375a618..00000000 --- a/docs/pages/developer_guide/offboard/troubleshooting.rst +++ /dev/null @@ -1,137 +0,0 @@ -Troubleshooting -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Windows or GUI not appearing -************************************************ - -Expose xhost -________________________________________________ - -If the :term:`Gazebo`, :term:`QGroundControl`, :term:`RViz` or :term:`QGIS` -windows do not appear on your screen soon after -:ref:`deploying your Docker Compose services `, you -may need to expose your ``xhost`` to your containers. - -.. include:: ../_shared/expose_xhost.rst - -Simulation is slow -************************************************ - -Headless mode -________________________________________________ - -When developing on a lower performance system or when doing automated testing -(e.g. with :term:`MAVSDK`) you may want to run the :term:`Gazebo` simulation -in headless mode to increase performance: - -.. note:: - See :ref:`Deploy with Docker Compose` for more information on how to build - the Docker Compose services used in the below example. - -.. tab-set:: - - .. tab-item:: PX4 - :selected: - - .. code-block:: bash - :caption: Run Gazebo in headless mode - - cd ~/colcon_ws/src/gisnav/docker - docker compose -f docker-compose.headless.yaml up px4 - - .. tab-item:: ArduPilot - - .. code-block:: bash - :caption: Run Gazebo in headless mode - - cd ~/colcon_ws/src/gisnav/docker - docker compose -f docker-compose.headless.yaml up ardupilot - -GPU drivers not available -________________________________________________ - -Your system might not be using the GPU. Check that CUDA is available: - -.. code-block:: - :caption: Check CUDA availability - - hmakelin@hmakelin-MS-7D48:~/colcon_ws/src/gisnav$ python3 - Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux - Type "help", "copyright", "credits" or "license" for more information. - >>> import torch - >>> torch.cuda.is_available() - True - -Sometimes this command will not return ``True`` and possibly raises an error. Try -updating your drivers and/or restarting your computer. - -GPU temperature -________________________________________________ - -Check your :term:`GPU` temperature to ensure it's not overheating and not -being throttled. - -Match visualization not appearing -************************************************ - -Here we assume the :term:`SITL` or :term:`HIL` simulation is working but -GISNav itself does not appear to be working since the match visualization -does not appear. - -Disable SharedMemory for Fast DDS -________________________________________________ - - [RTPS_TRANSPORT_SHM Error] Failed init_port fastrtps_port7412: - open_and_lock_file failed -> Function open_port_internal - -If you are not able to establish :term:`ROS` communication between the ``mavros`` container -and the host, or receive the above error when using the ``--network host`` option, try -disabling SharedMemory for Fast DDS **on your host**. You can do so by creating an XML -configuration (e.g., ``disable_shared_memory.xml``) as described in `this comment`_ -or discussion `here`_ and restarting the ROS daemon with the new configuration: - -.. _this comment: https://github.com/eProsima/Fast-DDS/issues/1698#issuecomment-778039676 -.. _here: https://stackoverflow.com/questions/65900201/troubles-communicating-with-ros2-node-in-docker-container - -.. code-block:: bash - - export FASTRTPS_DEFAULT_PROFILES_FILE=disable_fastrtps.xml - ros2 daemon stop - ros2 daemon start - -ArduPilot simulation not working -************************************************ - -.. todo:: - Currently ArduPilot support is broken and the simulation is not expected - to work. Use PX4 instead. - -Disable AppArmor -________________________________________________ - -.. warning:: - Consider the security implications to your system before trying this out. - -Possibly needed if using ``--network host``: If QGroundControl or Gazebo do -not seem to be starting when running the containers, you may need to run them -image with ``--security-opt apparmor:unconfined`` or ``--privileged`` options. - -General debugging -************************************************ - -Run shell inside container -________________________________________________ - -If you need to do debugging on your -:ref:`Docker Compose images ` with :term:`GUI` -applications (e.g. :term:`Gazebo` inside the ``px4`` service), run bash inside your -service container using the following command: - -.. code-block:: bash - :caption: Run bash inside px4 service container - - cd ~/colcon_ws/src/gisnav/docker - docker compose -p gisnav run px4 bash - -You will need to use ``docker compose`` here instead of ``docker`` to for the -GUI applications to work properly. diff --git a/docs/pages/developer_guide/onboard/companion_computer.rst b/docs/pages/developer_guide/onboard/companion_computer.rst deleted file mode 100644 index 703b55ba..00000000 --- a/docs/pages/developer_guide/onboard/companion_computer.rst +++ /dev/null @@ -1,98 +0,0 @@ -Jetson Nano SITL -____________________________________________________ - -.. todo:: - GISNav ``v0.65.0`` now comes with ``arm64`` images. Update section on QEMU. - -This page contains instructions on how to to run a GISNav on an :term:`onboard` -:term:`Jetson Nano` computer while a :term:`SITL` simulation runs on a development -computer to get a better idea of performance in a real use case. - -Connect to your Jetson Nano -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -These instructions assume that your Jetson Nano is connected to the same network -as your desktop computer. This could be done e.g. by connecting them to the -same WiFi (need dongle for Jetson Nano), or by connecting them by Ethernet cable. - -For connecting via Ethernet cable, see the below example. The below example is -compatible with the :ref:`Pixhawk HIL` example that exapnds on it by adding an -:term:`FMU`. - -.. tab-set:: - - .. tab-item:: Diagram - :selected: - - .. mermaid:: - - graph TB - subgraph "Laptop" - Laptop_ETH[Ethernet Port] - end - subgraph "Jetson Nano" - Nano_USB_1[USB Port] - Nano_USB_2[USB Port] - Nano_micro_USB[Micro USB Port] - Nano_HDMI[HDMI Port] - Nano_ETH[Ethernet] - end - Socket[Wall Socket] - Display[External Display] - Mouse[USB Mouse] - Keyboard[USB Keyboard] - Nano_micro_USB ---|Micro USB Power| Socket - Nano_HDMI ---|HDMI| Display - Nano_USB_1 ---|USB| Mouse - Nano_USB_2 ---|USB| Keyboard - Nano_ETH ---|To Laptop ETH| Laptop_ETH - - .. tab-item:: Picture - - .. figure:: ../../../_static/img/gisnav_hil_jetson_nano_setup.jpg - - Jetson Nano connected to laptop via micro-USB and Ethernet. Power supply from wall socket. - -.. seealso:: - Example instructions with screenshots for connecting via both WiFi and Ethernet - cable can also be found e.g. `here `_. - -Deploy offboard services -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Log into your development computer and deploy the services required for the -SITL simulation: - -.. code-block:: bash - :caption: Deploy Gazebo SITL simulation on development computer - - cd ~/colcon_ws/src/gisnav - make -C docker up-offboard-sitl-px4 - -Deploy onboard services -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Then log into your Jetson Nano and install `QEMU`_ emulators to make the -``linux/amd64`` images run on the ``linux/arm64`` Jetson Nano: - -.. code-block:: bash - :caption: Enable running ``amd64`` images on ``arm64`` architecture - - docker run --privileged --rm tonistiigi/binfmt --install all - -.. _QEMU: https://docs.docker.com/build/building/multi-platform/#building-multi-platform-images - -Then deploy the onboard services on the Jetson Nano: - -.. code-block:: bash - :caption: Run GISNav and GIS server on onboard computer - - cd ~/colcon_ws/src/gisnav - make -C docker up-onboard-sitl-px4 - -You should now have the SITL simulation and QGgroundControl running on your -offboard development computer, while ``gisnav``, ``mapserver``, ``autoheal``, -and the middleware (``mavros``) run on your Jetson Nano. If you have your network -setup correctly, the middleware on the Jetson Nano will connect to the simulated -autopilot on your development computer and receive the needed ROS messages for -GISNav to consume. diff --git a/docs/pages/developer_guide/onboard/jetson_pixhawk.rst b/docs/pages/developer_guide/onboard/jetson_pixhawk.rst deleted file mode 100644 index 1d092aeb..00000000 --- a/docs/pages/developer_guide/onboard/jetson_pixhawk.rst +++ /dev/null @@ -1,199 +0,0 @@ -Pixhawk & Jetson Nano HIL -____________________________________________________ - -.. todo:: - GISNav ``v0.65.0`` now comes with ``arm64`` images. Update section on QEMU. - -This section provides an example on how to run GISNav on a :term:`Jetson Nano` -in a :term:`PX4` :term:`HIL` simulation on a :term:`Pixhawk` :term:`FMU`. This -example uses the `NXP FMUK66-E board`_ as an example but any `PX4 supported board`_ -should work. - -.. _NXP FMUK66-E board: https://docs.px4.io/main/en/flight_controller/nxp_rddrone_fmuk66.html -.. _PX4 supported board: https://px4.io/autopilots/ - -.. warning:: - Keep the propellers **off** your drone throughout the HIL simulation. - -Prerequisites -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. include:: ../launch/_prerequisites_gisnav.rst - -Connect Jetson Nano and Pixhawk -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In this example we will power the Pixhawk from the development computer via USB -and the Jetson Nano from a wall socket via a DC adapter to avoid having to handle -LiPo batteries. In a more realistic setup you would supply power to both boards -from the :term:`onboard` battery. - -Follow the below steps and diagram to setup your HIL simulation hardware: - -* **Install a bootloader on your Pixhawk board if your board does not yet have - one.** See your board manufacturer's instructions on how to load one onto your - specific board. -* Connect your Jetson Nano to your development computer via Ethernet cable - (see :ref:`Jetson Nano SITL` for more information). -* Connect your Jetson Nano to a wall socket using a micro-USB power adapter. -* Connect your Pixhawk board to your development computer via micro-USB cable. -* Connect your Pixhawk board to your Jetson Nano via TELEM1 (set PX4 - ``XRCE_DDS_0_CFG`` parameter value to ``101``) using a USB to UART converter. - -.. tab-set:: - - .. tab-item:: Diagram - :selected: - - .. mermaid:: - - graph TB - subgraph "FMUK66-E (FMU)" - subgraph "TELEM1" - FMU_TELEM1_RX[RX] - FMU_TELEM1_TX[TX] - FMU_TELEM1_GND[GND] - end - FMU_USB[micro-USB Port] - end - subgraph "Development computer" - Laptop_ETH[Ethernet Port] - Laptop_USB[USB Port] - end - subgraph "Jetson Nano" - Nano_USB[USB Port x4] - Nano_micro_USB[Micro USB Port] - Nano_HDMI[HDMI Port] - Nano_ETH[Ethernet] - end - subgraph "USB to UART Converter" - Converter_RX[RX] - Converter_TX[TX] - Converter_GND[GND] - Converter_USB[USB] - end - Socket[Wall Socket] - subgraph "Optional (can also use RDP or VNC)" - Display[External Display] - Mouse[USB Mouse] - Keyboard[USB Keyboard] - end - FMU_TELEM1_TX ---|To UART RX| Converter_RX - FMU_TELEM1_RX ---|To UART TX| Converter_TX - FMU_TELEM1_GND ---|To UART GND| Converter_GND - FMU_USB ---|To Dev Computer USB| Laptop_USB - Converter_USB ---|To Nano USB| Nano_USB - Nano_micro_USB ---|Micro USB Power| Socket - Nano_HDMI ---|HDMI| Display - Nano_USB ---|USB| Mouse - Nano_USB ---|USB| Keyboard - Nano_ETH ---|To Dev Computer ETH| Laptop_ETH - - .. tab-item:: Picture - - .. figure:: ../../../_static/img/gisnav_hil_fmuk66-e_setup.jpg - - * NXP FMUK66-E (FMU) board connected to laptop via micro-USB and to Jetson - Nano via TELEM1. - - * FMU draws power from laptop via micro-USB, and Jetson Nano from wall - socket via dedicated micro-USB DC adapter, so no LiPo batteries needed. - - * Connection from FMU to Jetson Nano via TELEM1 serial port using USB to - UART converter. See `FMUK66-E revision C pin layout`_ for how to wire - the TELEM1 JST-GH connector (only GND, RX and TX used here). - - * Other wires as per `manufacturer's instructions`_, except for missing - telemetry radio. - - .. note:: - The TX from one board connects to the RX of the other board and vice - versa. - - .. _manufacturer's instructions: https://nxp.gitbook.io/hovergames/userguide/assembly/connecting-all-fmu-wires - .. _FMUK66-E revision C pin layout: https://nxp.gitbook.io/hovergames/rddrone-fmuk66/connectors/telemetry-1 - - -Install QEMU emulators -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Install `QEMU`_ emulators on your Jetson Nano to make ``linux/amd64`` images -run on the ``linux/arm64`` Jetson Nano: - -.. code-block:: bash - :caption: Install QEMU emulators - - docker run --privileged --rm tonistiigi/binfmt --install all - -.. _QEMU: https://docs.docker.com/build/building/multi-platform/#building-multi-platform-images - -Upload PX4 firmware -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -See the `PX4 uploading firmware instructions`_ for how to upload your development version of PX4 onto your Pixhawk -board. To find the ``make`` target for your specific board, list all options with the ``make list_config_targets`` -command: - -.. _PX4 uploading firmware instructions: https://docs.px4.io/main/en/dev_setup/building_px4.html#uploading-firmware-flashing-the-board - -.. code-block:: bash - :caption: List PX4 supported FMU boards - - cd ~/colcon_ws/src/gisnav/docker - export COMPOSE_PROJECT_NAME=gisnav - docker compose run px4 make list_config_targets - -Then choose your appropriate board for the following examples. We are going to -choose ``nxp_fmuk66-e_default`` for this example: - -.. code-block:: bash - :caption: Upload PX4 firmware to FMU - - export COMPOSE_PROJECT_NAME=gisnav - #docker compose run px4 git submodule update --recursive - docker compose run px4 make distclean - docker compose run px4 make nxp_fmuk66-e_default upload - -Deploy offboard services -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The following steps to deploy the :term:`offboard` services are based on the -official `PX4 HIL simulation instructions`_. The ``px4`` Docker compose service -has a custom ``iris_hitl`` model and a ``hitl_iris_ksql_airport.world`` Gazebo world -that we are going to use in this example: - -.. _PX4 HIL simulation instructions: https://docs.px4.io/main/en/simulation/hitl.html - -.. code-block:: bash - :caption: Deploy HIL simulation offboard services - - export COMPOSE_PROJECT_NAME=gisnav - #docker compose run px4 make clean - docker compose run -e DONT_RUN=1 px4 make px4_sitl_default gazebo-classic - docker compose run px4 source Tools/simulation/gazebo-classic/setup_gazebo.bash $(pwd) $(pwd)/build/px4_sitl_default - docker compose run px4 gazebo Tools/simulation/gazebo-classic/sitl_gazebo-classic/worlds/hitl_iris_ksql_airport.world - - # Important: Start QGroundControl last - docker compose up qgc - -After deploying the HIL simulation, adjust the settings via the :term:`QGC` -application as follows: - -* Precisely match the ``COM_RC_IN_MODE`` parameter setting if mentioned in the - instructions. -* Ensure that you have HITL enabled in QGC Safety settings. -* Ensure you have the virtual joystick enabled in QGC General settings. - -Deploy onboard services -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Once you have the HIL simulation running, login to your Jetson Nano and deploy -the onboard services: - -.. code-block:: bash - :caption: Deploy HIL simulation onboard services - - mkdir -p ~/colcon_ws/src - cd ~/colcon_ws/src - git clone https://github.com/hmakelin/gisnav.git - cd ~/colcon_ws/src/gisnav - make -C docker up-onboard-hil-px4 diff --git a/docs/pages/get_started.rst b/docs/pages/get_started.rst deleted file mode 100644 index 62f2c68b..00000000 --- a/docs/pages/get_started.rst +++ /dev/null @@ -1,12 +0,0 @@ -************************************************** -Get started -************************************************** -.. - Skip GitHub video link (:start-after: .mp4) - -.. video:: https://user-images.githubusercontent.com/22712178/187902004-480397cc-460f-4d57-8ed7-13f4e9bb3757.mp4 - :width: 100% - -.. include:: ../../README.md - :parser: myst_parser.sphinx_ - :start-after: .mp4 diff --git a/docs/Makefile b/docs/sphinx/Makefile similarity index 100% rename from docs/Makefile rename to docs/sphinx/Makefile diff --git a/docs/conf.py b/docs/sphinx/conf.py similarity index 97% rename from docs/conf.py rename to docs/sphinx/conf.py index 513a4604..b0ad8219 100644 --- a/docs/conf.py +++ b/docs/sphinx/conf.py @@ -8,7 +8,7 @@ from typing import Optional, Union from xml.etree import ElementTree -sys.path.insert(0, os.path.abspath("../gisnav")) +sys.path.insert(0, os.path.abspath("../../gisnav")) # -- Version information ----------------------------------------------------- @@ -67,7 +67,9 @@ def parse_package_data(cls, package_file: str) -> PackageData: raise FileNotFoundError(f"Could not find package file at {package_file}.") -package_data = PackageData.parse_package_data(os.path.abspath("../gisnav/package.xml")) +package_data = PackageData.parse_package_data( + os.path.abspath("../../gisnav/package.xml") +) # -- Project information ----------------------------------------------------- @@ -93,6 +95,7 @@ def parse_package_data(cls, package_file: str) -> PackageData: "sphinxcontrib.mermaid", "sphinx_copybutton", "sphinx_substitution_extensions", + "sphinx_markdown_builder", ] # Add any paths that contain templates here, relative to this directory. diff --git a/docs/pages/api_documentation/index.rst b/docs/sphinx/index.rst similarity index 97% rename from docs/pages/api_documentation/index.rst rename to docs/sphinx/index.rst index e518cf94..f8f28650 100644 --- a/docs/pages/api_documentation/index.rst +++ b/docs/sphinx/index.rst @@ -48,4 +48,4 @@ what you are looking for. :caption: Private API private/decorators - private/messaging + private/transformations diff --git a/docs/make.bat b/docs/sphinx/make.bat similarity index 100% rename from docs/make.bat rename to docs/sphinx/make.bat diff --git a/docs/pages/api_documentation/private/decorators.rst b/docs/sphinx/private/decorators.rst similarity index 100% rename from docs/pages/api_documentation/private/decorators.rst rename to docs/sphinx/private/decorators.rst diff --git a/docs/pages/api_documentation/private/messaging.rst b/docs/sphinx/private/transformations.rst similarity index 100% rename from docs/pages/api_documentation/private/messaging.rst rename to docs/sphinx/private/transformations.rst diff --git a/docs/pages/api_documentation/public/bbox_node.rst b/docs/sphinx/public/bbox_node.rst similarity index 100% rename from docs/pages/api_documentation/public/bbox_node.rst rename to docs/sphinx/public/bbox_node.rst diff --git a/docs/pages/api_documentation/public/constants.rst b/docs/sphinx/public/constants.rst similarity index 100% rename from docs/pages/api_documentation/public/constants.rst rename to docs/sphinx/public/constants.rst diff --git a/docs/pages/api_documentation/public/gis_node.rst b/docs/sphinx/public/gis_node.rst similarity index 100% rename from docs/pages/api_documentation/public/gis_node.rst rename to docs/sphinx/public/gis_node.rst diff --git a/docs/pages/api_documentation/public/gisnav.rst b/docs/sphinx/public/gisnav.rst similarity index 100% rename from docs/pages/api_documentation/public/gisnav.rst rename to docs/sphinx/public/gisnav.rst diff --git a/docs/pages/api_documentation/public/nmea_node.rst b/docs/sphinx/public/nmea_node.rst similarity index 100% rename from docs/pages/api_documentation/public/nmea_node.rst rename to docs/sphinx/public/nmea_node.rst diff --git a/docs/pages/api_documentation/public/pose_node.rst b/docs/sphinx/public/pose_node.rst similarity index 100% rename from docs/pages/api_documentation/public/pose_node.rst rename to docs/sphinx/public/pose_node.rst diff --git a/docs/pages/api_documentation/public/qgis_node.rst b/docs/sphinx/public/qgis_node.rst similarity index 100% rename from docs/pages/api_documentation/public/qgis_node.rst rename to docs/sphinx/public/qgis_node.rst diff --git a/docs/pages/api_documentation/public/stereo_node.rst b/docs/sphinx/public/stereo_node.rst similarity index 100% rename from docs/pages/api_documentation/public/stereo_node.rst rename to docs/sphinx/public/stereo_node.rst diff --git a/docs/pages/api_documentation/public/uorb_node.rst b/docs/sphinx/public/uorb_node.rst similarity index 100% rename from docs/pages/api_documentation/public/uorb_node.rst rename to docs/sphinx/public/uorb_node.rst diff --git a/docs/pages/api_documentation/test/launch.rst b/docs/sphinx/test/launch.rst similarity index 100% rename from docs/pages/api_documentation/test/launch.rst rename to docs/sphinx/test/launch.rst diff --git a/docs/pages/api_documentation/test/unit.rst b/docs/sphinx/test/unit.rst similarity index 100% rename from docs/pages/api_documentation/test/unit.rst rename to docs/sphinx/test/unit.rst diff --git a/docs/vitepress/docs/.vitepress/config.mts b/docs/vitepress/docs/.vitepress/config.mts index 934e818b..b7b74fb1 100644 --- a/docs/vitepress/docs/.vitepress/config.mts +++ b/docs/vitepress/docs/.vitepress/config.mts @@ -14,52 +14,94 @@ export default withMermaid({ nav: [ { text: 'Home', link: '/' }, { text: 'Guide', link: '/README' }, - { text: 'API Reference', link: '/api-reference' } + { text: 'API Reference', link: '/_build/sphinx/markdown/index' } ], outline: { level: 'deep' }, - sidebar: [ - { - text: 'SITL simulation', - items: [ - { text: 'Run mock GPS demo', link: '/README' }, - { text: 'Deploy with Docker Compose', link: '/deploy-with-docker-compose' }, - ] - }, - { - text: 'HIL simulation', - items: [ - { text: 'Jetson & Pixhawk', link: '/jetson-pixhawk' } - ] - }, - { - text: 'Configuration', - items: [ - { text: 'Admin portal', link: '/admin-portal' }, - { text: 'Setup GIS server', link: '/setup-gis-server' }, - ] - }, - { - text: 'Development', - items: [ - { text: 'Install locally', link: '/install-locally' }, - { text: 'Deploy for development', link: '/deploy-for-development' }, - { text: 'System architecture', link: '/system-architecture' }, - { text: 'Run ROS nodes', link: '/ros-run-node' }, - { text: 'Remap ROS topics', link: '/remap-ros-topics' }, - { text: 'Run tests', link: '/test-gisnav' }, - { text: 'Generate documentation', link: '/generate-documentation' }, - ] - }, - { - items: [ - { text: 'Troubleshooting', link: '/troubleshooting' }, - { text: 'Glossary', link: '/glossary' }, - { text: 'License', link: '/LICENSE' }, - ] - } - ], + sidebar: { + '/': [ + { + text: 'SITL simulation', + items: [ + { text: 'Run mock GPS demo', link: '/README' }, + { text: 'Deploy with Docker Compose', link: '/deploy-with-docker-compose' }, + ] + }, + { + text: 'HIL simulation', + items: [ + { text: 'Jetson & Pixhawk', link: '/jetson-pixhawk' } + ] + }, + { + text: 'Configuration', + items: [ + { text: 'Admin portal', link: '/admin-portal' }, + { text: 'Setup GIS server', link: '/setup-gis-server' }, + ] + }, + { + text: 'Development', + items: [ + { text: 'Install locally', link: '/install-locally' }, + { text: 'Deploy for development', link: '/deploy-for-development' }, + { text: 'System architecture', link: '/system-architecture' }, + { text: 'Run ROS nodes', link: '/ros-run-node' }, + { text: 'Remap ROS topics', link: '/remap-ros-topics' }, + { text: 'Run tests', link: '/test-gisnav' }, + { text: 'Generate documentation', link: '/generate-documentation' }, + ] + }, + { + items: [ + { text: 'Troubleshooting', link: '/troubleshooting' }, + { text: 'Glossary', link: '/glossary' }, + { text: 'License', link: '/LICENSE' }, + ] + } + ], + '/_build/sphinx/markdown/': [ + { + text: 'Core nodes', + items: [ + { text: 'BBoxNode', link: '/_build/sphinx/markdown/public/bbox_node' }, + { text: 'GISNode', link: '/_build/sphinx/markdown/public/gis_node' }, + { text: 'StereoNode', link: '/_build/sphinx/markdown/public/stereo_node' }, + { text: 'PoseNode', link: '/_build/sphinx/markdown/public/pose_node' }, + ] + }, + { + text: 'Extension nodes', + items: [ + { text: 'UORBNode', link: '/_build/sphinx/markdown/public/uorb_node' }, + { text: 'NMEANode', link: '/_build/sphinx/markdown/public/nmea_node' }, + { text: 'QGISNode', link: '/_build/sphinx/markdown/public/qgis_node' }, + ] + }, + { + text: 'Static API', + items: [ + { text: 'Constants', link: '/_build/sphinx/markdown/public/constants' }, + { text: 'Entry points', link: '/_build/sphinx/markdown/public/gisnav' }, + ] + }, + { + text: 'Testing', + items: [ + { text: 'Unit tests', link: '/_build/sphinx/markdown/test/unit' }, + { text: 'Launch tests', link: '/_build/sphinx/markdown/test/launch' }, + ] + }, + { + text: 'Private API', + items: [ + { text: 'Decorators', link: '/_build/sphinx/markdown/private/decorators' }, + { text: 'Transformations', link: '/_build/sphinx/markdown/test/transformations' }, + ] + }, + ] + }, socialLinks: [ { icon: 'github', link: 'https://github.com/hmakelin/gisnav' } diff --git a/docs/vitepress/docs/generate-documentation.md b/docs/vitepress/docs/generate-documentation.md index 0b41e82e..b4c51d8d 100644 --- a/docs/vitepress/docs/generate-documentation.md +++ b/docs/vitepress/docs/generate-documentation.md @@ -30,11 +30,21 @@ make docs ## Build Sphinx documentation + +Make HTML documentation for viewing in web browser: + ```bash cd ~/colcon_ws/src/gisnav/docs make html ``` +Make Markdown documentation for processing with VitePress: + +```bash +cd ~/colcon_ws/src/gisnav/docs +sphinx-build -M markdown ./sphinx vitepress/docs/_build/sphinx_ +``` + The HTML documentation will appear in the `~/colcon_ws/src/gisnav/docs/_build/` folder. ## Serve VitePress documentation diff --git a/docs/vitepress/docs/index.md b/docs/vitepress/docs/index.md index 130e8a47..39c3d733 100644 --- a/docs/vitepress/docs/index.md +++ b/docs/vitepress/docs/index.md @@ -15,7 +15,7 @@ hero: link: "/README" - theme: alt text: "API Reference" - link: "/api-examples" + link: "/_build/sphinx/markdown/index" features: - title: "GNSS-free navigation" diff --git a/gisnav/setup.py b/gisnav/setup.py index 6fe4b6b2..544862d1 100644 --- a/gisnav/setup.py +++ b/gisnav/setup.py @@ -142,6 +142,7 @@ def parse_package_data(cls, package_file: str) -> PackageData: "Sphinx-Substitution-Extensions", "sphinxcontrib-mermaid", "sphinxcontrib-video", + "sphinx-markdown-builder==0.6.6", "Sphinx", "types-PyYAML", ], From d1267dca3c2a2bdd31f297819d8660cae5421a0f Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Wed, 8 May 2024 00:09:14 +0100 Subject: [PATCH 05/30] Remove incorrect statements on NMEA timestamp precision --- docs/vitepress/docs/deploy-for-development.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/vitepress/docs/deploy-for-development.md b/docs/vitepress/docs/deploy-for-development.md index b6600578..c696fbab 100644 --- a/docs/vitepress/docs/deploy-for-development.md +++ b/docs/vitepress/docs/deploy-for-development.md @@ -29,9 +29,7 @@ make -C docker dev-nmea ::: ::: tip Use uORB if possible -The PX4 NMEA driver and the NMEA 0183 protocol itself does not support sub-second precision for timestamps, while the PX4 (v1.14) GPS driver seems to have hard-coded a 5 Hz / 200 ms frequency requirement for GPS messages even when using the NMEA protocol. This leads to the secondary NMEA mock GPS often being flagged as unhealthy. For context, see the driver code lines [gps.cpp#L985](https://github.com/PX4/PX4-Autopilot/blob/v1.14.2/src/drivers/gps/gps.cpp#L985), [gps.cpp#L994](https://github.com/PX4/PX4-Autopilot/blob/v1.14.2/src/drivers/gps/gps.cpp#L994) and [nmea.cpp#L545-L553](https://github.com/PX4/PX4-GPSDrivers/blob/release/1.14/src/nmea.cpp#L545-L553). - -The PX4 NMEA driver also hard-codes some variables like the `s_variance_m_s` speed variance to 0 which may lead to failsafes triggering (unverified) if only relying on GISNav for velocity estimates (e.g. when [simulating failure of the primary GPS](/README#simulate-gps-failure)). +The PX4 NMEA driver hard-codes some variables like the `s_variance_m_s` speed variance to 0 which may lead to failsafes triggering (unverified) if only relying on GISNav for velocity estimates (e.g. when [simulating failure of the primary GPS](/README#simulate-gps-failure)). ::: From 9485a8af51f376cfafe13198c2f052edaef5e331 Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Wed, 8 May 2024 18:42:37 +0100 Subject: [PATCH 06/30] Update GIS server section --- docs/vitepress/docs/.vitepress/config.mts | 19 ++++--- docs/vitepress/docs/setup-gis-server.md | 62 ++++++++++++---------- docs/vitepress/docs/system-architecture.md | 3 +- 3 files changed, 46 insertions(+), 38 deletions(-) diff --git a/docs/vitepress/docs/.vitepress/config.mts b/docs/vitepress/docs/.vitepress/config.mts index b7b74fb1..746e121c 100644 --- a/docs/vitepress/docs/.vitepress/config.mts +++ b/docs/vitepress/docs/.vitepress/config.mts @@ -4,8 +4,8 @@ import { withMermaid } from 'vitepress-plugin-mermaid' // https://vitepress.dev/reference/site-config //export default defineConfig({ export default withMermaid({ - title: "GISNav", - description: "GISNav documentation", + title: 'GISNav', + description: 'GISNav documentation', lastUpdated: true, cleanUrls: true, metaChunk: true, @@ -79,13 +79,6 @@ export default withMermaid({ { text: 'QGISNode', link: '/_build/sphinx/markdown/public/qgis_node' }, ] }, - { - text: 'Static API', - items: [ - { text: 'Constants', link: '/_build/sphinx/markdown/public/constants' }, - { text: 'Entry points', link: '/_build/sphinx/markdown/public/gisnav' }, - ] - }, { text: 'Testing', items: [ @@ -97,7 +90,13 @@ export default withMermaid({ text: 'Private API', items: [ { text: 'Decorators', link: '/_build/sphinx/markdown/private/decorators' }, - { text: 'Transformations', link: '/_build/sphinx/markdown/test/transformations' }, + { text: 'Transformations', link: '/_build/sphinx/markdown/private/transformations' }, + ] + }, + { + items: [ + { text: 'Constants', link: '/_build/sphinx/markdown/public/constants' }, + { text: 'Entry points', link: '/_build/sphinx/markdown/public/gisnav' }, ] }, ] diff --git a/docs/vitepress/docs/setup-gis-server.md b/docs/vitepress/docs/setup-gis-server.md index 1d8f97ab..4a920b90 100644 --- a/docs/vitepress/docs/setup-gis-server.md +++ b/docs/vitepress/docs/setup-gis-server.md @@ -1,19 +1,25 @@ # Setup GIS Server -GISNav requires access to a GIS server that serves high-resolution orthoimagery for the approximate global position of the vehicle. The orthoimagery consisting of orthophotos and optional DEM (Digital Elevation Model) rasters are requested from a WMS (Web Map Service) which allows querying rasters by an arbitrary bounding box, and DEM elevation values by an arbitrary global position via its GetMap requests. +GISNav uses an onboard GIS server as a source for high-resolution orthoimagery and digital elevation models (DEM) for the approximate global position of the vehicle. This page describes how to setup a self-hosted GIS server. If you are [running the mock GPS demo](/README), you can +simply use the provided `mapserver` [Docker Compose service](/deploy-with-docker-compose). -The DEM is optionally used to input ground elevation z-coordinates to the PnP (Perspective-n-Point) problem solved by GISNav's pose estimation algorithm in `PoseNode`. If a DEM is not available, GISNav simply assumes a planar ground elevation, which may be sufficient when flying at higher altitudes where an isometric perspective does not significantly distort the perceived image. +## Overview +GISnav requests rasters from a WMS service which allows querying by an arbitrary bounding box (instead of by pre-computed tile). + +The DEM is optionally used to input ground elevation z-coordinates to the Perspective-n-Point (PnP) problem solved by [PoseNode](/_build/sphinx/markdown/public/pose_node). If a DEM is not available, GISNav assumes a planar ground elevation, which may be sufficient when flying at higher altitudes where an isometric perspective does not significantly distort the perceived image. ## Example Setups -You are encouraged to self-host an onboard GIS server with public domain orthoimagery because in a realistic scenario, the GIS should be embedded onboard and not depend on an internet connection. For development, it may sometimes be more convenient to proxy an existing commercial tile-based endpoint. +You should probably self-host an onboard GIS server with public domain orthoimagery because in a realistic scenario, the GIS should be embedded onboard and not depend on an internet connection. For development, it may sometimes be more convenient to proxy an existing commercial tile-based endpoint. + +Because the [mock GPS demo](/README) uses NAIP imagery, in this section we will briefly describe how to obtain NAIP data in case you want to build your own GIS service. ::: info Tile-based endpoints Commercial web-based map services are often [tile-based](https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames) because it is more efficient to serve pre-rendered tiles than to render unique rasters for each individual requested bounding box. You will need a WMS proxy if you decide to go with a tile-based endpoint. ::: -::: warning: Warning: Caching of map tiles +::: warning Warning: Caching of map tiles Many commercial services explicitly prohibit the caching of map tiles in their Terms of Use (ToU), especially if their business model is based on billing API requests. This is mainly to prevent disintermediation in case their tiles are redistributed to a large number of end users. While caching tiles onboard your own drone is likely not the kind of misuse targeted by such clauses, you should still make sure you understand the ToU of the service you are using and that it fits your planned use case. @@ -105,12 +111,18 @@ If you have a drone, you can also use readily available [photogrammetry](https:/ ::: +::: info +The combined layers should cover the flight area of the vehicle at high resolution. Typically this list would have just one layer for highresolution aerial or satellite imagery. + +::: + + ## Rasterizing Vector Data -In some cases, useful map data is not directly provided in raster but in vector format. The GISNav `mapserver` service uses vector-format elevation data from [OSM Buildings](https://osmbuildings.org/) to determine building heights in the simulation area to improve the accuracy of pose estimates especially at lower flight altitudes where the perceived planarity of the terrain is lower. For an example of how the vector data is rasterized using GDAL, see this [old mapserver service Dockerfile](https://github.com/hmakelin/gisnav/blob/v0.65.0/docker/mapserver/Dockerfile). +In some cases, useful map data is not directly provided in raster but in vector format. The GISNav `mapserver` service uses vector-format elevation data from [OSM Buildings](https://osmbuildings.org/) to determine building heights in the simulation area to improve the accuracy of pose estimates especially at lower flight altitudes where the perceived planarity of the terrain is lower. For an example of how the vector data is rasterized using GDAL, see this [old mapserver service Dockerfile](https://github.com/hmakelin/gisnav/blob/v0.65.0/docker/mapserver/Dockerfile#L43-L47). ::: info Demo quirks -The GISNav SITL demo simulation does not actually benefit from the building height data because the simulated KSQL Airport model buildings are all featureless black blocks. See [SITL simulation quirks](#sitl-simulation-quirks) for more information. +The GISNav SITL demo simulation does not actually benefit from the building height data because the KSQL Airport world buildings are all featureless black blocks, meaning it is unlikely any keypoints will be detected in their location. ::: @@ -120,25 +132,21 @@ The KSQL Gazebo world buildings in the SITL simulation demo are featureless grey ![LoFTR does not find keypoints on featureless buildings or terrain (SITL simulation)](../../../_static/img/gisnav_sitl_featureless_buildings.jpg) -## Managing Onboard Map Rasters - -A shared volume is used to provide a way for external file manager services to add and delete maps onboard. The MapServer static mapfile points to a VRT file which is automatically regenerated whenever a change is detected on the shared volume which contains the source raster files. - -A sample FileGator-based `fileserver` service is defined in the `docker/docker-compose.yaml` for managing maps on the shared volume. The application is not necessary - e.g., `scp` could also be used instead. - -```mermaid -graph TB - subgraph mapserver - note1[inotify automatically extracts GDAL supported -formats and regenerates VRT file to which -the default mapfile points] - subgraph SharedVolume[Shared volume] - /etc/mapserver/maps/imagery - /etc/mapserver/maps/dem - end - note2[mapfile and VRT file not on shared volume. -Only external user managed map rasters.] - end - fileserver[External file manager] -->|add/delete| SharedVolume - GDAL[GDAL supported raster formats] -->|.zip, .jp2, .tif, .tiff, .ecw, etc.| SharedVolume +## Managing onboard maps + +A captive [admin web portal](/admin-portal) and a FileGator server are available for uploading maps without need for any commandline or coding knowledge. + +::: info Shared maps volume +A Docker shared volume is used to provide a way for external file managers like FileGatore to add and delete maps onboard. The MapServer static mapfile points to a VRT file which is automatically regenerated whenever a change is detected on the shared volume which contains the source raster files. + +See the [system architecture page](/system-architecture) for more information. +::: + +Simply drop rasters in any GDAL supported formats into the map folder and the GIS server will automatically update itself. + +You can use the provided QGIS service to check whether the maps are there: + +```bash +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav up qgis ``` diff --git a/docs/vitepress/docs/system-architecture.md b/docs/vitepress/docs/system-architecture.md index 2c8d10d7..343a6a72 100644 --- a/docs/vitepress/docs/system-architecture.md +++ b/docs/vitepress/docs/system-architecture.md @@ -29,8 +29,9 @@ graph TB end MAVROS -->|"sensor_msgs/NavSatFix"| BBoxNode + MAVROS -->|"geometry_msgs/PoseStamped"| BBoxNode MAVROS -->|"mavros_msgs/GimbalDeviceAttitudeStatus"| BBoxNode - gscam ---->|"sensor_msgs/Image"| StereoNode + gscam ---->|"sensor_msgs/Image"| BBoxNode subgraph core["GISNav core nodes"] BBoxNode -->|"geographic_msgs/BoundingBox"| GISNode From a6f39d8babdf7fb72cd1a006ce1a6ac316b563cf Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Fri, 10 May 2024 14:00:15 +0100 Subject: [PATCH 07/30] Remove old sphinx api docs --- .dockerignore | 7 +- .gitignore | 7 +- docs/pages/glossary.rst | 818 ------------------------ docs/sphinx/Makefile | 20 - docs/sphinx/conf.py | 209 ------ docs/sphinx/index.rst | 51 -- docs/sphinx/make.bat | 35 - docs/sphinx/private/decorators.rst | 9 - docs/sphinx/private/transformations.rst | 8 - docs/sphinx/public/bbox_node.rst | 8 - docs/sphinx/public/constants.rst | 8 - docs/sphinx/public/gis_node.rst | 8 - docs/sphinx/public/gisnav.rst | 8 - docs/sphinx/public/nmea_node.rst | 8 - docs/sphinx/public/pose_node.rst | 8 - docs/sphinx/public/qgis_node.rst | 8 - docs/sphinx/public/stereo_node.rst | 8 - docs/sphinx/public/uorb_node.rst | 8 - docs/sphinx/test/launch.rst | 9 - docs/sphinx/test/unit.rst | 7 - gisnav/setup.py | 11 - 21 files changed, 6 insertions(+), 1257 deletions(-) delete mode 100644 docs/pages/glossary.rst delete mode 100644 docs/sphinx/Makefile delete mode 100644 docs/sphinx/conf.py delete mode 100644 docs/sphinx/index.rst delete mode 100644 docs/sphinx/make.bat delete mode 100644 docs/sphinx/private/decorators.rst delete mode 100644 docs/sphinx/private/transformations.rst delete mode 100644 docs/sphinx/public/bbox_node.rst delete mode 100644 docs/sphinx/public/constants.rst delete mode 100644 docs/sphinx/public/gis_node.rst delete mode 100644 docs/sphinx/public/gisnav.rst delete mode 100644 docs/sphinx/public/nmea_node.rst delete mode 100644 docs/sphinx/public/pose_node.rst delete mode 100644 docs/sphinx/public/qgis_node.rst delete mode 100644 docs/sphinx/public/stereo_node.rst delete mode 100644 docs/sphinx/public/uorb_node.rst delete mode 100644 docs/sphinx/test/launch.rst delete mode 100644 docs/sphinx/test/unit.rst diff --git a/.dockerignore b/.dockerignore index f93a95c6..7c99b0c8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -15,10 +15,9 @@ build/ install/ log/ -# Built Sphinx documentation +# Built documentation docs/_build/ -docs/sphinx/_build/ -docs/vitepress/docs/_build/ +docs/vitepress/_build/ # coverage.py files .coverage* @@ -38,4 +37,4 @@ gisnav/test/sitl/ulog_analysis/.ipynb_checkpoints/ node_modules # vitepress -docs/vitepress/docs/.vitepress/cache/ +docs/docs/.vitepress/cache/ diff --git a/.gitignore b/.gitignore index cb1490cb..7acd9412 100644 --- a/.gitignore +++ b/.gitignore @@ -12,10 +12,9 @@ build/ install/ log/ -# Built Sphinx documentation +# Built documentation docs/_build/ -docs/sphinx/_build/ -docs/vitepress/docs/_build/ +docs/vitepress/_build/ # coverage.py files .coverage* @@ -43,4 +42,4 @@ debian node_modules # vitepress -docs/vitepress/docs/.vitepress/cache/ +docs/docs/.vitepress/cache/ diff --git a/docs/pages/glossary.rst b/docs/pages/glossary.rst deleted file mode 100644 index 76f5de63..00000000 --- a/docs/pages/glossary.rst +++ /dev/null @@ -1,818 +0,0 @@ -Glossary -==================================================== - -.. warning:: - These definitions are constantly revised as the software is developed so - do not rely on these just yet. They may also not perfectly correspond to - what is currently written in the source code. - -Terminology -____________________________________________________ - -Here we provide explanations for terms used throughout this documentation that are -more esoteric or represent jargon i.e. have meanings unique to the context of this -documentation, deviating from their more generally understood interpretations. - -Some terms may have multiple definitions (denoted by bullet points) and the -correct alternative should be easily inferred from the context they are used in. - -.. glossary:: - :sorted: - - Altitude - Altitude of :term:`vehicle` in any vertical datum or reference level. - - .. todo:: - Describe AGL, AMSL, ellipsoid and other flavors of altitude used by GISNav. - - Anchor - A :term:`YAML` anchor - - Extension - Extended functionality - * Functionality beyond GISNav :term:`core` functionality. For example, - :class:`.NMEANode` for integrating GISNav as a :term:`GPS` - substitute or complement. - * A `Docker Compose extension `_ - - Autopilot - Autopilot flight control software such as :term:`PX4` or :term:`ArduPilot`. - - Blending - Blending of multiple :term:`GPS` sensors by the :term:`navigation filter`. - - Bounding box - A geographical box or rectangle, the coordinates of which are known, that - bounds an area of interest to be used as a :term:`reference` for pose - estimation. - - .. seealso:: Learn more - `wiki.openstreetmap.org/wiki/Bounding_Box - `_ - - Bucket - An :term:`AWS` S3 bucket - - Camera - A standard RGB color camera carried :term:`onboard` that is used by GISNav - for pose estimation. - - Companion - Companion computer - The :term:`onboard` companion computer that GISNav runs on. E.g Nvidia - :term:`Jetson Nano`. - - Container - A :term:`Docker` container. - - Core - Core functionality - Deployment or implementation of GISNav which is enough to implement - core functionality needed by an :term:`extension`. GISNav is intended - to be extended by adding more application or integration specific - :term:`nodes ` instead of adding new features to :term:`core` - nodes. - - .. seealso:: - :ref:`Core data flow graph` - - Coverage - * Code coverage - * :term:`Onboard` :term:`GIS` embedded orthoimagery coverage over - flight :term:`mission` area - - Data flow - Data flow graph - Distributed :term:`ROS` topology. From which :term:`node` :term:`publisher` - and via which :term:`topic` is data flowing to which node and - :term:`subscriber`. - - .. todo:: - Use ROS terminology and call it computation graph? - - Decorator - A :term:`Python` decorator function. - - Deploy - Deployment - A GISNav deployment consisting of various configurations of - :term:`Docker Compose` services depending on deployment type. - - Elevation - Elevation of the ground surface or :term:`ground track` in any vertical - datum or reference level. - - .. seealso:: - :term:`DEM` - - .. todo:: - Describe AGL, AMSL, ellipsoid and other flavors of elevation used by GISNav. - - Firmware - :term:`Autopilot` software that is loaded onto and executed on - the :term:`FMU`. More specifically, :term:`PX4` or :term:`ArduPilot` - software running on the :term:`FMU`, for example in :term:`HIL` - simulation. - - Frame - * A spatial coordinate reference frame, especially as defined in - :term:`ROS` - * An :term:`image` frame (i.e. a single frame from a video stream) - - GetFeatureInfo - A :term:`WMS` operation for requesting non-:term:`raster` features from - :term:`GIS` servers. Used in earlier versions of GISNav to fetch DEM - values for specific points but no longer used. - - GetMap - A :term:`WMS` operation for requesting :term:`raster` images from - :term:`GIS` servers. - - .. seealso:: Learn more - `opengeospatial.github.io/e-learning/wms/text/operations.html#getmap - `_ - - Absolute position - Global position - Horizontal and vertical position in a :term:`CRS` that tells the location - of the :term:`vehicle` relative to Earth. - - .. seealso:: - :term:`Relative position`, :term:`Local position` - - Ground control - Ground control software - Ground control station - Ground control software that controls the :term:`vehicle` through - a remote radio link, using a protocol such as :term:`MAVLink`. - - Ground track - The :term:`vehicle` flight path projected to the ground directly below the - vehicle, in the direction of :term:`nadir`. - - Home - :term:`Vehicle` local origin or home position as defined by its - :term:`navigation filter`. - - .. todo:: - This is still poorly defined - the home and local origin may be - different. - - Image - * A :term:`Docker` image - * A single image frame from the :term:`camera` - - .. seealso:: - :term:`Query image` - - .. warning:: - Not to be confused with :term:`Orthoimage` or :term:`Imagery` - - Launch - Launch test - Launching using the :term:`ROS` launch system, ROS launch tests. - - Relative position - Local position - Horizontal and vertical position that tells the location of the - :term:`vehicle` relative to :term:`home`. - - .. note:: - The term "local position" often includes :term:`vehicle` attitude, - while the term "relative position" only includes its position. - But this distinction is currently not well established throughout - the documentation. - - .. seealso:: - :term:`Absolute position`, :term:`Global position` - - Map - map - * A world-fixed ROS coordinate :term:`frame ` as defined in - :term:`REP 105`. In GISNav the - ``map`` frame is defined as declared by :term:`MAVROS`. - * A :term:`raster` retrieved from a :term:`GIS` system. Generic - term that could e.g. mean :term:`orthoimagery` or :term:`DEMs ` - depending on context. - - .. todo:: - Define ``map`` frame independently of MAVROS - could use the - bounding box ``reference`` frame here instead. - - Match - Matching - Keypoint matching in the context of trying to estimate the camera - :term:`pose` between two images. - - .. seealso:: - :term:`PnP`. - - Message - A :term:`ROS` message. - - Middleware - A software application that facilitates communication between other - software applications (by transmitting data between them). More - specifically, :term:`MAVROS`. - - Mission - Mission mode - * A flight mission, typically a file uploaded to a :term:`GCS` which then - sends the appropriate commands to the :term:`vehicle` for executing the - flight mission. - * :term:`PX4` Mission :term:`mode` - - Mode - :term:`Autopilot` flight mode - - Model - * A machine learning model or neural :term:`network`, used for e.g. - :term:`camera` :term:`pose` estimation - * A :term:`Gazebo` model, more specifically a :term:`vehicle` model - - Module - A :term:`Python` module. - - Nadir - Direction pointing directly down from the :term:`vehicle` (opposed to - :term:`zenith`). Does not mean down relative to vehicle body but rather - the direction of the force of gravity. - - Navigation filter - An algorithm implemented by the :term:`FMU` that is responsible for - determining :term:`global position` and :term:`local position` based - on available sensor inputs. - - .. note:: - :term:`EKF` is one commonly used algorithm and is often used - interchangeably to describe the navigation filter, even if the - navigation filter does not use EKF. - - Network - A neural network (a machine learning :term:`model`), such as - :term:`LightGlue` - - Node - A :term:`ROS` node. - - Notebook - A :term:`Jupyter notebook`. - - Offboard - Anything that is not :term:`onboard`. More specifically any computer - (e.g. running the :term:`GCS`) that is not carried :term:`onboard` - and does not draw power from the :term:`vehicle` battery. - - .. todo:: - Change offboard to mean off FCU, not off vehicle? - - Onboard - Anything carried by the :term:`vehicle` that would draw power from its - battery, including the :term:`FMU` and the :term:`companion computer`. - - .. todo:: - Change onboard to mean on FCU, not on vehicle? - - Orientation - :term:`Vehicle` or :term:`camera` orientation (attitude) in 3D space, - typically represented by a :term:`quaternion`. - - .. seealso:: - :term:`RPY` for Euler angle representation - - Origin - .. todo:: - Available - - Imagery - Orthoimagery - Orthoimage - Orthophoto - * Orthorectified high-resolution geographic imagery stored in :term:`GIS` - * An orthorectified high-resolution image of a location on Earth for - which the :term:`bounding box` is known, retrieved from a :term:`GIS` - system. - - .. seealso:: Learn more - `en.wikipedia.org/wiki/Orthophoto - `_ - - .. todo:: - The jargon here is still a bit loose: sometimes the aligned and - stacked :term:`DEM` :term:`raster` is included in the term - "orthoimage", and the term "orthophoto" is used for the - high-resolution image only. - - Query - Query image - In a pose estimation context, the :term:`image` frame from the - :term:`camera`, to be compared to the :term:`reference` - :term:`orthoimage`. - - Package - * A :term:`ROS 2` (colcon) package - * A :term:`Python` package - - Parameter - Most likely one of these: - - * A :term:`ROS 2` parameter - * A :term:`PX4` parameter - * An :term:`ArduPilot` parameter - - Path - A series of :term:`pose` - - Perspective-n-Point - A problem in computer vision where a camera :term:`pose` is estimated - from 2D image to 3D :term:`world` coordinate point correspondences. - :term:`PnP` is used as an acronym. - - .. seealso:: Learn more - `docs.opencv.org/4.x/d5/d1f/calib3d_solvePnP.html - `_ - - Pose - A spatial pose in three dimensions including :term:`position` and - :term:`orientation`. - - Position - * A :term:`global position` - * A :term:`local position` - - Publish - Publisher - A :term:`ROS` publisher, to publish a ROS :term:`message`. - - Quaternion - A 4-tuple describing or :term:`orientation` in 3D space. Avoids - the gimbal lock problem that comes when using Euler angles. Should be - in (x, y, z, w) order unless otherwise defined. - - .. seealso:: - :term:`RPY` for Euler angle representation of orientation - - Raster - A rasterized image retrieved from a :term:`GIS` system, as opposed - to a vectorized image. Used exclusively for geographical imagery, - not e.g. for an :term:`image` from the :term:`camera`. - - .. seealso:: Learn more - `carto.com/blog/raster-vs-vector-whats-the-difference-which-is-best - `_ - - Reference - Reference image - Reference raster - In a pose estimation context, the :term:`orthoimage` frame from the - :term:`GIS` server, to be compared to the :term:`query image`. - - .. todo:: - "Raster" should probably be used exclusively here instead of "image" - to avoid confusing with the query image. - - Rotation - .. todo:: - Available - - Service - * A :term:`Docker Compose` service - * A :term:`ROS` service - - Service orchestration - Deploying and managing :term:`Docker Compose` services that - constitute a GISNav deployment. Currently done using Make (Makefiles). - - .. seealso:: - :term:`Service` - - Stack - Stacked - Stacked image - Stacked raster - * The :term:`orthophoto` stacked together with its aligned :term:`DEM` - :term:`raster`, representing a "3D orthoimage". - * The rotated 8-bit grayscale :term:`query` image, 8-bit grayscale :term:`reference` image, - and the 16-bit reference DEM stacked together in a single 4-channel (alpha channel) image. - Most likely in one of ``CvBridge`` supported (``rgb8`` or ``bgra8``) formats. - - Subscribe - Subscriber - Subscription - A :term:`ROS` subscription, to subscribe to a ROS :term:`topic`. - - Test - Currently the following kinds of tests are recognized: - - * A unit test - * A :term:`launch test` - * A simulation (:term:`SITL` or :term:`HIL`) test - - Topic - A :term:`ROS` topic. - - Vehicle - The unmanned aircraft that uses GISNav for navigation. Can e.g. be a - quadcopter of fixed-wing aircraft. - - .. todo:: - Adopt the term ``robot`` as an alias for vehicle to better align with - :term:`ROS` terminology? Vehicle seems to be the term adopted by - both PX4 and ArduPilot. - - World - World coordinates - World coordinate system - * In the :term:`PNP` problem context, the coordinate system of the - :term:`reference` including the z-axis used to represent ground - :term:`elevation`. - * A :term:`Gazebo` world. - - Zenith - Direction pointing directly up from the :term:`vehicle` (opposed to - :term:`nadir`). Does not mean up relative to vehicle body but rather the - direction opposite to the force of gravity. - -Abbreviations -____________________________________________________ - -.. glossary:: - :sorted: - - BBox - :term:`Bounding box` - - Dev - Development - - Coords - Coordinates - - Qry - Query - - Ref - Reference - - Sim - Simulation - -Acronyms -____________________________________________________ - -.. glossary:: - :sorted: - - AGL - :term:`Altitude` or :term:`Elevation` Above Ground Level - - AMSL - :term:`Altitude` or :term:`Elevation` Above Mean Sea Level - - API - Application Programming Interface - - AWS - Amazon Web Services - - CI - Continuous Integration - - SRS - CRS - Spatial Reference System / Coordinate Reference System - - CV - Computer Vision - - DEM - Digital Elevation Model - - .. seealso:: - `en.wikipedia.org/wiki/Digital_elevation_model - `_ - - DNS - Domain Name System: `en.wikipedia.org/wiki/Domain_Name_System - `_ - - ECEF - Earth-Centered, Earth-Fixed (coordinate frame) - - EKF - Extended Kalman Filter - - .. seealso:: - :term:`Navigation filter` - - ENU - East-North-Up coordinate system - - .. note:: - Up means in the direction of zenith. - - EOL - End-of-life, e.g. in context of ROS distributions that are no longer - officially supported. - - FCU - FMU - Flight Control Unit / Flight Management Unit. For example, - :term:`Pixhawk`. - - FOSS - Free and Open Source Software - - .. seealso:: - `en.wikipedia.org/wiki/Free_and_open-source_software - `_ - - FoV - FOV - Field Of View - - FRD - Front-Right-Down coordinate system. - - .. note:: - Down here means down relative to :term:`vehicle` body, not :term:`nadir`. - - GCS - :term:`Ground Control Station` - - GHCR - GitHub Container Registry - - GIS - Geographic Information System - - GML - Geography Markup Language - - GNSS - GPS - Global Navigation Satellite System / Global Positioning System - - GPU - Graphics Processing Unit - - GUI - Graphical User Interface - - HIL - HITL - Hardware In The Loop simulation - - IDE - Integrated/Interactive Development Environment - - NAIP - National Agriculture Imagery Program - - .. seealso:: - `USGS NAIP website `_ - - NED - North-East-Down coordinate system - - .. note:: - Down here means :term:`nadir`. - - NMEA - NMEA 0183 - Communication protocol for :term:`GPS` receivers - - OGC - Open Geospatial Consortium: `ogc.org `_ - - OS - Operating System - - OSM - :term:`OpenStreetMap` - - PnP - PNP - :term:`Perspective-n-Point` problem - - PR - Pull Request - - QGC - :term:`QGroundControl` - - RDP - Remote Desktop Protocol - - REP - REP 103 - REP 105 - ROS Enhancement Proposal - - * `REP 103 `_ - * `REP 105 `_ - - RPY - Roll, pitch, yaw - - SCP - scp - Secure Copy Protocol - - SITL - Software In The Loop simulation - - SQL - Structured Query Language: `en.wikipedia.org/wiki/SQL `_ - - TCP - TCP/IP - IP - Transmission Control Protocol/Internet Protocol - - ToU - TOU - Terms of Use - - UDP - User Datagram Protocol: `en.wikipedia.org/wiki/User_Datagram_Protocol - ` - - USGS - United States Geological Survey - - VNC - Virtual Network Computing - - VO - Visual Odometry - - WGS - WGS 84 - A World Geodetic System coordinate system: `en.wikipedia.org/wiki/World_Geodetic_System `_ - - WMS - WMTS - Web Map Service / Web Map Tile Service, two separate :term:`OGC` - developed communication protocols. WMS allows querying by arbitrary - :term:`bounding box` while WMTS returns pre-computed tiles in a - standardized grid. - - .. seealso:: - * https://www.ogc.org/standards/wms - - * https://www.ogc.org/standards/wmts - - XRCE - :term:`DDS` for eXtremely Resource Constrained Environments (`DDS-XRCE - protocol `_) - -Proper names -____________________________________________________ - -This is not an exhaustive list (e.g. does not include many of the specific technologies -used in the project) but should list many of the main ones, especially if they -relate to external interfaces. - -.. glossary:: - :sorted: - - ArduPilot - ArduPilot open source autopilot: `ardupilot.org `_ - - colcon - A build automation tool used by :term:`ROS 2`: `colcon.readthedocs.io/en/released/ `_ - - CUDA - NVIDIA parallel computing platform: `developer.nvidia.com/cuda-zone `_ - - D2 - A diagram scripting language: `d2lang.com `_ - - DDS - Data Distribution Service - A :term:`middleware` protocol and standard: - `dds-foundation.org `_ - - Docker - Software containerization tool: `docker.com `_ - - Docker Compose - Tool for defining and running multi-container :term:`Docker` applications: - `docs.docker.com/compose `_ - - FileGator - A :term:`FOSS` self-hosted file management application: `docs.filegator.io/ `_ - Gazebo - Simulation software: `gazebosim.org `_ - - GDAL - Geospatial Data Abstraction Library - Software library for handling geospatial data: `gdal.org `_ - - GSCam - :term:`ROS` :term:`GStreamer` camera driver: https://github.com/ros-drivers/gscam - - GStreamer - Open source multimedia framework: `gstreamer.freedesktop.org `_ - - Nano - Jetson Nano - An Nvidia Jetson Nano computer - - Jupyter - JupyterLab - Jupyter notebook - A web based :term:`IDE`: `jupyter.org `_ - - LightGlue - A keypoint matching model: `github.com/cvg/LightGlue `_ - - LoFTR - A keypoint matching model that GISNav used before switching over to :term:`LightGlue`: `zju3dv.github.io/loftr `_ - - Make - GNU Make, a build automation tool: `gnu.org/software/make/ `_ - - MapServer - Open source GIS software: `mapserver.org `_ - - MAVLink - MAVLink (Micro Air Vehicle Link) protocol: `mavlink.io `_ - - MAVROS - An open source :term:`MAVLink` to :term:`ROS` :term:`middleware`: - `wiki.ros.org/mavros `_ - - MAVSDK - :term:`MAVLink` software development kit: `mavsdk.mavlink.io/main/en/index.html `_ - - Mermaid - mermaid.js - A diagram scripting language: `mermaid.js.org ` - - OpenCV - Open source computer vision software library: `opencv.org `_ - - OpenStreetMap - Open source map of the world: `openstreetmap.org `_ - - Pixhawk - Hardware standard for open source autopilots: `pixhawk.org `_ - - PostGIS - :term:`GIS` extension for :term:`Postgres`: `postgis.net `_ - - Postgres - An :term:`SQL` server: `postgresql.org `_ - - PX4 - PX4 Autopilot: `px4.io `_ - - Python - A computer programming language: `python.org `_ - - QEMU - A :term:`FOSS` full-system emulator: `qemu.org `_ - - QGIS - A :term:`GIS` client (and server): `qgis.org/en/site/ `_ - - QGroundControl - :term:`GCS` software: `qgroundcontrol.com `_ - - ROS - ROS 2 - Robot Operating System: `ros.org `_ - - RViz - :term:`ROS` 3D visualization software: https://github.com/ros2/rviz - - tf2 - :term:`ROS 2` transformations library: `wiki.ros.org/tf2 `_ - - Torch - An open source machine learning software library: `torch.ch `_ - - Ubuntu - A Linux distribution, only supported :term:`OS` for GISNav: - `https://ubuntu.com/ `_ - - VRT - :term:`GDAL` Virtual Format (file format) - - X Server - Window system that comes with :term:`Ubuntu`: `www.x.org/wiki/ `_ - - YAML - A data serialization language: `yaml.org `_ - -Other -____________________________________________________ - -.. glossary:: - :sorted: - - KSQL - ICAO airport code for San Carlos Airport in California (used as simulation - environment in GISNav development and testing). diff --git a/docs/sphinx/Makefile b/docs/sphinx/Makefile deleted file mode 100644 index d4bb2cbb..00000000 --- a/docs/sphinx/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/sphinx/conf.py b/docs/sphinx/conf.py deleted file mode 100644 index b0ad8219..00000000 --- a/docs/sphinx/conf.py +++ /dev/null @@ -1,209 +0,0 @@ -from __future__ import annotations - -import datetime -import os -import subprocess -import sys -from dataclasses import dataclass -from typing import Optional, Union -from xml.etree import ElementTree - -sys.path.insert(0, os.path.abspath("../../gisnav")) - -# -- Version information ----------------------------------------------------- - - -@dataclass(frozen=True) -class PackageData: - """Stores data parsed from package.xml (not comprehensive)""" - - package_name: str - version: str - description: Optional[str] - author: Optional[str] - author_email: Optional[str] - maintainer: Optional[str] - maintainer_email: Optional[str] - license_name: Optional[str] - - @staticmethod - def require_not_none(text: Optional[Union[str, ElementTree.Element]]): - """Raises ValueError if input is not a string but None""" - if text is None: - raise ValueError("Expected not None") - return text - - @classmethod - def parse_package_data(cls, package_file: str) -> PackageData: - """Parses package.xml in current folder - - :param package_file: Absolute path to package.xml file - :return: Parsed package data - :raise FileNotFoundError: If package.xml file is not found - """ - if os.path.isfile(package_file): - tree = ElementTree.parse(package_file) - root = tree.getroot() - - author = root.find("author") - maintainer = root.find("maintainer") - kwargs = { - "package_name": cls.require_not_none(root.findtext("name")), - "version": cls.require_not_none(root.findtext("version")), - "description": root.findtext("description"), - "author": root.findtext("author"), - "author_email": author.attrib.get("email") - if author is not None - else None, - "maintainer": root.findtext("maintainer"), - "maintainer_email": maintainer.attrib.get("email") - if maintainer is not None - else None, - "license_name": root.findtext("license"), - } - package_data = PackageData(**kwargs) - return package_data - else: - raise FileNotFoundError(f"Could not find package file at {package_file}.") - - -package_data = PackageData.parse_package_data( - os.path.abspath("../../gisnav/package.xml") -) - -# -- Project information ----------------------------------------------------- - -project = package_data.package_name -copyright = f"2022, {package_data.author}" -author = package_data.author - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.coverage", - "sphinx.ext.autosectionlabel", - "sphinx.ext.todo", - "sphinx.ext.viewcode", - "sphinx_design", - "autodocsumm", - "myst_parser", - "sphinxcontrib.video", - "sphinxcontrib.mermaid", - "sphinx_copybutton", - "sphinx_substitution_extensions", - "sphinx_markdown_builder", -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] - -todo_include_todos = True - -language = "en" - -html_last_updated_fmt = "%b %d, %Y" - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = "pydata_sphinx_theme" - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] - -# Custom CSS styles -html_css_files = [ - "css/style.css", -] - -html_js_files = [ - "js/custom.js", -] - -# Configure pydata theming options here -html_theme_options = { - "logo": { - "image_light": "_static/svg/logo-no-background.svg", - "image_dark": "_static/svg/logo-no-background-white.svg", - }, - "collapse_navigation": True, - "icon_links": [ - { - "name": "GitHub", - "url": "https://github.com/hmakelin/gisnav", - "icon": "fab fa-github", - "type": "fontawesome", - } - ], - "icon_links_label": "Quick Links", - "show_toc_level": 3, - "primary_sidebar_end": ["indices.html"], - "favicons": [ - { - "rel": "icon", - "sizes": "128x128", - "href": "png/gisnav-website-favicon-color.png", - }, - ], -} - -# Handle dark mode mermaid diagrams - see also _static/js/custom.js -mermaid_init_js = """ - document.addEventListener('DOMContentLoaded', function(event) { - var mermaidDivs = document.querySelectorAll('.mermaid'); - mermaidDivs.forEach(function(div) { - div.setAttribute('data-mermaid', div.textContent); - }); - mermaid.initialize({startOnLoad: true, theme: 'default'}); - }); -""" - -# -- Version information ----------------------------------------------------- - -# Make version number accessible in .rst files -# rst_epilog = f'.. |version| replace:: **v{package_data.version}**' -version = package_data.version -release = version - -# Add git tag to release -try: - release = ( - subprocess.check_output(["git", "describe", "--tags"]).strip().decode("utf-8") - ) -except subprocess.CalledProcessError: - raise - -ros_version = "humble" -# Define dynamic content (substitutions) here -# This should reduce documentation maintenance burden -# Substitutions must be in prolog (not epilot) - otherwise -# Sphinx-Substition-Extensions might not work (substitutions inside directives) -rst_prolog = f""" -.. |release| replace:: {release} -.. |version| replace:: {version} -.. |vversion| replace:: {'v' + version} -.. |ros_version| replace:: {ros_version} -.. |ros_version_capitalized| replace:: Humble -.. |ROS 2 install instructions| replace:: ROS 2 install instructions -.. _ROS 2 install instructions: https://docs.ros.org/en/{ros_version}/Installation.html -.. |Docker Compose file| replace:: Docker Compose file -.. _Docker Compose file: https://github.com/hmakelin/gisnav/blob/v{version}/docker/docker-compose.yaml -""" # noqa: E501 - -rst_epilog = f""" -Updated on {datetime.datetime.today().strftime("%b %d, %Y")} - -GISNav release: |release| -""" diff --git a/docs/sphinx/index.rst b/docs/sphinx/index.rst deleted file mode 100644 index f8f28650..00000000 --- a/docs/sphinx/index.rst +++ /dev/null @@ -1,51 +0,0 @@ -************************************************** -API reference -************************************************** -Here you can find the automatically generated API reference for the -`GISNav ROS 2 package`_. Use the search bar or the below module index to find -what you are looking for. - -.. _GISNav ROS 2 package: https://github.com/hmakelin/gisnav - -.. warning:: - GISNav is under active development and stability of the public API is not - guaranteed. Use a specific commit or version tag to mitigate the impact of - breaking changes. - -.. note:: - The private API is also documented here for developer reference as the - private method docstrings may contain interesting and useful information. - The private API should not be relied on for any application integrations. - -.. toctree:: - :caption: Core nodes - - public/bbox_node - public/gis_node - public/stereo_node - public/pose_node - -.. toctree:: - :caption: Extension nodes - - public/uorb_node - public/nmea_node - public/qgis_node - -.. toctree:: - :caption: Static API - - public/constants - public/gisnav - -.. toctree:: - :caption: Tests - - test/unit - test/launch - -.. toctree:: - :caption: Private API - - private/decorators - private/transformations diff --git a/docs/sphinx/make.bat b/docs/sphinx/make.bat deleted file mode 100644 index 153be5e2..00000000 --- a/docs/sphinx/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/sphinx/private/decorators.rst b/docs/sphinx/private/decorators.rst deleted file mode 100644 index 0f47daba..00000000 --- a/docs/sphinx/private/decorators.rst +++ /dev/null @@ -1,9 +0,0 @@ -Decorators -____________________________________________________ -.. automodule:: gisnav._decorators - :autosummary: - :members: - :undoc-members: - :special-members: __init__ - :show-inheritance: - :exclude-members: P diff --git a/docs/sphinx/private/transformations.rst b/docs/sphinx/private/transformations.rst deleted file mode 100644 index e0c1d746..00000000 --- a/docs/sphinx/private/transformations.rst +++ /dev/null @@ -1,8 +0,0 @@ -Messaging -____________________________________________________ -.. automodule:: gisnav._messaging - :autosummary: - :members: - :undoc-members: - :special-members: __init__ - :show-inheritance: diff --git a/docs/sphinx/public/bbox_node.rst b/docs/sphinx/public/bbox_node.rst deleted file mode 100644 index c49c1024..00000000 --- a/docs/sphinx/public/bbox_node.rst +++ /dev/null @@ -1,8 +0,0 @@ -BBoxNode -____________________________________________________ -.. automodule:: gisnav.core.bbox_node - :autosummary: - :members: - :undoc-members: - :special-members: __init__ - :show-inheritance: diff --git a/docs/sphinx/public/constants.rst b/docs/sphinx/public/constants.rst deleted file mode 100644 index 08e956db..00000000 --- a/docs/sphinx/public/constants.rst +++ /dev/null @@ -1,8 +0,0 @@ -Constants -____________________________________________________ -.. automodule:: gisnav.constants - :autosummary: - :members: - :undoc-members: - :special-members: __init__ - :show-inheritance: diff --git a/docs/sphinx/public/gis_node.rst b/docs/sphinx/public/gis_node.rst deleted file mode 100644 index 2b1136dc..00000000 --- a/docs/sphinx/public/gis_node.rst +++ /dev/null @@ -1,8 +0,0 @@ -GISNode -____________________________________________________ -.. automodule:: gisnav.core.gis_node - :autosummary: - :members: - :undoc-members: - :special-members: __init__ - :show-inheritance: diff --git a/docs/sphinx/public/gisnav.rst b/docs/sphinx/public/gisnav.rst deleted file mode 100644 index 3546d1c6..00000000 --- a/docs/sphinx/public/gisnav.rst +++ /dev/null @@ -1,8 +0,0 @@ -Entry points -____________________________________________________ -.. automodule:: gisnav - :autosummary: - :members: - :undoc-members: - :special-members: __init__ - :show-inheritance: diff --git a/docs/sphinx/public/nmea_node.rst b/docs/sphinx/public/nmea_node.rst deleted file mode 100644 index f9ea8f9f..00000000 --- a/docs/sphinx/public/nmea_node.rst +++ /dev/null @@ -1,8 +0,0 @@ -NMEANode -____________________________________________________ -.. automodule:: gisnav.extensions.nmea_node - :autosummary: - :members: - :undoc-members: - :special-members: __init__ - :show-inheritance: diff --git a/docs/sphinx/public/pose_node.rst b/docs/sphinx/public/pose_node.rst deleted file mode 100644 index 9d0e3aaa..00000000 --- a/docs/sphinx/public/pose_node.rst +++ /dev/null @@ -1,8 +0,0 @@ -PoseNode -____________________________________________________ -.. automodule:: gisnav.core.pose_node - :autosummary: - :members: - :undoc-members: - :special-members: __init__ - :show-inheritance: diff --git a/docs/sphinx/public/qgis_node.rst b/docs/sphinx/public/qgis_node.rst deleted file mode 100644 index 11e9f321..00000000 --- a/docs/sphinx/public/qgis_node.rst +++ /dev/null @@ -1,8 +0,0 @@ -QGISNode -____________________________________________________ -.. automodule:: gisnav.extensions.qgis_node - :autosummary: - :members: - :undoc-members: - :special-members: __init__ - :show-inheritance: diff --git a/docs/sphinx/public/stereo_node.rst b/docs/sphinx/public/stereo_node.rst deleted file mode 100644 index 782a5a3b..00000000 --- a/docs/sphinx/public/stereo_node.rst +++ /dev/null @@ -1,8 +0,0 @@ -StereoNode -____________________________________________________ -.. automodule:: gisnav.core.stereo_node - :autosummary: - :members: - :undoc-members: - :special-members: __init__ - :show-inheritance: diff --git a/docs/sphinx/public/uorb_node.rst b/docs/sphinx/public/uorb_node.rst deleted file mode 100644 index 680c87ca..00000000 --- a/docs/sphinx/public/uorb_node.rst +++ /dev/null @@ -1,8 +0,0 @@ -UORBNode -____________________________________________________ -.. automodule:: gisnav.extensions.uorb_node - :autosummary: - :members: - :undoc-members: - :special-members: __init__ - :show-inheritance: diff --git a/docs/sphinx/test/launch.rst b/docs/sphinx/test/launch.rst deleted file mode 100644 index 888bd50f..00000000 --- a/docs/sphinx/test/launch.rst +++ /dev/null @@ -1,9 +0,0 @@ -Launch tests -____________________________________________________ - -.. automodule:: test.launch.testcases - :autosummary: - :members: - :undoc-members: - :special-members: __init__ - :show-inheritance: diff --git a/docs/sphinx/test/unit.rst b/docs/sphinx/test/unit.rst deleted file mode 100644 index 596e0b7b..00000000 --- a/docs/sphinx/test/unit.rst +++ /dev/null @@ -1,7 +0,0 @@ -Unit tests -____________________________________________________ - -.. automodule:: test.unit - :autosummary: - :members: - :show-inheritance: diff --git a/gisnav/setup.py b/gisnav/setup.py index 544862d1..9c0f3876 100644 --- a/gisnav/setup.py +++ b/gisnav/setup.py @@ -124,26 +124,15 @@ def parse_package_data(cls, package_file: str) -> PackageData: "qgis_node": ["psycopg2"], "dev": [ "aiohttp", - "autodocsumm", "coverage", "docutils>=0.17", "jupyter", "lxml", "mavsdk", - "myst_parser", "pre-commit", - "pydata-sphinx-theme", - "pygments", "pytest", "python-dateutil>=2.8.2", "pyyaml", - "sphinx-copybutton", - "sphinx-design", - "Sphinx-Substitution-Extensions", - "sphinxcontrib-mermaid", - "sphinxcontrib-video", - "sphinx-markdown-builder==0.6.6", - "Sphinx", "types-PyYAML", ], }, From ed99c853f4ea75d4b1fa80b6ae605abf9df6e494 Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Fri, 10 May 2024 14:11:44 +0100 Subject: [PATCH 08/30] Add makefile targets to build vitepress dist and remove some dead links to make build go through --- .dockerignore | 4 ++-- .gitignore | 4 ++-- Makefile | 8 ++++++-- docs/vitepress/docs/install-locally.md | 2 +- docs/vitepress/docs/jetson-pixhawk.md | 2 +- docs/vitepress/docs/setup-gis-server.md | 6 ++---- docs/vitepress/docs/system-architecture.md | 2 +- 7 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.dockerignore b/.dockerignore index 7c99b0c8..6a6cf062 100644 --- a/.dockerignore +++ b/.dockerignore @@ -16,8 +16,8 @@ install/ log/ # Built documentation -docs/_build/ -docs/vitepress/_build/ +docs/vitepress/docs/.vitepress/dist +docs/vitepress/docs/.vitepress/build # coverage.py files .coverage* diff --git a/.gitignore b/.gitignore index 7acd9412..8a3193b1 100644 --- a/.gitignore +++ b/.gitignore @@ -13,8 +13,8 @@ install/ log/ # Built documentation -docs/_build/ -docs/vitepress/_build/ +docs/vitepress/docs/.vitepress/dist +docs/vitepress/docs/.vitepress/build # coverage.py files .coverage* diff --git a/Makefile b/Makefile index 798eb827..657ba919 100644 --- a/Makefile +++ b/Makefile @@ -8,5 +8,9 @@ include docker/Makefile .PHONY: docs docs: - @$(MAKE) -C docs html - @cd docs/_build/html && touch .nojekyll # for GitHub Pages + @cd docs/vitepress && npm run docs:build + @cd docs/.vitepress/dist && touch .nojekyll # for GitHub Pages + +.PHONY: docs-preview +docs-preview: + @cd docs/vitepress && npm run docs:preview diff --git a/docs/vitepress/docs/install-locally.md b/docs/vitepress/docs/install-locally.md index aba09c39..41ca751d 100644 --- a/docs/vitepress/docs/install-locally.md +++ b/docs/vitepress/docs/install-locally.md @@ -130,4 +130,4 @@ pip3 install ./gisnav[dev] -Once GISNav is installed, you can try [deploying the development services](/deploy-development-services). +Once GISNav is installed, you can try [deploying the development services](/deploy-for-development). diff --git a/docs/vitepress/docs/jetson-pixhawk.md b/docs/vitepress/docs/jetson-pixhawk.md index cb431b19..0b2649db 100644 --- a/docs/vitepress/docs/jetson-pixhawk.md +++ b/docs/vitepress/docs/jetson-pixhawk.md @@ -81,7 +81,7 @@ graph TB #### Picture -![NXP FMUK66-E Setup](/gisnav_hil_fmuk66-e_setup.jpg) +![NXP FMUK66-E Setup](/public/gisnav_hil_fmuk66-e_setup.jpg) * NXP FMUK66-E (FMU) board connected to laptop via micro-USB and to Jetson Nano via TELEM1. * FMU draws power from laptop via micro-USB, and Jetson Nano from wall socket via dedicated micro-USB DC adapter, so no LiPo batteries needed. diff --git a/docs/vitepress/docs/setup-gis-server.md b/docs/vitepress/docs/setup-gis-server.md index 4a920b90..f8cfd124 100644 --- a/docs/vitepress/docs/setup-gis-server.md +++ b/docs/vitepress/docs/setup-gis-server.md @@ -6,7 +6,7 @@ simply use the provided `mapserver` [Docker Compose service](/deploy-with-docker ## Overview GISnav requests rasters from a WMS service which allows querying by an arbitrary bounding box (instead of by pre-computed tile). -The DEM is optionally used to input ground elevation z-coordinates to the Perspective-n-Point (PnP) problem solved by [PoseNode](/_build/sphinx/markdown/public/pose_node). If a DEM is not available, GISNav assumes a planar ground elevation, which may be sufficient when flying at higher altitudes where an isometric perspective does not significantly distort the perceived image. +The DEM is optionally used to input ground elevation z-coordinates to the Perspective-n-Point (PnP) problem solved by `PoseNode`. If a DEM is not available, GISNav assumes a planar ground elevation, which may be sufficient when flying at higher altitudes where an isometric perspective does not significantly distort the perceived image. ## Example Setups @@ -128,9 +128,7 @@ The GISNav SITL demo simulation does not actually benefit from the building heig ## SITL Simulation Quirks with DEMs -The KSQL Gazebo world buildings in the SITL simulation demo are featureless grey blocks, so any pose estimation model will most likely not use them for matching. This means any building elevation data (see [Rasterizing vector data](#rasterizing-vector-data)) will not technically be used to improve pose estimates in the SITL simulation. The below figure illustrates how LoFTR finds keypoints at an even density throughout the simulated vehicle's field of view except on the featureless buildings. - -![LoFTR does not find keypoints on featureless buildings or terrain (SITL simulation)](../../../_static/img/gisnav_sitl_featureless_buildings.jpg) +The KSQL Gazebo world buildings in the SITL simulation demo are featureless grey blocks, so any pose estimation model will most likely not use them for matching. This means any building elevation data (see [Rasterizing vector data](#rasterizing-vector-data)) will not technically be used to improve pose estimates in the SITL simulation. ## Managing onboard maps diff --git a/docs/vitepress/docs/system-architecture.md b/docs/vitepress/docs/system-architecture.md index 343a6a72..ca01eb09 100644 --- a/docs/vitepress/docs/system-architecture.md +++ b/docs/vitepress/docs/system-architecture.md @@ -8,7 +8,7 @@ This page provides an overview of both the topography of the GISNav ROS 2 packag ## ROS topography -The core ROS topography diagram below depicts how ROS messages flow through GISNav. The [API reference](/api-reference) has more detailed information on the purpose and design of each ROS node. +The core ROS topography diagram below depicts how ROS messages flow through GISNav. The API reference(under construction) has more detailed information on the purpose and design of each ROS node. ::: info `MonocularStereoImage` currently disabled This diagram depicts partially implemented but disabled visual odometry (VO) functionality (`MonocularStereoImage`). From 7ae0c459167c3b3b2d2ac6567f82e7dd9c5f1b83 Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Fri, 10 May 2024 16:08:35 +0100 Subject: [PATCH 09/30] Add refactored sphinx docs and add new build recipe into Makefile --- .dockerignore | 10 +++-- .gitignore | 10 +++-- Makefile | 8 +++- docs/sphinx/Makefile | 20 +++++++++ docs/sphinx/make.bat | 35 ++++++++++++++++ docs/sphinx/source/conf.py | 50 ++++++++++++++++++++++ docs/sphinx/source/index.rst | 51 +++++++++++++++++++++++ docs/sphinx/source/private/decorators.rst | 4 ++ docs/sphinx/source/private/messaging.rst | 3 ++ docs/sphinx/source/public/bbox_node.rst | 3 ++ docs/sphinx/source/public/constants.rst | 3 ++ docs/sphinx/source/public/gis_node.rst | 3 ++ docs/sphinx/source/public/gisnav.rst | 3 ++ docs/sphinx/source/public/nmea_node.rst | 3 ++ docs/sphinx/source/public/pose_node.rst | 3 ++ docs/sphinx/source/public/qgis_node.rst | 3 ++ docs/sphinx/source/public/stereo_node.rst | 3 ++ docs/sphinx/source/public/uorb_node.rst | 3 ++ docs/sphinx/source/test/launch.rst | 3 ++ docs/sphinx/source/test/unit.rst | 3 ++ docs/vitepress/docs/.vitepress/config.mts | 30 ++++++------- docs/vitepress/docs/index.md | 50 ++++++++++------------ gisnav/setup.py | 8 ++++ 23 files changed, 259 insertions(+), 53 deletions(-) create mode 100644 docs/sphinx/Makefile create mode 100644 docs/sphinx/make.bat create mode 100644 docs/sphinx/source/conf.py create mode 100644 docs/sphinx/source/index.rst create mode 100644 docs/sphinx/source/private/decorators.rst create mode 100644 docs/sphinx/source/private/messaging.rst create mode 100644 docs/sphinx/source/public/bbox_node.rst create mode 100644 docs/sphinx/source/public/constants.rst create mode 100644 docs/sphinx/source/public/gis_node.rst create mode 100644 docs/sphinx/source/public/gisnav.rst create mode 100644 docs/sphinx/source/public/nmea_node.rst create mode 100644 docs/sphinx/source/public/pose_node.rst create mode 100644 docs/sphinx/source/public/qgis_node.rst create mode 100644 docs/sphinx/source/public/stereo_node.rst create mode 100644 docs/sphinx/source/public/uorb_node.rst create mode 100644 docs/sphinx/source/test/launch.rst create mode 100644 docs/sphinx/source/test/unit.rst diff --git a/.dockerignore b/.dockerignore index 6a6cf062..67e99ea5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -15,9 +15,14 @@ build/ install/ log/ -# Built documentation +# VitePress docs/vitepress/docs/.vitepress/dist docs/vitepress/docs/.vitepress/build +docs/vitepress/docs/.vitepress/cache +docs/vitepress/docs/reference/ + +# Sphinx +docs/sphinx/build # coverage.py files .coverage* @@ -35,6 +40,3 @@ gisnav/test/sitl/ulog_analysis/.ipynb_checkpoints/ # node node_modules - -# vitepress -docs/docs/.vitepress/cache/ diff --git a/.gitignore b/.gitignore index 8a3193b1..c8ca0305 100644 --- a/.gitignore +++ b/.gitignore @@ -12,9 +12,14 @@ build/ install/ log/ -# Built documentation +# VitePress docs/vitepress/docs/.vitepress/dist docs/vitepress/docs/.vitepress/build +docs/vitepress/docs/.vitepress/cache +docs/vitepress/docs/reference/ + +# Sphinx +docs/sphinx/build # coverage.py files .coverage* @@ -40,6 +45,3 @@ debian # node node_modules - -# vitepress -docs/docs/.vitepress/cache/ diff --git a/Makefile b/Makefile index 657ba919..b39bda53 100644 --- a/Makefile +++ b/Makefile @@ -8,9 +8,15 @@ include docker/Makefile .PHONY: docs docs: + @cd docs/sphinx && sphinx-build -M markdown ./source ./build + @mkdir -p docs/vitepress/docs/reference && cp -r docs/sphinx/build/markdown/* docs/vitepress/docs/reference @cd docs/vitepress && npm run docs:build - @cd docs/.vitepress/dist && touch .nojekyll # for GitHub Pages + @cd docs/vitepress/docs/.vitepress/dist && touch .nojekyll # for GitHub Pages .PHONY: docs-preview docs-preview: @cd docs/vitepress && npm run docs:preview + +.PHONY: docs-dev +docs-preview: + @cd docs/vitepress && npm run docs:dev diff --git a/docs/sphinx/Makefile b/docs/sphinx/Makefile new file mode 100644 index 00000000..d0c3cbf1 --- /dev/null +++ b/docs/sphinx/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/sphinx/make.bat b/docs/sphinx/make.bat new file mode 100644 index 00000000..747ffb7b --- /dev/null +++ b/docs/sphinx/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/sphinx/source/conf.py b/docs/sphinx/source/conf.py new file mode 100644 index 00000000..fc076358 --- /dev/null +++ b/docs/sphinx/source/conf.py @@ -0,0 +1,50 @@ +import os +import sys + +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html +sys.path.insert(0, os.path.abspath("../gisnav")) + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = "GISNav" +copyright = "2024, Harri Makelin" +author = "Harri Makelin" +release = "v0.66.0" + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.coverage", + "sphinx.ext.todo", + "sphinx.ext.viewcode", + "sphinx_design", + "autodocsumm", + "sphinxcontrib.mermaid", + "sphinx_copybutton", +] +templates_path = ["_templates"] + +autoclass_content = "both" # document __init__ + +# sphinx-markdown-builder +markdown_anchor_signatures = True + +# automodule default options +autodoc_default_options = { + "autosummary": True, + "members": True, + "undoc-members": True, + "show-inheritance": True, +} + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "alabaster" +html_static_path = ["_static"] diff --git a/docs/sphinx/source/index.rst b/docs/sphinx/source/index.rst new file mode 100644 index 00000000..e518cf94 --- /dev/null +++ b/docs/sphinx/source/index.rst @@ -0,0 +1,51 @@ +************************************************** +API reference +************************************************** +Here you can find the automatically generated API reference for the +`GISNav ROS 2 package`_. Use the search bar or the below module index to find +what you are looking for. + +.. _GISNav ROS 2 package: https://github.com/hmakelin/gisnav + +.. warning:: + GISNav is under active development and stability of the public API is not + guaranteed. Use a specific commit or version tag to mitigate the impact of + breaking changes. + +.. note:: + The private API is also documented here for developer reference as the + private method docstrings may contain interesting and useful information. + The private API should not be relied on for any application integrations. + +.. toctree:: + :caption: Core nodes + + public/bbox_node + public/gis_node + public/stereo_node + public/pose_node + +.. toctree:: + :caption: Extension nodes + + public/uorb_node + public/nmea_node + public/qgis_node + +.. toctree:: + :caption: Static API + + public/constants + public/gisnav + +.. toctree:: + :caption: Tests + + test/unit + test/launch + +.. toctree:: + :caption: Private API + + private/decorators + private/messaging diff --git a/docs/sphinx/source/private/decorators.rst b/docs/sphinx/source/private/decorators.rst new file mode 100644 index 00000000..cf2d4ba5 --- /dev/null +++ b/docs/sphinx/source/private/decorators.rst @@ -0,0 +1,4 @@ +Decorators +____________________________________________________ +.. automodule:: gisnav._decorators + :exclude-members: P diff --git a/docs/sphinx/source/private/messaging.rst b/docs/sphinx/source/private/messaging.rst new file mode 100644 index 00000000..f4acb66d --- /dev/null +++ b/docs/sphinx/source/private/messaging.rst @@ -0,0 +1,3 @@ +Messaging +____________________________________________________ +.. automodule:: gisnav._messaging diff --git a/docs/sphinx/source/public/bbox_node.rst b/docs/sphinx/source/public/bbox_node.rst new file mode 100644 index 00000000..351e8fb4 --- /dev/null +++ b/docs/sphinx/source/public/bbox_node.rst @@ -0,0 +1,3 @@ +BBoxNode +____________________________________________________ +.. automodule:: gisnav.core.bbox_node diff --git a/docs/sphinx/source/public/constants.rst b/docs/sphinx/source/public/constants.rst new file mode 100644 index 00000000..de2f7502 --- /dev/null +++ b/docs/sphinx/source/public/constants.rst @@ -0,0 +1,3 @@ +Constants +____________________________________________________ +.. automodule:: gisnav.constants diff --git a/docs/sphinx/source/public/gis_node.rst b/docs/sphinx/source/public/gis_node.rst new file mode 100644 index 00000000..1adbf04f --- /dev/null +++ b/docs/sphinx/source/public/gis_node.rst @@ -0,0 +1,3 @@ +GISNode +____________________________________________________ +.. automodule:: gisnav.core.gis_node diff --git a/docs/sphinx/source/public/gisnav.rst b/docs/sphinx/source/public/gisnav.rst new file mode 100644 index 00000000..d6af6362 --- /dev/null +++ b/docs/sphinx/source/public/gisnav.rst @@ -0,0 +1,3 @@ +Entry points +____________________________________________________ +.. automodule:: gisnav diff --git a/docs/sphinx/source/public/nmea_node.rst b/docs/sphinx/source/public/nmea_node.rst new file mode 100644 index 00000000..23592878 --- /dev/null +++ b/docs/sphinx/source/public/nmea_node.rst @@ -0,0 +1,3 @@ +NMEANode +____________________________________________________ +.. automodule:: gisnav.extensions.nmea_node diff --git a/docs/sphinx/source/public/pose_node.rst b/docs/sphinx/source/public/pose_node.rst new file mode 100644 index 00000000..ff8cef6c --- /dev/null +++ b/docs/sphinx/source/public/pose_node.rst @@ -0,0 +1,3 @@ +PoseNode +____________________________________________________ +.. automodule:: gisnav.core.pose_node diff --git a/docs/sphinx/source/public/qgis_node.rst b/docs/sphinx/source/public/qgis_node.rst new file mode 100644 index 00000000..9eeee947 --- /dev/null +++ b/docs/sphinx/source/public/qgis_node.rst @@ -0,0 +1,3 @@ +QGISNode +____________________________________________________ +.. automodule:: gisnav.extensions.qgis_node diff --git a/docs/sphinx/source/public/stereo_node.rst b/docs/sphinx/source/public/stereo_node.rst new file mode 100644 index 00000000..37480888 --- /dev/null +++ b/docs/sphinx/source/public/stereo_node.rst @@ -0,0 +1,3 @@ +StereoNode +____________________________________________________ +.. automodule:: gisnav.core.stereo_node diff --git a/docs/sphinx/source/public/uorb_node.rst b/docs/sphinx/source/public/uorb_node.rst new file mode 100644 index 00000000..af7c75ba --- /dev/null +++ b/docs/sphinx/source/public/uorb_node.rst @@ -0,0 +1,3 @@ +UORBNode +____________________________________________________ +.. automodule:: gisnav.extensions.uorb_node diff --git a/docs/sphinx/source/test/launch.rst b/docs/sphinx/source/test/launch.rst new file mode 100644 index 00000000..757c0426 --- /dev/null +++ b/docs/sphinx/source/test/launch.rst @@ -0,0 +1,3 @@ +Launch tests +____________________________________________________ +.. automodule:: test.launch.testcases diff --git a/docs/sphinx/source/test/unit.rst b/docs/sphinx/source/test/unit.rst new file mode 100644 index 00000000..c547cc27 --- /dev/null +++ b/docs/sphinx/source/test/unit.rst @@ -0,0 +1,3 @@ +Unit tests +____________________________________________________ +.. automodule:: test.unit diff --git a/docs/vitepress/docs/.vitepress/config.mts b/docs/vitepress/docs/.vitepress/config.mts index 746e121c..e98dacbc 100644 --- a/docs/vitepress/docs/.vitepress/config.mts +++ b/docs/vitepress/docs/.vitepress/config.mts @@ -14,7 +14,7 @@ export default withMermaid({ nav: [ { text: 'Home', link: '/' }, { text: 'Guide', link: '/README' }, - { text: 'API Reference', link: '/_build/sphinx/markdown/index' } + { text: 'API Reference', link: '/reference/index' } ], outline: { level: 'deep' @@ -61,42 +61,42 @@ export default withMermaid({ ] } ], - '/_build/sphinx/markdown/': [ + '/reference/': [ { text: 'Core nodes', items: [ - { text: 'BBoxNode', link: '/_build/sphinx/markdown/public/bbox_node' }, - { text: 'GISNode', link: '/_build/sphinx/markdown/public/gis_node' }, - { text: 'StereoNode', link: '/_build/sphinx/markdown/public/stereo_node' }, - { text: 'PoseNode', link: '/_build/sphinx/markdown/public/pose_node' }, + { text: 'BBoxNode', link: '/reference/public/bbox_node' }, + { text: 'GISNode', link: '/reference/public/gis_node' }, + { text: 'StereoNode', link: '/reference/public/stereo_node' }, + { text: 'PoseNode', link: '/reference/public/pose_node' }, ] }, { text: 'Extension nodes', items: [ - { text: 'UORBNode', link: '/_build/sphinx/markdown/public/uorb_node' }, - { text: 'NMEANode', link: '/_build/sphinx/markdown/public/nmea_node' }, - { text: 'QGISNode', link: '/_build/sphinx/markdown/public/qgis_node' }, + { text: 'UORBNode', link: '/reference/public/uorb_node' }, + { text: 'NMEANode', link: '/reference/public/nmea_node' }, + { text: 'QGISNode', link: '/reference/public/qgis_node' }, ] }, { text: 'Testing', items: [ - { text: 'Unit tests', link: '/_build/sphinx/markdown/test/unit' }, - { text: 'Launch tests', link: '/_build/sphinx/markdown/test/launch' }, + { text: 'Unit tests', link: '/reference/test/unit' }, + { text: 'Launch tests', link: '/reference/test/launch' }, ] }, { text: 'Private API', items: [ - { text: 'Decorators', link: '/_build/sphinx/markdown/private/decorators' }, - { text: 'Transformations', link: '/_build/sphinx/markdown/private/transformations' }, + { text: 'Decorators', link: '/reference/private/decorators' }, + { text: 'Transformations', link: '/reference/private/transformations' }, ] }, { items: [ - { text: 'Constants', link: '/_build/sphinx/markdown/public/constants' }, - { text: 'Entry points', link: '/_build/sphinx/markdown/public/gisnav' }, + { text: 'Constants', link: '/reference/public/constants' }, + { text: 'Entry points', link: '/reference/public/gisnav' }, ] }, ] diff --git a/docs/vitepress/docs/index.md b/docs/vitepress/docs/index.md index 39c3d733..b0fb23f4 100644 --- a/docs/vitepress/docs/index.md +++ b/docs/vitepress/docs/index.md @@ -15,35 +15,27 @@ hero: link: "/README" - theme: alt text: "API Reference" - link: "/_build/sphinx/markdown/index" - -features: - - title: "GNSS-free navigation" - details: "Operates independent of GNSS systems such as GPS, providing secondary navigation in environments where GNSS signals are weak or unavailable." - - - title: "Offline navigation" - details: "Designed to function without any external connections, ensuring continuous operation even in remote or network-restricted areas." - - - title: "Optical terrain-matching" - details: "Provides a precise global position by visually comparing frames from the vehicle's nadir-facing camera to a map of the UAVs approximate global position retrieved from an onboard GIS server." - - - title: "FOSS with MIT License" - details: "Open source under the permissive MIT license, allowing for free use, modification, and distribution." - - - title: "Monocular Camera Compatibility" - details: "Compatible with any standard monocular camera, facilitating easy adoption and integration with existing equipment, without requiring specialized hardware." - - - title: "MAVLink, NMEA and uORB Protocols" - details: "Supports integration with popular autopilot systems like PX4 and ArduPilot through MAVLink, NMEA and uORB protocols." - - - title: "Secondary GPS Over Serial Port (NMEA)" - details: "Functions as a reliable secondary GPS, easily integrating over serial connections without the need for firmware modifications." - - - title: "Simulation with Gazebo" - details: "Includes support for Gazebo simulations, enabling developers to test and refine drone operations in a fully controlled virtual environment." - - - title: "ROS 2 Integration" - details: "Integrates with the ROS 2 ecosystem, providing easy extensibility." + link: "reference/index" + +#features: +# - title: "GNSS-free navigation" +# details: "Operates independent of GNSS systems such as GPS, providing secondary navigation in environments where GNSS signals are weak or unavailable." +# - title: "Offline navigation" +# details: "Designed to function without any external connections, ensuring continuous operation even in remote or network-restricted areas." +# - title: "Optical terrain-matching" +# details: "Provides a precise global position by visually comparing frames from the vehicle's nadir-facing camera to a map of the UAVs approximate global position retrieved from an onboard GIS server." +# - title: "FOSS with MIT License" +# details: "Open source under the permissive MIT license, allowing for free use, modification, and distribution." +# - title: "Monocular Camera Compatibility" +# details: "Compatible with any standard monocular camera, facilitating easy adoption and integration with existing equipment, without requiring specialized hardware." +# - title: "MAVLink, NMEA and uORB Protocols" +# details: "Supports integration with popular autopilot systems like PX4 and ArduPilot through MAVLink, NMEA and uORB protocols." +# - title: "Secondary GPS Over Serial Port (NMEA)" +# details: "Functions as a reliable secondary GPS, easily integrating over serial connections without the need for firmware modifications." +# - title: "Simulation with Gazebo" +# details: "Includes support for Gazebo simulations, enabling developers to test and refine drone operations in a fully controlled virtual environment." +# - title: "ROS 2 Integration" +# details: "Integrates with the ROS 2 ecosystem, providing easy extensibility." --- diff --git a/gisnav/setup.py b/gisnav/setup.py index 9c0f3876..7bbd2bf8 100644 --- a/gisnav/setup.py +++ b/gisnav/setup.py @@ -124,15 +124,23 @@ def parse_package_data(cls, package_file: str) -> PackageData: "qgis_node": ["psycopg2"], "dev": [ "aiohttp", + "autodocsumm", "coverage", "docutils>=0.17", "jupyter", "lxml", "mavsdk", "pre-commit", + "pygments", "pytest", "python-dateutil>=2.8.2", "pyyaml", + "sphinx-copybutton", + "sphinx-design" "sphinx-markdown-builder==0.6.6", + "Sphinx-Substitution-Extensions", + "sphinxcontrib-mermaid", + "sphinxcontrib-video", + "Sphinx", "types-PyYAML", ], }, From 8d9317cee43dd5c6a5a9ab5d2fbb267f8fe7bfbd Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Fri, 10 May 2024 20:36:28 +0100 Subject: [PATCH 10/30] Update BBoxNode docstrings --- docs/sphinx/source/index.rst | 18 ++-- gisnav/gisnav/constants.py | 13 +++ gisnav/gisnav/core/bbox_node.py | 142 +++++++++++--------------------- 3 files changed, 67 insertions(+), 106 deletions(-) diff --git a/docs/sphinx/source/index.rst b/docs/sphinx/source/index.rst index e518cf94..8c9d5f49 100644 --- a/docs/sphinx/source/index.rst +++ b/docs/sphinx/source/index.rst @@ -1,21 +1,13 @@ ************************************************** API reference ************************************************** -Here you can find the automatically generated API reference for the -`GISNav ROS 2 package`_. Use the search bar or the below module index to find -what you are looking for. +Here you can find the automatically generated API reference for the GISNav ROS 2 package. Use the search bar or the below module index to find what you are looking for. -.. _GISNav ROS 2 package: https://github.com/hmakelin/gisnav +> [!WARNING] Unstable API +> GISNav is under active development and the public API is unstabe. Use a specific commit or version tag to mitigate the impact of breaking changes. -.. warning:: - GISNav is under active development and stability of the public API is not - guaranteed. Use a specific commit or version tag to mitigate the impact of - breaking changes. - -.. note:: - The private API is also documented here for developer reference as the - private method docstrings may contain interesting and useful information. - The private API should not be relied on for any application integrations. +> [!NOTE] Private API +> The private API is also documented here for developer reference as the private method docstrings may contain useful information. The private API should not be relied on for any integrations. .. toctree:: :caption: Core nodes diff --git a/gisnav/gisnav/constants.py b/gisnav/gisnav/constants.py index 5d268422..9f090750 100644 --- a/gisnav/gisnav/constants.py +++ b/gisnav/gisnav/constants.py @@ -80,6 +80,19 @@ ROS_TOPIC_IMAGE: Final = "/camera/image_raw" """Name of ROS topic for :class:`sensor_msgs.msg.Image` messages""" +ROS_TOPIC_MAVROS_GLOBAL_POSITION = "/mavros/global_position/global" +"""MAVROS topic for vehicle :class:`.NavSatFix`""" + +ROS_TOPIC_MAVROS_LOCAL_POSITION = "/mavros/local_position/pose" +"""MAVROS topic for vehicle :class:`.PoseStamped` in EKF local frame""" + +ROS_TOPIC_MAVROS_GIMBAL_DEVICE_ATTITUDE_STATUS = ( + "/mavros/gimbal_control/device/attitude_status" +) +"""MAVROS topic for vehicle :class:`.GimbalDeviceAttitudeStatus` message +(MAVLink Gimbal protocl v2) +""" + DELAY_DEFAULT_MS: Final = 2000 """Max acceptable delay for things like global position""" diff --git a/gisnav/gisnav/core/bbox_node.py b/gisnav/gisnav/core/bbox_node.py index dad64b0a..d0c4b14e 100644 --- a/gisnav/gisnav/core/bbox_node.py +++ b/gisnav/gisnav/core/bbox_node.py @@ -1,34 +1,7 @@ -"""This module contains :class:`.BBoxNode`, a :term:`ROS` node for computing -and publishing a :term:`bounding box` of the :term:`camera's ` -ground-projected :term:`field of view `. This bounding box is used by -:class:`.GISNode` to retrieve orthoimagery for the :term:`vehicle's ` -approximate :term:`global position`. - -The below graph shows :class:`.BBoxNode` in the computational graph. - -.. mermaid:: - :caption: :class:`.BBoxNode` computational graph - - graph LR - - subgraph MAVROS - navsatfix[mavros/global_position/global] - vehicle_pose[mavros/local_position/pose] - gimbal_device_attitude_status[mavros/global_position/global] - end - - subgraph gscam - camera_info[camera/camera_info] - end - - subgraph BBoxNode - bounding_box[gisnav/bbox_node/fov/bounding_box] - end - - navsatfix -->|sensor_msgs/NavSatFix| BBoxNode - vehicle_pose -->|geometry_msgs/PoseStamped| BBoxNode - camera_info -->|sensor_msgs/CameraInfo| BBoxNode - bounding_box -->|geographic_msgs/BoundingBox| GISNode:::hidden. +"""This module contains :class:`.BBoxNode`, a ROS node that computes and +publishes a bounding box of the camera's ground-projected field of view. The +bounding box is used by :class:`.GISNode` to retrieve orthoimagery for the +vehicle's approximate global position. """ from typing import Final, Optional @@ -49,19 +22,26 @@ from .. import _transformations as messaging from .._decorators import ROS, narrow_types from ..constants import ( - DELAY_DEFAULT_MS, ROS_TOPIC_CAMERA_INFO, + ROS_TOPIC_MAVROS_GIMBAL_DEVICE_ATTITUDE_STATUS, + ROS_TOPIC_MAVROS_GLOBAL_POSITION, + ROS_TOPIC_MAVROS_LOCAL_POSITION, ROS_TOPIC_RELATIVE_FOV_BOUNDING_BOX, FrameID, ) class BBoxNode(Node): - """Publishes :class:`.BoundingBox` of the :term:`camera's ` - ground-projected :term:`field of view `""" + """Publishes a :class:`.BoundingBox` of the camera's ground-projected FOV + + > [!IMPORTANT] MAVLink Gimbal Protocol v2 + > If the MAVLink gimbal protocol v2 :class:`.GimbalDeviceAttitudeStatus` message is + > available, only the ``flags`` value of 12 i.e. bit mask 1100 (horizon-locked + > pitch and roll, floating yaw) is supported. + """ _ROS_PARAM_DESCRIPTOR_READ_ONLY: Final = ParameterDescriptor(read_only=True) - """A read only ROS parameter descriptor""" + """A read-only ROS parameter descriptor""" def __init__(self, *args, **kwargs): """Class initializer @@ -85,29 +65,20 @@ def __init__(self, *args, **kwargs): self._tf_listener = tf2_ros.TransformListener(self._tf_buffer, self) def _nav_sat_fix_cb(self, msg: NavSatFix) -> None: - """Callback for the :term:`global position` message from the - :term:`navigation filter` - """ + """Callback for the global position message from the EKF""" self.fov_bounding_box @property - @ROS.max_delay_ms(DELAY_DEFAULT_MS) @ROS.subscribe( - "/mavros/global_position/global", + ROS_TOPIC_MAVROS_GLOBAL_POSITION, QoSPresetProfiles.SENSOR_DATA.value, callback=_nav_sat_fix_cb, ) def nav_sat_fix(self) -> Optional[NavSatFix]: - """Vehicle GPS fix, or None if unknown or too old""" + """Vehicle GNSS fix, or None if unknown""" def _vehicle_pose_cb(self, msg: PoseStamped) -> None: - """Callback for the :term:`vehicle` :term:`local position` message from - the :term:`navigation filter` - - The local position is expected to have a ROS header frame_id called 'map', - and this 'map' frame is assumed to be the :term:`EKF` local frame. - The 'map' frame is assumed to follow the :term:`ENU` axes convention. - """ + """Callback for vehicle local position message from the EKF""" assert msg.header.frame_id == "map", ( f"Unexpected frame_id for vehicle local frame received via vehicle " f"local position pose topic: {msg.header.frame_id} (expected 'map')" @@ -119,49 +90,44 @@ def _vehicle_pose_cb(self, msg: PoseStamped) -> None: self.broadcaster.sendTransform([transform_base_link]) @property - # @ROS.max_delay_ms(messaging.DELAY_FAST_MS) # TODO: @ROS.subscribe( - "/mavros/local_position/pose", + ROS_TOPIC_MAVROS_LOCAL_POSITION, QoSPresetProfiles.SENSOR_DATA.value, callback=_vehicle_pose_cb, ) def vehicle_pose(self) -> Optional[PoseStamped]: - """Vehicle local :term:`pose`, or None if not available or too old""" + """Vehicle pose in EKF local frame, or None unknown""" @property - # @ROS.max_delay_ms(messaging.DELAY_DEFAULT_MS) - camera info has no header (?) @ROS.subscribe( ROS_TOPIC_CAMERA_INFO, QoSPresetProfiles.SENSOR_DATA.value, ) def camera_info(self) -> Optional[CameraInfo]: - """Camera info for determining appropriate :attr:`.orthoimage` resolution""" + """Camera info for determining appropriate orthoimage resolution, or None + unknown + """ @property @ROS.publish( ROS_TOPIC_RELATIVE_FOV_BOUNDING_BOX, QoSPresetProfiles.SENSOR_DATA.value ) def fov_bounding_box(self) -> Optional[BoundingBox]: - """:class:`.BoundingBox` of the :term:`camera's ` ground-projected - :term:`field of view `. - """ + """:class:`.BoundingBox` of the camera's ground-projected FOV""" @narrow_types(self) def _fov_and_principal_point_on_ground_plane( transform: TransformStamped, camera_info: CameraInfo, ) -> Optional[np.ndarray]: - """Projects :term:`camera` principal point and :term:`FOV` corners - on ground plane + """Projects camera principal point and FOV corners onto ground plane - .. note:: - Assumes ground is a flat plane, does not take :term:`DEM` into account + Assumes ground is a flat plane, does not take DEM into account :return: Numpy array of FOV corners and principal point projected onto - ground (vehicle :term:`local position` z==0) plane in following - order: top-left, top-right, bottom-right, bottom-left, principal point. - Shape is (5, 2). Coordinates are meters in - :term:`local tangent plane ` :term:`ENU`. + ground plane (z=0 in EKF local frame) in following order: top-left, + top-right, bottom-right, bottom-left, principal point. Shape is (5, 2). + Coordinates are meters. """ R = tf_transformations.quaternion_matrix( tuple(messaging.as_np_quaternion(transform.transform.rotation)) @@ -216,11 +182,10 @@ def _fov_and_principal_point_on_ground_plane( def _enu_to_latlon( bbox_coords: np.ndarray, navsatfix: NavSatFix ) -> Optional[np.ndarray]: - """Convert :term:`ENU` local tangent plane coordinates to - latitude and longitude. + """Convert EKF local frame ENU coordinates into WGS 84 coordinates :param bbox_coords: A bounding box in local ENU frame (units in meters) - :param navsatfix: :term:`Vehicle` :term:`global position` + :param navsatfix: Vehicle global position :return: Same bounding box in WGS 84 coordinates """ @@ -253,9 +218,7 @@ def _determine_utm_zone(longitude): @narrow_types(self) def _square_bounding_box(enu_coords: np.ndarray) -> np.ndarray: - """ - Adjust the given bounding box to ensure it's square in the ENU local - tangent plane (meters). + """Adjusts given bounding box to ensure it's square in the local frame Adds padding in X (easting) and Y (northing) directions to ensure camera FOV is fully enclosed by the bounding box, and to reduce need @@ -304,13 +267,13 @@ def _square_bounding_box(enu_coords: np.ndarray) -> np.ndarray: def _bounding_box( fov_local_enu: np.ndarray, ) -> BoundingBox: - """Create a BoundingBox :term:`message` that envelops the provided - :term:`FOV` coordinates. + """Create a :class:`.BoundingBox` message that envelops the provided + FOV coordinates. - fov_local_enu: A 4x2 numpy array where N is the number of points, - and each row represents [longitude, latitude]. + :param fov_local_enu: A 4x2 numpy array where N is the number of points, + and each row represents [longitude, latitude]. - Returns: geographic_msgs.msg.BoundingBox + :return: A :class:`.BoundingBox` """ assert fov_local_enu.shape == (4, 2) @@ -361,10 +324,9 @@ def _bounding_box( def _gimbal_device_attitude_status_cb( self, msg: GimbalDeviceAttitudeStatus ) -> None: - """Callback for :class:`mavros_msgs.msg.GimbalDeviceAttitudeStatus` message + """Callback for :class:`.GimbalDeviceAttitudeStatus` message - :param msg: :class:`mavros_msgs.msg.GimbalDeviceAttitudeStatus` message - from MAVROS + :param msg: :class:`.GimbalDeviceAttitudeStatus` message from MAVROS """ def _normalize_quaternion(q: Quaternion) -> Quaternion: @@ -382,16 +344,13 @@ def _publish_camera_transform( ) -> None: """Publish camera ENU pose to transformations - .. note:: - * Current implementation assumes camera faces directly down from - :term:`vehicle` body if GimbalDeviceAttitudeStatus :term:`message` - (:term:`MAVLink` gimbal protocol v2) is not available. Should - probably not be used for estimating :term:`vehicle` - :term:`orientation`. - * If GimbalDeviceAttitudeStatus :term:`message` - (:term:`MAVLink` gimbal protocol v2) is available, only the flags - value of 12 i.e. bit mask 1100 (horizon-locked pitch and roll, - floating yaw) is supported. + * Current implementation assumes camera faces directly down from + vehicle body if GimbalDeviceAttitudeStatus message (MAVLink` gimbal + protocol v2) is not available. Should probably not be used for estimating + vehicle orientation + * If GimbalDeviceAttitudeStatus message is available, only the flags + value of 12 i.e. bit mask 1100 (horizon-locked pitch and roll, + floating yaw) is supported. :param vehicle_pose: :param gimbal_device_attitude_status: @@ -454,13 +413,10 @@ def _publish_camera_transform( self.fov_bounding_box @property - # @ROS.max_delay_ms(messaging.DELAY_FAST_MS) # TODO re-enable @ROS.subscribe( - "/mavros/gimbal_control/device/attitude_status", + ROS_TOPIC_MAVROS_GIMBAL_DEVICE_ATTITUDE_STATUS, QoSPresetProfiles.SENSOR_DATA.value, callback=_gimbal_device_attitude_status_cb, ) def gimbal_device_attitude_status(self) -> Optional[GimbalDeviceAttitudeStatus]: - """:term:`Camera` :term:`FRD` :term:`orientation`, or None if not available - or too old - """ + """Camera FRD orientation, or None unknown""" From 82f6fd2d8d226635cd2ff64cca09bfde8f006d16 Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 06:46:03 +0100 Subject: [PATCH 11/30] Update GISNode docstrings --- gisnav/gisnav/core/bbox_node.py | 13 +- gisnav/gisnav/core/gis_node.py | 226 ++++++++++++-------------------- 2 files changed, 88 insertions(+), 151 deletions(-) diff --git a/gisnav/gisnav/core/bbox_node.py b/gisnav/gisnav/core/bbox_node.py index d0c4b14e..c81e6418 100644 --- a/gisnav/gisnav/core/bbox_node.py +++ b/gisnav/gisnav/core/bbox_node.py @@ -75,7 +75,7 @@ def _nav_sat_fix_cb(self, msg: NavSatFix) -> None: callback=_nav_sat_fix_cb, ) def nav_sat_fix(self) -> Optional[NavSatFix]: - """Vehicle GNSS fix, or None if unknown""" + """Vehicle GNSS fix from FCU, or None if unknown""" def _vehicle_pose_cb(self, msg: PoseStamped) -> None: """Callback for vehicle local position message from the EKF""" @@ -96,7 +96,7 @@ def _vehicle_pose_cb(self, msg: PoseStamped) -> None: callback=_vehicle_pose_cb, ) def vehicle_pose(self) -> Optional[PoseStamped]: - """Vehicle pose in EKF local frame, or None unknown""" + """Vehicle pose in EKF local frame from FCU, or None unknown""" @property @ROS.subscribe( @@ -104,8 +104,9 @@ def vehicle_pose(self) -> Optional[PoseStamped]: QoSPresetProfiles.SENSOR_DATA.value, ) def camera_info(self) -> Optional[CameraInfo]: - """Camera info for determining appropriate orthoimage resolution, or None - unknown + """Subscribed camera info, or None if unknown + + Camera intrinsics from this message are needed for the FOV projection """ @property @@ -113,7 +114,7 @@ def camera_info(self) -> Optional[CameraInfo]: ROS_TOPIC_RELATIVE_FOV_BOUNDING_BOX, QoSPresetProfiles.SENSOR_DATA.value ) def fov_bounding_box(self) -> Optional[BoundingBox]: - """:class:`.BoundingBox` of the camera's ground-projected FOV""" + """Published bounding box of the camera's ground-projected FOV""" @narrow_types(self) def _fov_and_principal_point_on_ground_plane( @@ -419,4 +420,4 @@ def _publish_camera_transform( callback=_gimbal_device_attitude_status_cb, ) def gimbal_device_attitude_status(self) -> Optional[GimbalDeviceAttitudeStatus]: - """Camera FRD orientation, or None unknown""" + """Camera orientation from FCU, or None unknown""" diff --git a/gisnav/gisnav/core/gis_node.py b/gisnav/gisnav/core/gis_node.py index b667d2cf..e81ea8ef 100644 --- a/gisnav/gisnav/core/gis_node.py +++ b/gisnav/gisnav/core/gis_node.py @@ -1,32 +1,5 @@ -"""This module contains :class:`.GISNode`, a :term:`ROS` node for retrieving and -publishing geographic information and images. - -:class:`.GISNode` manages geographic information for the system, including -downloading, storing, and publishing the :term:`orthophoto` and optional -:term:`DEM` :term:`raster`. These rasters are retrieved from an :term:`onboard` -:term:`WMS` based on the projected location of the :term:`camera` field of view. - -.. mermaid:: - :caption: :class:`.GISNode` computational graph - - graph LR - subgraph GISNode - image[gisnav/gis_node/image] - geotransform[gisnav/gis_node/geotransform] - end - - subgraph BBoxNode - bounding_box[gisnav/bbox_node/fov/bounding_box] - end - - subgraph gscam - camera_info[camera/camera_info] - end - - camera_info -->|sensor_msgs/CameraInfo| GISNode - bounding_box -->|geographic_msgs/BoundingBox| GISNode - geotransform -->|sensor_msgs/PointCloud2| NMEANode - image -->|sensor_msgs/Image| StereoNode:::hidden +"""This module contains :class:`.GISNode`, a ROS node that requests +orthoimagery from the GIS and publishes it to ROS. """ from copy import deepcopy from typing import IO, Final, List, Optional, Tuple @@ -62,118 +35,95 @@ class GISNode(Node): - """Publishes the :term:`orthophoto` and optional :term:`DEM` as a single - :term:`stacked ` :class:`.Image` message. - - .. warning:: - ``OWSLib``, *as of version 0.25.0*, uses the Python ``requests`` - library under the hood but does not document the various exceptions it - raises that are passed through by ``OWSLib`` as part of its public API. - The :meth:`.get_map` method is therefore expected to raise `errors and - exceptions - `_ - specific to the ``requests`` library. - - These errors and exceptions are not handled by the :class:`.GISNode` - to avoid a direct dependency on ``requests``. They are therefore handled - as unexpected errors. + """Publishes the orthoimage, DEM, and their CRS as a single, atomic ROS message. + + > [!WARNING] OWSLib exception handling + > ``OWSLib``, *as of version 0.25.0*, uses the Python ``requests`` library under + > the hood but does not document the various exceptions it raises that are passed + > through by ``OWSLib`` as part of its public API. The private method that calls the + > ``OWSLib`` method that implements the GetMap call is therefore expected to raise > `errors and exceptions `_ + > specific to the ``requests`` library. + > + > These errors and exceptions are not handled by the :class:`.GISNode` to avoid a + > direct dependency on ``requests``. They are therefore handled as unexpected + > errors. """ # noqa: E501 ROS_D_URL = "http://127.0.0.1:80/wms" - """Default WMS URL + """Default value for :attr:`.wms_url` - When :ref:`deploying Docker Compose services ` the host - name of the MapServer container ``gisnav-mapserver-1`` should be used instead. - This should already be configured in the `default launch parameter file - `_ - which overrides this default value. + When :ref:`deploying Docker Compose services ` the + Docker DNS host name of the MapServer container ``gisnav-mapserver-1`` should be + used in the URL. This should already be configured in the `default launch parameter + file `_ which overrides this default value. """ ROS_D_VERSION = "1.3.0" - """Default WMS version""" + """Default value for :attr:`.wms_version`""" ROS_D_TIMEOUT = 10 - """Default WMS GetMap request timeout in seconds""" + """Default value for :attr:`.wms_timeout`""" ROS_D_PUBLISH_RATE = 1.0 - """Default publish rate for :class:`.OrthoImage3D` messages in Hz""" + """Default value for :attr:`.publish_rate`""" ROS_D_WMS_POLL_RATE = 0.1 - """Default WMS connection status poll rate in Hz""" + """Default value for :attr:`.wms_poll_rate`""" ROS_D_LAYERS = ["imagery"] - """Default WMS GetMap request layers parameter for image raster + """Default value for :attr:`.wms_layers` - .. note:: - The combined layers should cover the flight area of the vehicle at high - resolution. Typically this list would have just one layer for high - resolution aerial or satellite imagery. + > [!TIP] + > The combined layers should cover the flight area of the vehicle at high + > resolution. Typically this list would have just one layer for high resolution + > aerial or satellite imagery. """ ROS_D_DEM_LAYERS = ["dem"] - """Default WMS GetMap request layers parameter for DEM raster - - .. note:: - This is an optional elevation layer that makes the pose estimation more - accurate especially when flying at low altitude. It should be a grayscale - raster with pixel values corresponding meters relative to vertical datum. - Vertical datum can be whatever system is used (e.g. USGS DEM uses NAVD 88), - although it is assumed to be flat across the flight mission area. + """Default value for :attr:`.wms_dem_layers` + + > [!TIP] + > This is an optional elevation layer that makes the pose estimation more accurate + > especially when flying at low altitude. It should be a grayscale raster with + > pixel values corresponding meters relative to vertical datum. Vertical datum can + > be whatever system is used (e.g. USGS DEM uses NAVD 88), although it is assumed + > to be flat across the flight mission area. """ ROS_D_STYLES = [""] - """Default WMS GetMap request styles parameter for image raster + """Default value for :attr:`.wms_styles` - .. note:: - Must be same length as :py:attr:`.ROS_D_LAYERS`. Use empty strings for - server default styles. + > [!TIP] + > Must be same length as :py:attr:`.ROS_D_LAYERS`. Use empty strings for server + > default styles. """ ROS_D_DEM_STYLES = [""] - """Default WMS GetMap request styles parameter for DEM raster + """Default value for :attr:`.wms_dem_styles` - .. note:: - Must be same length as :py:attr:`.ROS_D_DEM_LAYERS`. Use empty strings - for server default styles. + > [!TIP] + > Must be same length as :py:attr:`.ROS_D_DEM_LAYERS`. Use empty strings for server + > default styles. """ ROS_D_SRS = "EPSG:4326" - """Default WMS GetMap request SRS parameter""" + """Default value for :attr:`.wms_srs`""" ROS_D_IMAGE_FORMAT = "image/jpeg" - """Default WMS GetMap request image format""" + """Default value for :attr:`.wms_format`""" ROS_D_IMAGE_TRANSPARENCY = False - """Default WMS GetMap request image transparency + """Default value for :attr:`.wms_transparency` - .. note:: - Not supported by jpeg format + > [!NOTE] + > This parameter is not supported by jpeg format """ ROS_D_MAP_OVERLAP_UPDATE_THRESHOLD = 0.85 - """Required overlap ratio between suggested new :term:`bounding box` and - current :term:`orthoimage` bounding box, under which a new map will be - requested. - """ - - ROS_D_MAP_UPDATE_UPDATE_DELAY = 1 - """Default delay in seconds for throttling WMS GetMap requests - - .. todo:: - TODO: ROS_D_MAP_UPDATE_UPDATE_DELAY not currently used but could be - useful (old param from basenode) - - When the camera is mounted on a gimbal and is not static, this delay should - be set quite low to ensure that whenever camera field of view is moved to - some other location, the map update request will follow very soon after. - The field of view of the camera projected on ground generally moves - *much faster* than the vehicle itself. - - .. note:: - This parameter provides a hard upper limit for WMS GetMap request - frequency. Even if this parameter is set low, WMS GetMap requests will - likely be much less frequent because they will throttled by the - conditions set in :meth:`._should_request_new_map`. + """The default ROS parameter for the minimum required overlap ratio between the + bounding boxes of the current and new orthoimages. If the overlap is below this + threshold, a new map request is triggered. """ _ROS_PARAM_DESCRIPTOR_READ_ONLY: Final = ParameterDescriptor(read_only=True) @@ -213,67 +163,64 @@ def __init__(self, *args, **kwargs): @property @ROS.parameter(ROS_D_URL, descriptor=_ROS_PARAM_DESCRIPTOR_READ_ONLY) def wms_url(self) -> Optional[str]: - """WMS client endpoint URL""" + """ROS WMS client endpoint URL parameter""" @property @ROS.parameter(ROS_D_VERSION, descriptor=_ROS_PARAM_DESCRIPTOR_READ_ONLY) def wms_version(self) -> Optional[str]: - """Used WMS protocol version""" + """ROS WMS protocol version parameter""" @property @ROS.parameter(ROS_D_TIMEOUT, descriptor=_ROS_PARAM_DESCRIPTOR_READ_ONLY) def wms_timeout(self) -> Optional[int]: - """WMS request timeout in seconds""" + """ROS WMS request timeout [seconds] parameter""" @property @ROS.parameter(ROS_D_LAYERS) def wms_layers(self) -> Optional[List[str]]: - """WMS request layers for :term:`orthophoto` :term:`GetMap` requests""" + """ROS parameter for WMS request layers for orthoimagery GetMap requests""" @property @ROS.parameter(ROS_D_DEM_LAYERS) def wms_dem_layers(self) -> Optional[List[str]]: - """WMS request layers for :term:`DEM` :term:`GetMap` requests""" + """ROS WMS parameter for WMS request layers for DEM GetMap requests""" @property @ROS.parameter(ROS_D_STYLES) def wms_styles(self) -> Optional[List[str]]: - """WMS request styles for :term:`orthophoto` :term:`GetMap` requests""" + """ROS parameter for WMS request styles for orthoimegry GetMap requests""" @property @ROS.parameter(ROS_D_DEM_STYLES) def wms_dem_styles(self) -> Optional[List[str]]: - """WMS request styles for :term:`DEM` :term:`GetMap` requests""" + """ROS parameter for WMS request styles for :DEM GetMap requests""" @property @ROS.parameter(ROS_D_SRS) def wms_srs(self) -> Optional[str]: - """WMS request :term:`SRS` for all :term:`GetMap` requests""" + """ROS parameter for WMS request CRS for all GetMap requests""" @property @ROS.parameter(ROS_D_IMAGE_TRANSPARENCY) def wms_transparency(self) -> Optional[bool]: - """WMS request transparency for all :term:`GetMap` requests""" + """ROS parameter for WMS request transparency for all GetMap requests""" @property @ROS.parameter(ROS_D_IMAGE_FORMAT) def wms_format(self) -> Optional[str]: - """WMS request format for all :term:`GetMap` requests""" + """ROS parameter for WMS request format for all GetMap requests""" @property @ROS.parameter(ROS_D_WMS_POLL_RATE, descriptor=_ROS_PARAM_DESCRIPTOR_READ_ONLY) def wms_poll_rate(self) -> Optional[float]: - """:term:`WMS` connection status poll rate in Hz""" + """ROS parameter for WMS connection status poll rate in Hz""" @property @ROS.parameter(ROS_D_MAP_OVERLAP_UPDATE_THRESHOLD) def min_map_overlap_update_threshold(self) -> Optional[float]: - """Required :term:`bounding box` overlap ratio for new :term:`GetMap` - requests - - If the overlap between the candidate new bounding box and the current - :term:`orthoimage` bounding box is below this value, a new map will be - requested. + """ROS parameter for the minimum required overlap ratio between the bounding + boxes of the current and new orthoimages. If the overlap is below this + threshold, a new map request is triggered. """ @property @@ -300,7 +247,7 @@ def _create_publish_timer(self, publish_rate: float) -> Timer: @narrow_types def _create_connect_wms_timer(self, poll_rate: float) -> Timer: - """Returns a timer that reconnects :term:`WMS` client when needed + """Returns a timer that reconnects the WMS client when needed :param poll_rate: WMS connection status poll rate for the timer (in Hz) :return: The :class:`.Timer` instance @@ -317,7 +264,7 @@ def _create_connect_wms_timer(self, poll_rate: float) -> Timer: @property def _publish_timer(self) -> Timer: - """:class:`gisnav_msgs.msg.OrthoImage3D` publish and map update timer""" + """:attr:`.orthoimage` publish and map update timer""" return self.__publish_timer @_publish_timer.setter @@ -325,12 +272,7 @@ def _publish_timer(self, value: Timer) -> None: self.__publish_timer = value def publish(self): - """ - Publish :attr:`.orthoimage` (:attr:`.ground_track_elevation` and - :attr:`.terrain_geopoint_stamped` are also published but that - publish is triggered by callbacks since the messages are smaller and - can be published more often) - """ + """Publishes :attr:`.orthoimage`""" self.orthoimage def _try_wms_client_instantiation(self) -> None: @@ -400,33 +342,32 @@ def _bounding_box_with_padding_for_latlon( QoSPresetProfiles.SENSOR_DATA.value, ) def time_reference(self) -> Optional[TimeReference]: - """:term:`FCU` time reference via :term:`MAVROS`""" + """FCU time reference from FCU, or None if unknown""" @property - # @ROS.max_delay_ms(messaging.DELAY_DEFAULT_MS) @ROS.subscribe( f"/{ROS_NAMESPACE}" f'/{ROS_TOPIC_RELATIVE_FOV_BOUNDING_BOX.replace("~", BBOX_NODE_NAME)}', QoSPresetProfiles.SENSOR_DATA.value, ) def bounding_box(self) -> Optional[BoundingBox]: - """:term:`Bounding box` of approximate :term:`vehicle` :term:`camera` - :term:`FOV` location. + """Subscribed bounding box of the camera's ground-projected FOV, or None if + unknown """ @property - # @ROS.max_delay_ms(messaging.DELAY_DEFAULT_MS) - camera info has no header (?) @ROS.subscribe( ROS_TOPIC_CAMERA_INFO, QoSPresetProfiles.SENSOR_DATA.value, ) def camera_info(self) -> Optional[CameraInfo]: - """Camera info for determining appropriate :attr:`.orthoimage` resolution""" + """Subscribed camera info for determining appropriate :attr:`.orthoimage` + resolution, or None if unknown + """ @property def _orthoimage_size(self) -> Optional[Tuple[int, int]]: - """ - Padded map size tuple (height, width) or None if the information + """Padded map size tuple (height, width) or None if the information is not available. Because the deep learning models used for predicting matching keypoints @@ -463,8 +404,7 @@ def _request_orthoimage_for_bounding_box( styles: List[str], dem_styles: List[str], ) -> Optional[Tuple[np.ndarray, np.ndarray]]: - """ - Sends GetMap request to GIS WMS for image and DEM layers and returns + """Sends GetMap request to GIS WMS for image and DEM layers and returns :attr:`.orthoimage` attribute. Assumes zero raster as DEM if no DEM layer is available. @@ -561,11 +501,7 @@ def _orthoimage_overlap_is_too_low( ) @cache_if(_should_request_orthoimage) def orthoimage(self) -> Optional[OrthoImage]: - """Outgoing orthoimage and elevation raster :term:`stack` - - First channel is 8-bit grayscale orthoimage, 2nd and 3rd channels are - 16-bit elevation reference (:term:`DEM`) - """ + """Outgoing orthoimage and DEM raster""" bounding_box = deepcopy(self.bounding_box) map = self._request_orthoimage_for_bounding_box( bounding_box, @@ -619,7 +555,7 @@ def orthoimage(self) -> Optional[OrthoImage]: def _calculate_affine_transformation_matrix( self, height: int, width: int, bbox: BoundingBox ) -> Optional[np.ndarray]: - """Calculate the 3x3 affine transformation matrix that transforms orthoimage + """Calculates the 3x3 affine transformation matrix that transforms orthoimage frame pixel coordinates to WGS84 lon, lat coordinates. :param height: Height in pixels of the :term:`reference` image @@ -711,7 +647,7 @@ def _bounding_box_perimeter_meters(bounding_box: BoundingBox) -> float: def _get_map( self, layers, styles, srs, bbox, size, format_, transparency, grayscale=False ) -> Optional[np.ndarray]: - """Sends WMS :term:`GetMap` request and returns response :term:`raster`""" + """Sends WMS GetMap request and returns response raster""" if self._wms_client is None: self.get_logger().warning( "WMS client not instantiated. Skipping sending GetMap request." From b7c318aea9c22e49fd630c15972709e2c27832cb Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 06:46:21 +0100 Subject: [PATCH 12/30] Update transformations module name in Sphinx docs --- docs/sphinx/source/index.rst | 2 +- .../source/private/{messaging.rst => transformations.rst} | 2 +- gisnav/gisnav/_decorators.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) rename docs/sphinx/source/private/{messaging.rst => transformations.rst} (61%) diff --git a/docs/sphinx/source/index.rst b/docs/sphinx/source/index.rst index 8c9d5f49..f58b027c 100644 --- a/docs/sphinx/source/index.rst +++ b/docs/sphinx/source/index.rst @@ -40,4 +40,4 @@ Here you can find the automatically generated API reference for the GISNav ROS 2 :caption: Private API private/decorators - private/messaging + private/transformations diff --git a/docs/sphinx/source/private/messaging.rst b/docs/sphinx/source/private/transformations.rst similarity index 61% rename from docs/sphinx/source/private/messaging.rst rename to docs/sphinx/source/private/transformations.rst index f4acb66d..02372ad8 100644 --- a/docs/sphinx/source/private/messaging.rst +++ b/docs/sphinx/source/private/transformations.rst @@ -1,3 +1,3 @@ Messaging ____________________________________________________ -.. automodule:: gisnav._messaging +.. automodule:: gisnav._transformations diff --git a/gisnav/gisnav/_decorators.py b/gisnav/gisnav/_decorators.py index a5536424..c4bc91ad 100644 --- a/gisnav/gisnav/_decorators.py +++ b/gisnav/gisnav/_decorators.py @@ -71,6 +71,7 @@ def _is_generic_instance(value, origin_type, type_args): # TODO: make this work with typed dicts? +# TODO: consider using @typechecked from the typeguard library instead def narrow_types( arg: Union[Callable[..., T], Node] = None, return_value: Optional[Any] = None ): From 4847dac3223b2c52f336277ab84324d5be16066a Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 07:10:11 +0100 Subject: [PATCH 13/30] Update PoseNode docstrings --- gisnav/gisnav/core/gis_node.py | 2 +- gisnav/gisnav/core/pose_node.py | 91 +++++++++++++++------------------ 2 files changed, 41 insertions(+), 52 deletions(-) diff --git a/gisnav/gisnav/core/gis_node.py b/gisnav/gisnav/core/gis_node.py index e81ea8ef..d15f0994 100644 --- a/gisnav/gisnav/core/gis_node.py +++ b/gisnav/gisnav/core/gis_node.py @@ -342,7 +342,7 @@ def _bounding_box_with_padding_for_latlon( QoSPresetProfiles.SENSOR_DATA.value, ) def time_reference(self) -> Optional[TimeReference]: - """FCU time reference from FCU, or None if unknown""" + """Time reference from FCU, or None if unknown""" @property @ROS.subscribe( diff --git a/gisnav/gisnav/core/pose_node.py b/gisnav/gisnav/core/pose_node.py index 47b79225..ca3c0015 100644 --- a/gisnav/gisnav/core/pose_node.py +++ b/gisnav/gisnav/core/pose_node.py @@ -1,12 +1,18 @@ -"""This module contains :class:`.PoseNode`, a :term:`ROS` node for estimating the -:term:`camera` :term:`pose` in given frames of reference. +"""This module contains :class:`.PoseNode`, a ROS node that estimates camera +pose in global (REP 105 ``earth``) and local (REP 103 ``camera_optical``) frames +of reference. -The reference image can be either a orthoimagery :term:`raster` from the onboard GIS -server (deep map matching for noisy global position), or a previous image frame form -the camera (visual odometry for smooth relative position). +The reference image is either an orthoimage raster from the onboard GIS server, or a +previous image frame from the onboard camera. The former provides a global (absolute) +but noisy pose estimate that is drift-free, while the latter (visual odometry) provides +a smooth local (relative) pose estimate that drifts over the long-term. + +A deep learning model is used for the global matching problem ("deep" matching), while +traditional computer vision is used for the local matching problem ("shallow") +matching. The pose is estimated by finding matching keypoints between the query and -reference images and then solving the resulting :term:`PnP` problem. +reference images and then solving the resulting PnP problem. """ from typing import Final, Optional, Tuple, Union, cast @@ -60,26 +66,25 @@ class PoseNode(Node): - """Solves the keypoint matching and :term:`PnP` problems and publishes the - solution to ROS + """Estimates camera pose in global (REP 105 ``earth``) and local (REP 103 + ``camera_optical``) frames of reference by finding matching keypoints and + solving the PnP problem. """ CONFIDENCE_THRESHOLD_SHALLOW_MATCH = 0.9 """Confidence threshold for filtering out bad keypoint matches for shallow matching - .. note: - Stricter threshold for shallow matching because mistakes accumulate in VO + > [!NOTE] + > Stricter threshold for shallow matching because mistakes accumulate in VO """ CONFIDENCE_THRESHOLD_DEEP_MATCH = 0.8 - """Confidence threshold for filtering out bad keypoint matches for - deep matching - """ + """Confidence threshold for filtering out bad keypoint matches for deep matching""" MIN_MATCHES = 30 """Minimum number of keypoint matches before attempting pose estimation""" - class ScalingBuffer: + class _ScalingBuffer: """Maintains timestamped query frame to world frame scaling in a sliding windown buffer so that shallow matching (VO) pose can be scaled to meters using scaling information obtained from deep matching @@ -165,7 +170,7 @@ def __init__(self, *args, **kwargs): self._set_pose_request = SetPose.Request() self._pose_sent = False - self._scaling_buffer = self.ScalingBuffer() + self._scaling_buffer = self._ScalingBuffer() def _set_initial_pose(self, pose): if not self._pose_sent: @@ -179,16 +184,15 @@ def _set_initial_pose(self, pose): QoSPresetProfiles.SENSOR_DATA.value, ) def time_reference(self) -> Optional[TimeReference]: - """:term:`FCU` time reference via :term:`MAVROS`""" + """Time reference from FCU, or None if unknown""" @property - # @ROS.max_delay_ms(messaging.DELAY_SLOW_MS) - gst plugin does not enable timestamp? @ROS.subscribe( ROS_TOPIC_CAMERA_INFO, QoSPresetProfiles.SENSOR_DATA.value, ) def camera_info(self) -> Optional[CameraInfo]: - """Camera info message including the camera intrinsics matrix""" + """Camera info including the intrinsics matrix, or None if unknown""" def _pose_image_cb(self, msg: Image) -> None: """Callback for :attr:`.pose_image` message""" @@ -209,14 +213,13 @@ def _twist_image_cb(self, msg: Image) -> None: ) @ROS.transform(child_frame_id="camera_optical") def pose(self) -> Optional[PoseWithCovarianceStamped]: - """Camera pose in :term:`REP 105` ``map`` frame + """Camera pose in :term:`REP 105` ``earth`` frame - This represents the global 3D poseition and orienation of the ``camera_optical`` - frame in the earth-fixed ``map`` frame. This is obtained via :term:`deep - matching` and is a discontinuous estimate of pose. It is intended to be fused + This represents the global 3D position and orientation of the ``camera_optical`` + frame in the REP 105 ``earth`` (ECEF) frame. This is obtained via deep + matching and is a discontinuous estimate of pose. It is intended to be fused with and complement the continous or smooth twist estimate obtained via - :term:`shallow matching or visual odometry ` subsequent images from - the onboard camera. + shallow matching or visual odometry (VO). """ return self._get_pose(self.pose_image) @@ -321,12 +324,11 @@ def _get_pose( ROS_TOPIC_RELATIVE_QUERY_TWIST, QoSPresetProfiles.SENSOR_DATA.value, ) - # @ROS.transform("camera_optical") # TODO: enable after scaling to meters @narrow_types def camera_optical_twist_in_camera_optical_frame( self, msg: MonocularStereoImage ) -> Optional[TwistWithCovarianceStamped]: - """Camera pose in visual odometry world frame (i.e. previous query frame)""" + """REP 105 ``camera_optical`` frame twist in its intrisic frame""" scaling = self._scaling_buffer.interpolate( tf_.usec_from_header(msg.query.header) ) @@ -347,7 +349,6 @@ def camera_optical_twist_in_camera_optical_frame( return None @property - # @ROS.max_delay_ms(DELAY_DEFAULT_MS) @ROS.subscribe( f"/{ROS_NAMESPACE}" f'/{ROS_TOPIC_RELATIVE_POSE_IMAGE.replace("~", STEREO_NODE_NAME)}', @@ -355,18 +356,10 @@ def camera_optical_twist_in_camera_optical_frame( callback=_pose_image_cb, ) def pose_image(self) -> Optional[OrthoStereoImage]: - """:term:`Query `, :term:`reference`, and :term:`elevation` image - in a single 4-channel :term:`stack`. The query image is in the first - channel, the reference image is in the second, and the elevation reference - is in the last two (sum them together to get a 16-bit elevation reference). - - .. note:: - The existing :class:`sensor_msgs.msg.Image` message is repurposed - to represent a stereo couple with depth information (technically a - triplet) to avoid having to introduce custom messages that would - have to be distributed in a separate package. It will be easier to - package this later as a rosdebian if everything is already in the - rosdep index. + """Aligned and cropped query, reference, DEM rasters from + :class:`.StereoNode` + + This image couple is used for "deep" matching. """ @property @@ -378,23 +371,19 @@ def pose_image(self) -> Optional[OrthoStereoImage]: callback=_twist_image_cb, ) def twist_image(self) -> Optional[MonocularStereoImage]: - """:term:`Query `, :term:`reference image couple for :term:`VO`""" + """Aligned and cropped query and reference images from :class:`.StereoNode` + + This image couple is used for visual odometry or "shallow" matching. + """ @narrow_types def _preprocess( self, stereo_image: Union[OrthoStereoImage, MonocularStereoImage], ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: - """Converts sensor_msgs/Image message to numpy arrays - - :param stereo_image: A 4-channel image where the first channel is the - :term:`query`, the second channel is the 8-bit - :term:`elevation reference`, and the last two channels combined - represent the 16-bit :term:`elevation reference` (for deep matching/absolute - global position), or a 1-channel image where the left side is the query - image, and the right side is the reference image (for VO/shallow matching/ - relative position) + """Converts :class:`.Image` message to numpy arrays + :param stereo_image: A GISNav format stereo image message :return: A 3-tuple/triplet of query image, reference image, and DEM rasters """ # Convert the ROS Image message to an OpenCV image @@ -522,10 +511,10 @@ def _visualize_matches_and_pose( t: np.ndarray, label: str, ) -> None: - """Visualizes matches and projected :term:`FOV`""" + """Visualizes matches and projected FOV""" def _project_fov(img, h_matrix): - """Projects :term:`FOV` on :term:`reference` image""" + """Projects FOV on reference image""" height, width = img.shape[0:2] src_pts = np.float32( [[0, 0], [width - 1, 0], [width - 1, height - 1], [0, height - 1]] From a9b816890e25c512690f1610a6bc2886f719aa09 Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 07:41:37 +0100 Subject: [PATCH 14/30] Update StereoNode docstrings --- gisnav/gisnav/core/stereo_node.py | 42 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/gisnav/gisnav/core/stereo_node.py b/gisnav/gisnav/core/stereo_node.py index 9e1923cb..34a44d93 100644 --- a/gisnav/gisnav/core/stereo_node.py +++ b/gisnav/gisnav/core/stereo_node.py @@ -1,8 +1,14 @@ -"""This module contains :class:`.StereoNode`, a :term:`ROS` node generating the -:term:`query` and :term:`reference` stereo image pair by either rotating the reference -image based on :term:`vehicle` heading and then cropping it based on the -:term:`camera` information, or by coupling to successive image frames from the monocular -onboard camera. +"""This module contains :class:`.StereoNode`, a ROS node that generates and publishes a +synthetic query and reference stereo image couple. + +Synthetic refers to the fact that no stereo camera is actually assumed or required. +The reference can be an older image from the same monocular camera, or alternatively an +aligned orthoimage and DEM raster from the GIS server. + +Alignment and cropping to the same dimension as the query image is done to the +reference orthoimage by using information of the onboard camera resolution and the +vehicle's heading. Alignment is required since the deep learning network that is used +for matching keypoints is not assumed to be rotation agnostic. """ from typing import Final, Optional, Tuple @@ -39,11 +45,7 @@ class StereoNode(Node): - """Generates and publishes a synthetic :term:`query` and :term:`reference` stereo - image couple. Synthetic refers to the fact that no stereo camera is actually assumed - or required. The reference can be an older image from the same monocular camera, or - alternatively an aligned map raster from the GIS server. - """ + """Generates and publishes a synthetic query and reference stereo image couple.""" _ROS_PARAM_DESCRIPTOR_READ_ONLY: Final = ParameterDescriptor(read_only=True) """A read only ROS parameter descriptor""" @@ -84,7 +86,7 @@ def __init__(self, *args, **kwargs) -> None: QoSPresetProfiles.SENSOR_DATA.value, ) def orthoimage(self) -> Optional[OrthoImage]: - """Subscribed :term:`orthoimage` for :term:`pose` estimation""" + """Subscribed orthoimage, or None if unknown""" @property # @ROS.max_delay_ms(messaging.DELAY_SLOW_MS) - gst plugin does not enable timestamp? @@ -93,7 +95,8 @@ def orthoimage(self) -> Optional[OrthoImage]: QoSPresetProfiles.SENSOR_DATA.value, ) def camera_info(self) -> Optional[CameraInfo]: - """Camera info for determining appropriate :attr:`.orthoimage` resolution""" + """Subscribed camera info for determining appropriate :attr:`.orthoimage` crop + resolution, or None if unknown""" def _image_cb(self, msg: Image) -> None: """Callback for :attr:`.image` message""" @@ -115,7 +118,7 @@ def _image_cb(self, msg: Image) -> None: callback=_image_cb, ) def image(self) -> Optional[Image]: - """Raw image data from vehicle camera for pose estimation""" + """Subscribed raw image from vehicle camera, or None if unknown""" def _world_to_reference_proj_str( self, @@ -158,14 +161,8 @@ def _transform( QoSPresetProfiles.SENSOR_DATA.value, ) def pose_image(self) -> Optional[OrthoStereoImage]: - """Published :term:`stacked ` image consisting of query image, - reference image, and optional reference elevation raster (:term:`DEM`). - - .. note:: - Semantically not a single image, but a stack of two 8-bit grayscale - images and one 16-bit "image-like" elevation reference, stored in a - compact way in an existing message type so to avoid having to also - publish custom :term:`ROS` message definitions. + """Published aligned and cropped orthoimage consisting of query image, + reference image, and optional reference elevation raster (DEM). """ @narrow_types(self) @@ -259,7 +256,8 @@ def _pnp_image( ) def twist_image(self) -> Optional[MonocularStereoImage]: """Published stereo couple image consisting of query image and reference image - for :term:`VO` use. + + Used for visual odometry and specifically velocity estimation """ @narrow_types(self) From 1a9d63c07d3bf753b6a3e4eb7377be2e96ce32b6 Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 07:48:44 +0100 Subject: [PATCH 15/30] Add GISNav version information to API docs [skip ci] --- docs/sphinx/source/conf.py | 14 ++++++++++++++ docs/sphinx/source/private/transformations.rst | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/source/conf.py b/docs/sphinx/source/conf.py index fc076358..e5e86d2a 100644 --- a/docs/sphinx/source/conf.py +++ b/docs/sphinx/source/conf.py @@ -1,4 +1,5 @@ import os +import subprocess import sys # Configuration file for the Sphinx documentation builder. @@ -48,3 +49,16 @@ html_theme = "alabaster" html_static_path = ["_static"] + + +# -- Footer ------------------------------------------------- +try: + # Try to add git version and commit information to footer + release = ( + subprocess.check_output(["git", "describe", "--tags"]).strip().decode("utf-8") + ) + rst_epilog = """ +GISNav |release| +""" +except subprocess.CalledProcessError: + raise diff --git a/docs/sphinx/source/private/transformations.rst b/docs/sphinx/source/private/transformations.rst index 02372ad8..e09c1c87 100644 --- a/docs/sphinx/source/private/transformations.rst +++ b/docs/sphinx/source/private/transformations.rst @@ -1,3 +1,3 @@ -Messaging +Transformations ____________________________________________________ .. automodule:: gisnav._transformations From 64ae3304f2e1ee6fb2f408b1137c5709c8c8fe28 Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 08:08:32 +0100 Subject: [PATCH 16/30] Update NMEANode docstrings [skip ci] --- gisnav/gisnav/constants.py | 3 ++ gisnav/gisnav/extensions/nmea_node.py | 66 ++++++++++++++------------- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/gisnav/gisnav/constants.py b/gisnav/gisnav/constants.py index 9f090750..a6a17268 100644 --- a/gisnav/gisnav/constants.py +++ b/gisnav/gisnav/constants.py @@ -93,6 +93,9 @@ (MAVLink Gimbal protocl v2) """ +ROS_TOPIC_ROBOT_LOCALIZATION_ODOMETRY = "/robot_localization/odometry/filtered" +"""Topic for filtered odometry from the ``robot_localization`` package EKF node""" + DELAY_DEFAULT_MS: Final = 2000 """Max acceptable delay for things like global position""" diff --git a/gisnav/gisnav/extensions/nmea_node.py b/gisnav/gisnav/extensions/nmea_node.py index ac250f7a..6ee3a971 100644 --- a/gisnav/gisnav/extensions/nmea_node.py +++ b/gisnav/gisnav/extensions/nmea_node.py @@ -1,6 +1,5 @@ -"""This module contains the :class:`.NMEANode` :term:`extension` :term:`node` -that publishes mock GPS (GNSS) messages to autopilot middleware over -:term:`NMEA` +"""This module contains :class:`.NMEANode`, an extension ROS node that publishes +mock GPS (GNSS) messages to FCU over the NMEA protocol via a serial port """ from datetime import datetime from typing import Final, List, Optional, Tuple @@ -21,22 +20,23 @@ from .. import _transformations as tf_ from .._decorators import ROS, narrow_types +from ..constants import ROS_TOPIC_ROBOT_LOCALIZATION_ODOMETRY _ROS_PARAM_DESCRIPTOR_READ_ONLY: Final = ParameterDescriptor(read_only=True) """A read only ROS parameter descriptor""" class NMEANode(Node): - """A node that publishes a mock GPS message to autopilot middleware""" + """Publishes mock GPS messages to FCU over NMEA protocol via a serial port""" ROS_D_DEM_VERTICAL_DATUM = 5703 - """Default :term:`DEM` vertical datum""" + """Default for :attr:`.dem_vertical_datum`""" ROS_D_PORT = "/dev/ttyS1" - """Default serial port for outgoing :term:`NMEA` messages""" + """Default for :attr:`.port`""" ROS_D_BAUDRATE = 9600 - """Default baudrate for outgoing :term:`NMEA` messages""" + """Default for :attr:`.badurate`""" # EPSG code for WGS 84 and a common mean sea level datum (e.g., EGM96) _EPSG_WGS84 = 4326 @@ -69,33 +69,35 @@ def __init__(self, *args, **kwargs): @property @ROS.parameter(ROS_D_DEM_VERTICAL_DATUM, descriptor=_ROS_PARAM_DESCRIPTOR_READ_ONLY) def dem_vertical_datum(self) -> Optional[int]: - """:term:`DEM` vertical datum (must match DEM that is published in - :attr:`.GISNode.orthoimage`) + """DEM vertical datum + + > [!IMPORTANT] + > Must match DEM that is published in :attr:`.GISNode.orthoimage` """ @property @ROS.parameter(ROS_D_PORT, descriptor=_ROS_PARAM_DESCRIPTOR_READ_ONLY) def port(self) -> Optional[str]: - """Serial port for outgoing :term:`NMEA` messages""" + """Serial port for outgoing NMEA messages""" @property @ROS.parameter(ROS_D_BAUDRATE, descriptor=_ROS_PARAM_DESCRIPTOR_READ_ONLY) def baudrate(self) -> Optional[int]: - """Baudrate for outgoing :term:`NMEA` messages""" + """Baudrate for outgoing NMEA messages""" def _odometry_cb(self, msg: Odometry) -> None: """Callback for :attr:`.odometry`""" self._publish(msg) @property - # @ROS.max_delay_ms(messaging.DELAY_SLOW_MS) - gst plugin does not enable timestamp? @ROS.subscribe( - "/robot_localization/odometry/filtered", + ROS_TOPIC_ROBOT_LOCALIZATION_ODOMETRY, QoSPresetProfiles.SENSOR_DATA.value, callback=_odometry_cb, ) def odometry(self) -> Optional[Odometry]: - """Camera info message including the camera intrinsics matrix""" + """Subscribed filtered odometry from ``robot_localization`` package EKF node, + or None if unknown""" def _publish(self, odometry: Odometry) -> None: @narrow_types(self) @@ -234,8 +236,7 @@ def _calculate_cog_variance( def _calculate_course_over_ground( east_velocity: float, north_velocity: float ) -> float: - """ - Calculate the course over ground from east and north velocities. + """Calculates course over ground from east and north velocities. :param east_velocity: The velocity towards the east in meters per second. @@ -311,7 +312,7 @@ def _calculate_course_over_ground( def compute_rmc_parameters( self, odometry: Odometry ) -> Tuple[str, str, str, str, str, str, float, float, str]: - """Calculate the RMC parameters based on odometry data. + """Calculates RMC parameters based on odometry data. :param odometry: The odometry data from which to extract GPS details. :returns: A tuple with formatted time, status, latitude, latitude direction, @@ -380,7 +381,13 @@ def nmea_sentences( ) -> Optional[List[str]]: """Outgoing NMEA mock GPS sentences - Constructs GPGGA, GPVTG, and GPGSA NMEA sentences based on provided GPS data. + Published sentences: + - GGA (position fix) + - GSA (position fix) + - HDT (heading) + - GST (standard deviations) + - VTG (velocities, disabled) + - RMC (velocities, disabled) """ # Convert timestamp to hhmmss format time_str = self.format_time_from_timestamp(timestamp) @@ -424,7 +431,7 @@ def GGA( altitude_amsl: float, hdop: float, ) -> str: - """Returns an :term:`NMEA` GGA sentence + """Returns an NMEA GPGGA sentence :param time_str: UTC time in HHMMSS format. :param lat_nmea: Latitude in decimal degrees. @@ -460,7 +467,7 @@ def GGA( @staticmethod def VTG(cog_degrees: float, ground_speed_knots: float) -> str: - """Returns an :term:`NMEA` VTG sentence + """Returns an NMEA GPVTG sentence :param cog_degrees: Course over ground in degrees. :param ground_speed_knots: Speed over ground in knots. @@ -485,7 +492,7 @@ def VTG(cog_degrees: float, ground_speed_knots: float) -> str: @staticmethod def GSA(pdop: float, hdop: float, vdop: float) -> str: - """Returns an :term:`NMEA` GSA sentence + """Returns an NMEA GPGSA sentence :param pdop: Positional dilution of precision. :param hdop: Horizontal dilution of precision. @@ -509,7 +516,7 @@ def GSA(pdop: float, hdop: float, vdop: float) -> str: @staticmethod def HDT(yaw_deg: float): - """Returns an :term:`NMEA` HDT sentence + """Returns an NMEA GPHDT sentence :param yaw_deg: Vehicle heading in degrees. Heading increases "clockwise" so that north is 0 degrees and east is 90 degrees. @@ -528,7 +535,7 @@ def GST( std_dev_longitude: float, std_dev_altitude: float, ) -> str: - """Returns an NMEA GST sentence. + """Returns an NMEA GPGST sentence. :param time_str: UTC time in hhmmss format. :param rms_deviation: RMS deviation of the pseudorange. @@ -571,7 +578,7 @@ def RMC( magnetic_variation: float = 0, var_dir: str = "E", ) -> str: - """Returns an NMEA RMC sentence. + """Returns an NMEA GPRMC sentence. :param time_str: UTC time in hhmmss format. :param status: Status, 'A' for active or 'V' for void. @@ -628,15 +635,12 @@ def format_date_from_timestamp(self, timestamp: int) -> str: def _convert_to_wgs84( self, lat: float, lon: float, elevation: float ) -> Optional[Tuple[float, float]]: - """ - Convert :term:`elevation` or :term:`altitude` from a specified vertical - datum to :term:`WGS 84`. + """Converts elevation or altitude from :attr:`.dem_vertical_datum` to WGS 84. :param lat: Latitude in decimal degrees. :param lon: Longitude in decimal degrees. :param elevation: Elevation in the specified datum. - :return: A tuple containing :term:`elevation` above :term:`WGS 84` and - :term:`AMSL`. + :return: A tuple containing elevation above WGS 84 ellipsoid and AMSL. """ _, _, wgs84_elevation = self._transformer_to_wgs84.transform( lon, lat, elevation @@ -646,7 +650,7 @@ def _convert_to_wgs84( return wgs84_elevation, msl_elevation def _write_nmea_to_serial(self, nmea_sentences: List[str]) -> None: - """Writes a collection of NMEA sentences to the specified serial port. + """Writes a collection of NMEA sentences to :attr:`.port`. :param nmea_sentences: A list of NMEA sentences to be written to the serial port """ @@ -687,7 +691,7 @@ def ZDA( @property def GSV(self) -> str: - """Returns NMEA GSV sentences for 12 satellites statically defined. + """Returns NMEA GPGSV sentences for 12 statically defined dummy satellites. :returns: A formatted NMEA GSV sentences as a string. """ From 12936998371a88b3dfdb668c469bf973dc0f3e6c Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 08:17:18 +0100 Subject: [PATCH 17/30] Update UORBNode docstrings [skip ci] --- gisnav/gisnav/extensions/nmea_node.py | 2 +- gisnav/gisnav/extensions/uorb_node.py | 38 +++++++++++++-------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/gisnav/gisnav/extensions/nmea_node.py b/gisnav/gisnav/extensions/nmea_node.py index 6ee3a971..96db8094 100644 --- a/gisnav/gisnav/extensions/nmea_node.py +++ b/gisnav/gisnav/extensions/nmea_node.py @@ -36,7 +36,7 @@ class NMEANode(Node): """Default for :attr:`.port`""" ROS_D_BAUDRATE = 9600 - """Default for :attr:`.badurate`""" + """Default for :attr:`.baudrate`""" # EPSG code for WGS 84 and a common mean sea level datum (e.g., EGM96) _EPSG_WGS84 = 4326 diff --git a/gisnav/gisnav/extensions/uorb_node.py b/gisnav/gisnav/extensions/uorb_node.py index a23a08ef..739a6a02 100644 --- a/gisnav/gisnav/extensions/uorb_node.py +++ b/gisnav/gisnav/extensions/uorb_node.py @@ -1,5 +1,5 @@ -"""This module contains the :class:`.UORBNode` :term:`extension` :term:`node` -that publishes PC4 uORB SensorGps (GNSS) messages to the micro-ros agent middleware +"""This module contains :class:`.UORBNode`, an extension ROS node that publishes PX4 +uORB :class:`.SensorGps` (GNSS) messages to the uXRCE-DDS middleware """ from typing import Final, Optional, Tuple @@ -18,17 +18,18 @@ from .. import _transformations as tf_ from .._decorators import ROS, narrow_types -from ..constants import ROS_TOPIC_SENSOR_GPS +from ..constants import ROS_TOPIC_ROBOT_LOCALIZATION_ODOMETRY, ROS_TOPIC_SENSOR_GPS _ROS_PARAM_DESCRIPTOR_READ_ONLY: Final = ParameterDescriptor(read_only=True) """A read only ROS parameter descriptor""" class UORBNode(Node): - """A node that publishes a mock GPS message to autopilot middleware""" + """A node that publishes PX4 uORB :class:`.SensorGps` (GNSS) messages to the + uXRCE-DDS middleware""" ROS_D_DEM_VERTICAL_DATUM = 5703 - """Default :term:`DEM` vertical datum""" + """Default for :attr:`.dem_vertical_datum`""" # EPSG code for WGS 84 and a common mean sea level datum (e.g., EGM96) _EPSG_WGS84 = 4326 @@ -67,8 +68,10 @@ def __init__(self, *args, **kwargs): @property @ROS.parameter(ROS_D_DEM_VERTICAL_DATUM, descriptor=_ROS_PARAM_DESCRIPTOR_READ_ONLY) def dem_vertical_datum(self) -> Optional[int]: - """:term:`DEM` vertical datum (must match DEM that is published in - :attr:`.GISNode.orthoimage`) + """DEM vertical datum + + > [!IMPORTANT] + > Must match DEM that is published in :attr:`.GISNode.orthoimage` """ def _odometry_cb(self, msg: Odometry) -> None: @@ -76,14 +79,14 @@ def _odometry_cb(self, msg: Odometry) -> None: self._publish(msg) @property - # @ROS.max_delay_ms(messaging.DELAY_SLOW_MS) - gst plugin does not enable timestamp? @ROS.subscribe( - "/robot_localization/odometry/filtered", + ROS_TOPIC_ROBOT_LOCALIZATION_ODOMETRY, QoSPresetProfiles.SENSOR_DATA.value, callback=_odometry_cb, ) def odometry(self) -> Optional[Odometry]: - """Camera info message including the camera intrinsics matrix""" + """Subscribed filtered odometry from ``robot_localization`` package EKF node, + or None if unknown""" def _publish(self, odometry: Odometry) -> None: @narrow_types(self) @@ -221,7 +224,7 @@ def _calculate_course_over_ground( east_velocity: float, north_velocity: float ) -> float: """ - Calculate the course over ground from east and north velocities. + Calculates course over ground from east and north velocities. :param east_velocity: The velocity towards the east in meters per second. @@ -311,10 +314,10 @@ def sensor_gps( epv: float, satellites_visible: int, ) -> Optional[SensorGps]: - """Outgoing mock :term:`GNSS` :term:`message` when :attr:`use_sensor_gps` - is ``True`` + """Outgoing mock GPS message, or None if cannot be computed - Uses the release/1.14 tag version of :class:`px4_msgs.msg.SensorGps` + > [!IMPORTANT] px4_msgs release/1.14 + > Uses the release/1.14 tag version of :class:`px4_msgs.msg.SensorGps` """ yaw_rad = np.radians(yaw_degrees) @@ -370,15 +373,12 @@ def _device_id(self) -> int: def _convert_to_wgs84( self, lat: float, lon: float, elevation: float ) -> Optional[Tuple[float, float]]: - """ - Convert :term:`elevation` or :term:`altitude` from a specified vertical - datum to :term:`WGS 84`. + """Converts elevation or altitude from :attr:`.dem_vertical_datum` to WGS 84. :param lat: Latitude in decimal degrees. :param lon: Longitude in decimal degrees. :param elevation: Elevation in the specified datum. - :return: A tuple containing :term:`elevation` above :term:`WGS 84` and - :term:`AMSL`. + :return: A tuple containing elevation above WGS 84 ellipsoid and AMSL. """ _, _, wgs84_elevation = self._transformer_to_wgs84.transform( lon, lat, elevation From 7e806bb03013c3fcf820ea6ef32de47bad642115 Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 08:18:31 +0100 Subject: [PATCH 18/30] Hide GISNode publish method --- gisnav/gisnav/core/gis_node.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gisnav/gisnav/core/gis_node.py b/gisnav/gisnav/core/gis_node.py index d15f0994..2c29dad4 100644 --- a/gisnav/gisnav/core/gis_node.py +++ b/gisnav/gisnav/core/gis_node.py @@ -242,7 +242,7 @@ def _create_publish_timer(self, publish_rate: float) -> Timer: ) self.get_logger().error(error_msg) raise ValueError(error_msg) - timer = self.create_timer(1 / publish_rate, self.publish) + timer = self.create_timer(1 / publish_rate, self._publish) return timer @narrow_types @@ -271,7 +271,7 @@ def _publish_timer(self) -> Timer: def _publish_timer(self, value: Timer) -> None: self.__publish_timer = value - def publish(self): + def _publish(self): """Publishes :attr:`.orthoimage`""" self.orthoimage From ec7bbeb9d45c2341b1e32bc556fffeec91db0b8c Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 08:55:39 +0100 Subject: [PATCH 19/30] Update QGISNode docstrings [skip ci] --- gisnav/gisnav/extensions/qgis_node.py | 108 ++++---------------------- 1 file changed, 16 insertions(+), 92 deletions(-) diff --git a/gisnav/gisnav/extensions/qgis_node.py b/gisnav/gisnav/extensions/qgis_node.py index 01e0ea7d..a99ca3b4 100644 --- a/gisnav/gisnav/extensions/qgis_node.py +++ b/gisnav/gisnav/extensions/qgis_node.py @@ -1,15 +1,12 @@ -"""This module contains a :term:`ROS 2` node for interacting with a PostgreSQL -database. The node enables real-time data visualization in QGIS by subscribing -to specific GISNav :term:`core` output messages and updating the database -accordingly. +"""This module contains :class:`.QGISNode`, an extension ROS node that +subscribes to and stores :attr:`.UORBNode.sensor_gps` messages in a PostGIS +database. -This node enables real-time visualization of data in QGIS, aiding in -development and debugging. +The node enables real-time data visualization in QGIS by via the PostGIS database. """ -from typing import Final, Optional, Union +from typing import Final, Optional import psycopg2 -from geographic_msgs.msg import BoundingBox from px4_msgs.msg import SensorGps from rcl_interfaces.msg import ParameterDescriptor from rclpy.node import Node @@ -17,23 +14,16 @@ from rclpy.timer import Timer from .._decorators import ROS, narrow_types -from ..constants import ( - BBOX_NODE_NAME, - DELAY_DEFAULT_MS, - ROS_NAMESPACE, - ROS_TOPIC_RELATIVE_FOV_BOUNDING_BOX, - ROS_TOPIC_SENSOR_GPS, -) +from ..constants import ROS_TOPIC_SENSOR_GPS class QGISNode(Node): - """:term:`ROS 2` node that subscribes to GISNav :term:`core` output - messages and updates an in-memory PostgreSQL database for visualization in - QGIS. + """:term:`ROS 2` node that that subscribes to and stores + :attr:`.UORBNode.sensor_gps` messages in a PostGIS database. """ ROS_D_SQL_POLL_RATE = 0.1 - """Default :term:`SQL` client connection attempt poll rate in Hz""" + """Default for :attr:`.sql_poll_rate`""" _ROS_PARAM_DESCRIPTOR_READ_ONLY: Final = ParameterDescriptor(read_only=True) """A read only ROS parameter descriptor""" @@ -50,9 +40,6 @@ class QGISNode(Node): DEBUG_GPS_TABLE: Final = "gps_table" """Table name for mock GPS location""" - DEBUG_BBOX_TABLE: Final = "bbox_table" - """Table name for :term:`FOV` :term:`bounding box`""" - def __init__(self, *args, **kwargs): """Class initializer @@ -62,7 +49,6 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Initialize ROS subscriptions by calling the decorated properties once - # self.bounding_box # todo: this callback writes to disk very often self.sensor_gps sql_poll_rate = self.sql_poll_rate @@ -131,11 +117,11 @@ def __del__(self): @property @ROS.parameter(ROS_D_SQL_POLL_RATE, descriptor=_ROS_PARAM_DESCRIPTOR_READ_ONLY) def sql_poll_rate(self) -> Optional[float]: - """:term:`SQL` connection attempt poll rate in Hz""" + """SQL connection attempt poll rate in Hz""" def _create_tables(self): - """Create (and recreate if exist) temporary tables for storing SensorGps - and BoundingBox data as PostGIS geometries. + """Create (and recreate if exist) temporary tables for storing + :attr:`UORBNode.SensorGps` data as PostGIS geometries. """ drop_gps_table_query = f""" DROP TABLE IF EXISTS {self.DEBUG_GPS_TABLE}; @@ -147,34 +133,20 @@ def _create_tables(self): altitude DOUBLE PRECISION ); """ - drop_bbox_table_query = f""" - DROP TABLE IF EXISTS {self.DEBUG_BBOX_TABLE}; - """ - create_bbox_table_query = f""" - CREATE UNLOGGED TABLE {self.DEBUG_BBOX_TABLE} ( - id SERIAL PRIMARY KEY, - geom GEOMETRY(Polygon, 4326) -- SRID 4326 for GPS coordinates - ); - """ with self._db_connection.cursor() as cursor: cursor.execute("CREATE EXTENSION IF NOT EXISTS postgis;") - # Drop the tables if they exist - we do not want to persist old # debugging data cursor.execute(drop_gps_table_query) - cursor.execute(drop_bbox_table_query) - cursor.execute(create_gps_table_query) - cursor.execute(create_bbox_table_query) self._db_connection.commit() - def _update_database(self, msg: Union[SensorGps, BoundingBox]) -> None: + def _update_database(self, msg: SensorGps) -> None: """Updates the PostgreSQL database with the received ROS 2 message data - :param msg: :class:`px4_msgs.msg.SensorGps` or - :class:`geographic_msgs.msg.BoundingBox` message containing - data to insert into the database + :param msg: :class:`.SensorGps` message containing data to insert into the + database """ if self._db_connection is None: self.get_logger().error( @@ -198,61 +170,13 @@ def _update_database(self, msg: Union[SensorGps, BoundingBox]) -> None: f"Cannot insert SensorGps message." ) - elif isinstance(msg, BoundingBox): - query = f""" - INSERT INTO {self.DEBUG_BBOX_TABLE} (geom) - VALUES (ST_SetSRID(ST_MakePolygon(ST_GeomFromText('LINESTRING(' - || %s || ' ' || %s || ', ' - || %s || ' ' || %s || ', ' - || %s || ' ' || %s || ', ' - || %s || ' ' || %s || ', ' - || %s || ' ' || %s || ')')), 4326)); - """ - try: - cursor.execute( - query, - ( - msg.min_pt.longitude, - msg.min_pt.latitude, - msg.min_pt.longitude, - msg.max_pt.latitude, - msg.max_pt.longitude, - msg.max_pt.latitude, - msg.max_pt.longitude, - msg.min_pt.latitude, - msg.min_pt.longitude, - msg.min_pt.latitude, - ), - ) - except psycopg2.errors.UndefinedTable: - self.get_logger().error( - f"Table f{self.DEBUG_BBOX_TABLE} does not exist. " - f"Cannot insert BoundingBox message." - ) - self._db_connection.commit() @property - # @ROS.max_delay_ms(messaging.DELAY_DEFAULT_MS) - @ROS.subscribe( - f"/{ROS_NAMESPACE}" - f'/{ROS_TOPIC_RELATIVE_FOV_BOUNDING_BOX.replace("~", BBOX_NODE_NAME)}', - QoSPresetProfiles.SENSOR_DATA.value, - callback=_update_database, - ) - def bounding_box(self) -> Optional[BoundingBox]: - """:term:`Bounding box` of approximate :term:`vehicle` :term:`camera` - :term:`FOV` location published via :attr:`.GisNode.bounding_box` - """ - - @property - @ROS.max_delay_ms(DELAY_DEFAULT_MS) @ROS.subscribe( ROS_TOPIC_SENSOR_GPS, QoSPresetProfiles.SENSOR_DATA.value, callback=_update_database, ) def sensor_gps(self) -> Optional[SensorGps]: - """Subscribed mock :term:`GNSS` :term:`message` published by - :class:`.MockGPSNode` - """ + """Subscribed :attr:`.UORBNode.sensor_gps` mock GPS message""" From acfa9b503ed9900e915a2abe32260cc320e8f98c Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 09:01:55 +0100 Subject: [PATCH 20/30] Update test subpackage docstrings [skip ci] --- gisnav/test/launch/__init__.py | 8 +------- gisnav/test/launch/test_default_launch.py | 2 +- gisnav/test/launch/testcases.py | 12 +++++------- gisnav/test/unit/__init__.py | 8 +++----- 4 files changed, 10 insertions(+), 20 deletions(-) diff --git a/gisnav/test/launch/__init__.py b/gisnav/test/launch/__init__.py index 2907d48c..cf6a90f4 100644 --- a/gisnav/test/launch/__init__.py +++ b/gisnav/test/launch/__init__.py @@ -1,7 +1 @@ -"""Launch tests - -.. note:: - - This subpackage is still very much Work In Progress - do not expect the tests - here to work. -""" +"""This sub-package contains launch tests""" diff --git a/gisnav/test/launch/test_default_launch.py b/gisnav/test/launch/test_default_launch.py index 3d9bd28e..33041cad 100644 --- a/gisnav/test/launch/test_default_launch.py +++ b/gisnav/test/launch/test_default_launch.py @@ -1,4 +1,4 @@ -"""Tests :term:`ROS` launch file for :term:`PX4` configuration""" +"""Tests the ``default.launch.py`` ROS launch file""" import os import pytest diff --git a/gisnav/test/launch/testcases.py b/gisnav/test/launch/testcases.py index a3a9a4b1..d7561741 100644 --- a/gisnav/test/launch/testcases.py +++ b/gisnav/test/launch/testcases.py @@ -1,4 +1,4 @@ -"""Tests case for topic subscriptions""" +"""This module contains launch test case implementations""" import time import unittest from typing import List, Tuple @@ -8,9 +8,7 @@ class TestTopographyCase(unittest.TestCase): - """Tests that all nodes initialize with the correct :term:`ROS` topic - subscriptions - """ + """Tests that all nodes initialize with the correct ROS topic subscriptions""" EXPECTED_NODES = ( "gis_node", @@ -22,7 +20,7 @@ class TestTopographyCase(unittest.TestCase): ) """Names of nodes that should be found when running the launch configuration - `test_node` is created by the test case. + ``test_node`` is created by the test case. """ EXPECTED_NAMESPACES = ("/gisnav", "/gisnav", "/gisnav", "/gisnav", "/gisnav", "/") @@ -81,7 +79,7 @@ def _get_names_and_namespaces_within_timeout( :param timeout: Timeout in seconds :return: List of tuples containing node name and type :raise: :class:`.AssertionError` if names and namespaces are not - returned within :param timeout_sec: + returned within timeout """ names_and_namespaces = None end_time = time.time() + timeout @@ -102,7 +100,7 @@ def _get_topic_names_and_types_within_timeout( :param timeout: Timeout in seconds :return: List of tuples containing topic name and type :raise: :class:`.AssertionError` if names and types are not returned - within :param timeout_sec: + within timeout """ names_and_types = None end_time = time.time() + timeout diff --git a/gisnav/test/unit/__init__.py b/gisnav/test/unit/__init__.py index 549ca300..0245470a 100644 --- a/gisnav/test/unit/__init__.py +++ b/gisnav/test/unit/__init__.py @@ -1,7 +1,5 @@ -"""Unit tests +"""This sub-package contains unit tests -.. note:: - - This subpackage is still very much Work In Progress - do not expect the tests - here to work. +> [!NOTE] Not implemented +> No unit test suites implemented yet. """ From 8ef8d71e69c17462d2176cfc5e39ac67092a5bba Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 09:05:31 +0100 Subject: [PATCH 21/30] Update constants module docstrings [skip ci] --- gisnav/gisnav/constants.py | 43 +++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/gisnav/gisnav/constants.py b/gisnav/gisnav/constants.py index a6a17268..1bd07e6f 100644 --- a/gisnav/gisnav/constants.py +++ b/gisnav/gisnav/constants.py @@ -1,17 +1,16 @@ -"""This module contains the static configuration of the :term:`ROS` namespace -and node and topic names +"""This module contains the static configuration of the ROS namespace and node and +topic names -Using this module nodes that talk to each other can refer to a single source -of truth +Using this module, nodes that talk to each other can refer to a single source of truth. -.. warning:: - This module should not import anything from the gisnav package namespace - to prevent circular imports. +> [!WARNING] Circular imports +> This module should not import anything from the gisnav package namespace to prevent +> circular imports. """ from typing import Final, Literal ROS_NAMESPACE: Final = "gisnav" -""":term:`ROS` node namespace""" +"""Namespace for all GISNav ROS nodes""" GIS_NODE_NAME: Final = "gis_node" """Name of :class:`.GISNode` spun up by :func:`.run_gis_node`""" @@ -35,43 +34,39 @@ """Name of :class:`.QGISNode` spun up by :func:`.run_qgis_node`""" ROS_TOPIC_RELATIVE_ORTHOIMAGE: Final = "~/orthoimage" -"""Relative :term:`topic` into which :class:`.GISNode` publishes -:attr:`.GISNode.orthoimage`. -""" +"""Relative topic into which :class:`.GISNode` publishes :attr:`.GISNode.orthoimage`.""" ROS_TOPIC_SENSOR_GPS: Final = "/fmu/in/sensor_gps" -""":term:`Topic` into which :class:`.UORBNode` publishes -:attr:`.GISNode.sensor_gps`. -""" +"""Topic into which :class:`.UORBNode` publishes :attr:`.GISNode.sensor_gps`.""" ROS_TOPIC_RELATIVE_FOV_BOUNDING_BOX: Final = "~/fov/bounding_box" -"""Relative :term:`topic` into which :class:`.BBoxNode` publishes +"""Relative topic into which :class:`.BBoxNode` publishes :attr:`.BBoxNode.fov_bounding_box`. """ ROS_TOPIC_RELATIVE_POSE_IMAGE: Final = "~/pose_image" -"""Relative :term:`topic` into which :class:`.StereoNode` publishes +"""Relative topic into which :class:`.StereoNode` publishes :attr:`.StereoNode.pose_image`. """ ROS_TOPIC_RELATIVE_TWIST_IMAGE: Final = "~/twist_image" -"""Relative :term:`topic` into which :class:`.StereoNode` publishes +"""Relative topic into which :class:`.StereoNode` publishes :attr:`.StereoNode.twist_image`. """ ROS_TOPIC_RELATIVE_POSE: Final = "~/pose" -"""Relative :term:`topic` into which :class:`.StereoNode` publishes +"""Relative topic into which :class:`.StereoNode` publishes :attr:`.StereoNode.pose`. """ ROS_TOPIC_RELATIVE_QUERY_TWIST: Final = "~/vo/twist" -"""Relative :term:`topic` into which :class:`.StereoNode` publishes +"""Relative topic into which :class:`.StereoNode` publishes :attr:`.StereoNode.camera_optical_twist_in_query_frame`. """ MAVROS_TOPIC_TIME_REFERENCE: Final = "/mavros/time_reference" -"""The :term:`MAVROS` time reference topic that has the difference between -the local system time and the foreign :term:`FCU` time +"""The MAVROS time reference topic that has the difference between +the local system time and the foreign FCU time """ ROS_TOPIC_CAMERA_INFO: Final = "/camera/camera_info" @@ -106,7 +101,7 @@ "map", "earth", ] -"""Allowed ROS message header ``frame_id``s as specified in :term:`REP 103` and -:term:`REP 105`. The ``odom`` frame is not used by GISNav but may be published e.g. by -:term:`MAVROS`. +"""Allowed ROS message header ``frame_id``s as specified in REP 103 and +REP 105. The ``odom`` frame is not used by GISNav but may be published e.g. by +MAVROS. """ From 8ec0c34a97ebb521289cb87409ac41eeae14e717 Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 09:15:02 +0100 Subject: [PATCH 22/30] Update package init module docstrings [skip ci] --- gisnav/gisnav/__init__.py | 52 ++++++++++++++++++++-------- gisnav/gisnav/core/__init__.py | 2 +- gisnav/gisnav/extensions/__init__.py | 4 +-- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/gisnav/gisnav/__init__.py b/gisnav/gisnav/__init__.py index 52e5b3ec..4f9e8263 100644 --- a/gisnav/gisnav/__init__.py +++ b/gisnav/gisnav/__init__.py @@ -1,17 +1,11 @@ -"""ROS 2 package for estimating airborne drone global position by matching -video to map retrieved from onboard GIS server +"""This is a ROS 2 package that determines UAV global position by aligning real-time +video with maps from an onboard GIS server. All ROS 2 nodes are defined in dedicated modules to keep individual file size -down. They are imported here to package namespace for convenience. For example: +down. -.. code-block:: - - #from gisnav.core.gis_node import GISNode - from gisnav import GISNode - -ROS namespace and :term:`core` node names are hard-coded inside the static -public node entrypoints defined here. Other node initialization arguments are p -rovided via ROS 2 launch arguments. +The ROS namespace and node names are hard-coded in :mod:`.constants`, and the +static node entrypoints are defined here in the package root namespace. """ import cProfile import io @@ -37,7 +31,17 @@ from .extensions.qgis_node import QGISNode def run_qgis_node(): - """Spins up a :class:`.QGISNode`""" + """Spins up a :class:`.QGISNode` + + > [!NOTE] Must install extra + > Not available if the ``ggis_node`` Python extra has not been installed. + + .. code-block:: bash + :caption: Install QGISNode + + cd ~/colcon_ws/src/gisnav/gisnav + pip install .[qgis_node] + """ _run(QGISNode, QGIS_NODE_NAME, **_rclpy_node_kwargs) except ModuleNotFoundError as e: @@ -47,7 +51,17 @@ def run_qgis_node(): from .extensions.nmea_node import NMEANode def run_nmea_node(): - """Spins up a :class:`.NMEANode`""" + """Spins up a :class:`.NMEANode` + + > [!NOTE] Must install extra + > Not available if the ``nmea_node`` Python extra has not been installed. + + .. code-block:: bash + :caption: Install NMEANode + + cd ~/colcon_ws/src/gisnav/gisnav + pip install .[nmea_node] + """ _run(NMEANode, NMEA_NODE_NAME, **_rclpy_node_kwargs) except ModuleNotFoundError as e: @@ -57,7 +71,17 @@ def run_nmea_node(): from .extensions.uorb_node import UORBNode def run_uorb_node(): - """Spins up a :class:`.UORBNode`""" + """Spins up a :class:`.UORBNode` + + > [!NOTE] Must install extra + > Not available if the ``uorb_node`` Python extra has not been installed. + + .. code-block:: bash + :caption: Install UORBNode + + cd ~/colcon_ws/src/gisnav/gisnav + pip install .[uorb_node] + """ _run(UORBNode, UORB_NODE_NAME, **_rclpy_node_kwargs) except ModuleNotFoundError as e: diff --git a/gisnav/gisnav/core/__init__.py b/gisnav/gisnav/core/__init__.py index 142889b6..484f6bed 100644 --- a/gisnav/gisnav/core/__init__.py +++ b/gisnav/gisnav/core/__init__.py @@ -1,4 +1,4 @@ -"""Package containing GISNav :term:`core` :term:`ROS` nodes""" +"""This package contains the GISNav core ROS nodes""" from .bbox_node import BBoxNode from .gis_node import GISNode diff --git a/gisnav/gisnav/extensions/__init__.py b/gisnav/gisnav/extensions/__init__.py index 6788e184..f42552cd 100644 --- a/gisnav/gisnav/extensions/__init__.py +++ b/gisnav/gisnav/extensions/__init__.py @@ -1,3 +1 @@ -"""Package containing GISNav :term:`ROS` nodes implementing -:term:`extended functionality` -""" +"""This package contains the GISNav extension ROS nodes""" From b80bfeec641f6436df83429211bd9e66f3fba548 Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 09:19:20 +0100 Subject: [PATCH 23/30] Fix various errors in constants module docstrings [skip ci] --- gisnav/gisnav/constants.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/gisnav/gisnav/constants.py b/gisnav/gisnav/constants.py index 1bd07e6f..2cd0625b 100644 --- a/gisnav/gisnav/constants.py +++ b/gisnav/gisnav/constants.py @@ -19,10 +19,10 @@ """Name of :class:`.BBoxNode` spun up by :func:`.run_bbox_node`""" POSE_NODE_NAME: Final = "pose_node" -"""Name of :class:`.PoseNode` from gisnav_gpu package.""" +"""Name of :class:`.PoseNode` spun up by :func:`.run_pose_node`.""" STEREO_NODE_NAME: Final = "stereo_node" -"""Name of :class:`.StereoNode` spun up by :func:`.run_transform_node`""" +"""Name of :class:`.StereoNode` spun up by :func:`.run_stereo_node`""" NMEA_NODE_NAME: Final = "nmea_node" """Name of :class:`.NMEANode` spun up by :func:`.run_nmea_node`""" @@ -37,7 +37,7 @@ """Relative topic into which :class:`.GISNode` publishes :attr:`.GISNode.orthoimage`.""" ROS_TOPIC_SENSOR_GPS: Final = "/fmu/in/sensor_gps" -"""Topic into which :class:`.UORBNode` publishes :attr:`.GISNode.sensor_gps`.""" +"""Topic into which :class:`.UORBNode` publishes :attr:`.UORBNode.sensor_gps`.""" ROS_TOPIC_RELATIVE_FOV_BOUNDING_BOX: Final = "~/fov/bounding_box" """Relative topic into which :class:`.BBoxNode` publishes @@ -55,13 +55,13 @@ """ ROS_TOPIC_RELATIVE_POSE: Final = "~/pose" -"""Relative topic into which :class:`.StereoNode` publishes -:attr:`.StereoNode.pose`. +"""Relative topic into which :class:`.PoseNode` publishes +:attr:`.PoseNode.pose`. """ ROS_TOPIC_RELATIVE_QUERY_TWIST: Final = "~/vo/twist" -"""Relative topic into which :class:`.StereoNode` publishes -:attr:`.StereoNode.camera_optical_twist_in_query_frame`. +"""Relative topic into which :class:`.PoseNode` publishes +:attr:`.PoseNode.camera_optical_twist_in_camera_optical_frame`. """ MAVROS_TOPIC_TIME_REFERENCE: Final = "/mavros/time_reference" @@ -85,7 +85,7 @@ "/mavros/gimbal_control/device/attitude_status" ) """MAVROS topic for vehicle :class:`.GimbalDeviceAttitudeStatus` message -(MAVLink Gimbal protocl v2) +(MAVLink Gimbal protocol v2) """ ROS_TOPIC_ROBOT_LOCALIZATION_ODOMETRY = "/robot_localization/odometry/filtered" @@ -101,7 +101,7 @@ "map", "earth", ] -"""Allowed ROS message header ``frame_id``s as specified in REP 103 and +"""Allowed ROS message header ``frame_id`` as specified in REP 103 and REP 105. The ``odom`` frame is not used by GISNav but may be published e.g. by MAVROS. """ From ec7371ead3e4a5b4257b5e2ad894ed961b8e7794 Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 09:31:46 +0100 Subject: [PATCH 24/30] Update documentation build instructions [skip ci] --- docs/vitepress/docs/generate-documentation.md | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/docs/vitepress/docs/generate-documentation.md b/docs/vitepress/docs/generate-documentation.md index b4c51d8d..e5a58404 100644 --- a/docs/vitepress/docs/generate-documentation.md +++ b/docs/vitepress/docs/generate-documentation.md @@ -14,6 +14,10 @@ Install Node v18+ on your system by following the [official instructions](https: ## Make docs +The documentation is built in a two-stage process where first the reST Python docstrings are converted into Markdown files using Sphinx, after which they are built into the final static documentation page using VitePress. + +The Makefile `docs` target implements this recipe: + ::: code-group ```bash [Local] @@ -22,31 +26,18 @@ make docs ``` ```bash [Docker] -cd ~/colcon_ws/src/gisnav -make docs +cd ~/colcon_ws/src/gisnav/docker +docker compose -p gisnav run gisnav make docs ``` ::: -## Build Sphinx documentation - - -Make HTML documentation for viewing in web browser: +The static HTML documentation will appear in the below folder: -```bash -cd ~/colcon_ws/src/gisnav/docs -make html -``` - -Make Markdown documentation for processing with VitePress: - -```bash -cd ~/colcon_ws/src/gisnav/docs -sphinx-build -M markdown ./sphinx vitepress/docs/_build/sphinx_ +```text +~/colcon_ws/src/gisnav/docs/vitepress/docs/dist ``` -The HTML documentation will appear in the `~/colcon_ws/src/gisnav/docs/_build/` folder. - ## Serve VitePress documentation ```bash From aba419dacc28921b9f2499ed5b9ac5693a4197f6 Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 09:33:30 +0100 Subject: [PATCH 25/30] Add not implemented notice for docs docker build (gisnav image does not have Node) [skip ci] --- docs/vitepress/docs/generate-documentation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/vitepress/docs/generate-documentation.md b/docs/vitepress/docs/generate-documentation.md index e5a58404..64d6dfd4 100644 --- a/docs/vitepress/docs/generate-documentation.md +++ b/docs/vitepress/docs/generate-documentation.md @@ -25,7 +25,7 @@ cd ~/colcon_ws/src/gisnav make docs ``` -```bash [Docker] +```bash [Docker (Todo: not implemented)] cd ~/colcon_ws/src/gisnav/docker docker compose -p gisnav run gisnav make docs ``` From 3210cf6a58efc6a2106d315d50ed141f8e2746cd Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 09:35:09 +0100 Subject: [PATCH 26/30] Remove duplicate documentation and license sections from mock GPS demo page [skip ci] --- docs/vitepress/docs/README.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/docs/vitepress/docs/README.md b/docs/vitepress/docs/README.md index c681bce5..3d2dac49 100644 --- a/docs/vitepress/docs/README.md +++ b/docs/vitepress/docs/README.md @@ -60,15 +60,3 @@ listener sensor_gps ``` [4]: https://docs.px4.io/main/en/debug/mavlink_shell.html#qgroundcontrol - -## Documentation - -See the [GISNav home page][5] for user and developer guides and API reference. - -[5]: https://hmakelin.github.io/gisnav - -## License - -This software is released under the MIT license. See the [LICENSE.md][6] file for more information. - -[6]: https://github.com/hmakelin/gisnav/blob/master/LICENSE.md From d191822ca79fad02f59fb38e14ab31eff5063825 Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 09:35:55 +0100 Subject: [PATCH 27/30] Remove unintended character [skip ci] --- docs/vitepress/docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/vitepress/docs/README.md b/docs/vitepress/docs/README.md index 3d2dac49..f10cc812 100644 --- a/docs/vitepress/docs/README.md +++ b/docs/vitepress/docs/README.md @@ -31,7 +31,7 @@ You will need to have the [Docker Compose plugin][2] and [NVIDIA Container Toolk Build the Docker images and create and run the containers (downloading and building everything will take a long time): > [!WARNING] Warning: Exposed X server -> This script will expose your X server to the Docker containers to make GUI > applications work. +> This script will expose your X server to the Docker containers to make GUI applications work. ```bash git clone https://github.com/hmakelin/gisnav.git From 787f1f6b5437017a1fdb5b0fb5826b81e9d0eab6 Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 09:38:18 +0100 Subject: [PATCH 28/30] Fix typo on API reference landing page [skip ci] --- docs/sphinx/source/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/source/index.rst b/docs/sphinx/source/index.rst index f58b027c..02e287af 100644 --- a/docs/sphinx/source/index.rst +++ b/docs/sphinx/source/index.rst @@ -4,7 +4,7 @@ API reference Here you can find the automatically generated API reference for the GISNav ROS 2 package. Use the search bar or the below module index to find what you are looking for. > [!WARNING] Unstable API -> GISNav is under active development and the public API is unstabe. Use a specific commit or version tag to mitigate the impact of breaking changes. +> GISNav is under active development and the public API is unstable. Use a specific commit or version tag to mitigate the impact of breaking changes. > [!NOTE] Private API > The private API is also documented here for developer reference as the private method docstrings may contain useful information. The private API should not be relied on for any integrations. From 2ebe0ce84a75d4bbae2392e62da113934469d6ae Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sat, 11 May 2024 09:55:49 +0100 Subject: [PATCH 29/30] Update github docs workflo [skip ci] --- ...inx_docs.yml => build_and_deploy_docs.yml} | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) rename .github/workflows/{build_and_deploy_sphinx_docs.yml => build_and_deploy_docs.yml} (64%) diff --git a/.github/workflows/build_and_deploy_sphinx_docs.yml b/.github/workflows/build_and_deploy_docs.yml similarity index 64% rename from .github/workflows/build_and_deploy_sphinx_docs.yml rename to .github/workflows/build_and_deploy_docs.yml index ff9fdf8b..452c17f1 100644 --- a/.github/workflows/build_and_deploy_sphinx_docs.yml +++ b/.github/workflows/build_and_deploy_docs.yml @@ -1,10 +1,14 @@ -name: Build and deploy Sphinx documentation +name: Build and deploy docs on: - push: - # Run when new version tag is pushed - tags: - - v* + # Push trigger is disabled because the Docker container that builds the docs + # in this workflow will not have the new tag, meaning the API reference would + # have the wrong version tag displayed (commit would still be correct). Need to + # build a new Docker image and then use that to build and deploy the docs. + #push: + # # Run when new version tag is pushed + # tags: + # - v* # Allows running manually from the Actions tab workflow_dispatch: @@ -17,11 +21,11 @@ jobs: run: shell: bash steps: - - name: Build Sphinx docs + - name: Build docs run: | mkdir -p docs/_build docker run ghcr.io/${{ github.repository }}:latest make docs - docker cp $(docker ps -q -l):/opt/colcon_ws/src/gisnav/docs/_build docs + docker cp $(docker ps -q -l):/opt/colcon_ws/src/gisnav/docs/vitepress/docs/dist docs - name: Install node run: | @@ -30,19 +34,13 @@ jobs: curl -fsSL https://deb.nodesource.com/setup_18.x | bash - sudo apt-get install -y nodejs - - name: Setup pages + - name: Configure pages uses: actions/configure-pages@v1 - #- name: Make "Upload pages artifact" step work with containers - # run: | - # mkdir -p ${{runner.temp}} - # mkdir -p /__w/_temp - # ln -s ${{runner.temp}}/artifact.tar /__w/_temp/artifact.tar - - name: Upload pages artifact uses: actions/upload-pages-artifact@v2 with: - path: 'docs/_build/html/' + path: "docs" deploy: needs: build @@ -58,6 +56,6 @@ jobs: runs-on: ubuntu-20.04 steps: - - name: Deploy to GitHub Pages + - name: Deploy pages id: deployment uses: actions/deploy-pages@v3 From b2c340cc0ff7425d0135cf59a99dc06727e1de9e Mon Sep 17 00:00:00 2001 From: Harri Makelin Date: Sun, 12 May 2024 10:52:28 +0100 Subject: [PATCH 30/30] Enable npm to build docs in gisnav container --- docker/mavros/Dockerfile | 6 ++++++ docker/mavros/gisnav/entrypoint.sh | 3 +++ gisnav/setup.py | 3 ++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docker/mavros/Dockerfile b/docker/mavros/Dockerfile index fc56396a..363274c8 100644 --- a/docker/mavros/Dockerfile +++ b/docker/mavros/Dockerfile @@ -107,6 +107,12 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* \ && apt clean +# Node 18 for building the docs (Sphinx dependencies already in setup.py) +RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash \ + && source ~/.nvm/nvm.sh \ + && nvm install 18 \ + && nvm use 18 + ENTRYPOINT ["/entrypoint.sh"] # TODO: proper health check - check that public facing topics are publishing diff --git a/docker/mavros/gisnav/entrypoint.sh b/docker/mavros/gisnav/entrypoint.sh index 3a448930..19e217b0 100644 --- a/docker/mavros/gisnav/entrypoint.sh +++ b/docker/mavros/gisnav/entrypoint.sh @@ -4,6 +4,9 @@ set -e source "/opt/ros/$ROS_VERSION/setup.bash" source "/opt/colcon_ws/install/setup.bash" -- +# Ensure we have npm to build docs +source ~/.nvm/nvm.sh + # Needed for pip installed dev tools like pre-commit and sphinx-build export PATH=/usr/lib/gisnav:$PATH diff --git a/gisnav/setup.py b/gisnav/setup.py index 7bbd2bf8..fcdfca54 100644 --- a/gisnav/setup.py +++ b/gisnav/setup.py @@ -136,7 +136,8 @@ def parse_package_data(cls, package_file: str) -> PackageData: "python-dateutil>=2.8.2", "pyyaml", "sphinx-copybutton", - "sphinx-design" "sphinx-markdown-builder==0.6.6", + "sphinx-design", + "sphinx-markdown-builder==0.6.6", "Sphinx-Substitution-Extensions", "sphinxcontrib-mermaid", "sphinxcontrib-video",