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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Deploy Water Linked Docs
name: Build Docs

on:
push:
Expand Down Expand Up @@ -26,6 +26,7 @@ jobs:
- name: Build docs
run: |
mkdocs build --strict
./check-links.sh

# Only deploy on master
- name: Deploy docs
Expand Down
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,33 @@ This is done by creating a pull request.
Make sure you have Python3 installed.

```
git clone https://github.com/waterlinked/waterlinked.github.io
cd waterlinked.github.io
git clone https://github.com/waterlinked/docs.git
cd docs

python -m venv venv
source venv/bin/activate (Linux)
venv\Scripts\activate.bat (Windows)
pip install -r requirements.txt

mkdocs serve
./install-hooks.sh # (Optional) To automatically check links on git push
```

2. Make changes using your favorite editor

3. Test them

```
mkdocs serve # Allow you to view the changes on your browser
```
* Fire up your browser and go to localhost:8000

Verify links are valid:

```
./check-links.sh
```

## Deploy changes to server
After the changes have been tested and they work, push the changes to the master branch and Github will build the website and publish it on https://docs.waterlinked.com.
After the changes have been tested and they work, push the changes to a branch, and make a merge request. The documentation site will built automatically and links will be verified.

Once the pull request is merged the documentation will be automatically built and published to https://docs.waterlinked.com.
99 changes: 99 additions & 0 deletions check-internal-links.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/usr/bin/env python3
import re
import sys
from pathlib import Path

# Regex for Markdown links: [text](target)
LINK_RE = re.compile(r"\[([^\]]+)\]\(([^)]+)\)")

def slugify_heading(text: str) -> str:
"""Convert Markdown heading text to MkDocs anchor format."""
slug = text.strip().lower()
slug = re.sub(r"[^\w\s-]", "", slug) # remove punctuation
slug = re.sub(r"\s+", "-", slug) # spaces -> dashes
return slug


def extract_headings(md_file: Path):
"""Return a set of anchor slugs from headings in md files."""
headings = set()
if not md_file.is_file():
return headings
for line in md_file.read_text(encoding="utf-8").splitlines():
if line.startswith("#"):
heading_text = line.lstrip("#").strip()
headings.add(slugify_heading(heading_text))
return headings

def check_md_file(md_file: Path):
"""Check links in md files."""
errors = []
if not md_file.is_file(): # <-- add this check
return errors

for line_number, line in enumerate(md_file.read_text(encoding="utf-8").splitlines(), start=1):

stripped = line.strip()
# Skip HTML and python comments
if stripped.startswith("#"):
continue
elif stripped.startswith("<!--") and stripped.endswith("-->"):
continue

for match in LINK_RE.finditer(line):
text, target = match.groups()

# Skip external links
if target.startswith(("http://", "https://", "mailto:")):
continue

# Remove leading slash for site-root relative links
elif target.startswith("/"):
target = target[1:]

# Split anchor from file
if "#" in target:
if target.startswith("#"):
file_part, anchor = md_file, target[1:]
else:
file_part, anchor = target.split("#", 1)
else:
file_part, anchor = target, None

# Resolve relative path'
target_path = Path(file_part) if isinstance(file_part, Path) else Path(file_part) #Make sure it's a Path object
target_file = (md_file.parent / target_path).resolve()

if not target_file.exists() and not target_file.suffix:
target_file = (md_file.parent / (target_path.name + ".md")).resolve()
if not target_file.exists():
errors.append(f"{md_file}:{line_number}: File not found -> {target}")
continue

if target_file.is_file() and anchor:
headings = extract_headings(target_file)
if anchor not in headings:
errors.append(f"{md_file}:{line_number}: Anchor not found -> {target}")

if target_file.is_dir():
continue

return errors


def main(md_dir):
md_dir = Path(md_dir)
all_errors = []
for md_file in md_dir.rglob("*.md"):
all_errors.extend(check_md_file(md_file))

if all_errors:
print(f'Found {len(all_errors)} internal link errors in md files:')
for e in all_errors:
print(e)
return 1
print("No internal link errors found.")
return 0

if __name__ == "__main__":
exit(main(sys.argv[1]))
74 changes: 74 additions & 0 deletions check-links.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/bin/bash
# Pre-push hook: Custom linkchecker for local links, Build MkDocs locally and run LinkChecker on external links

# Exit on error
set -e

# === Paths ===
PROJECT_ROOT="$(git rev-parse --show-toplevel)"
VENV_BIN="$PROJECT_ROOT/venv/bin"
TMP_BUILD_DIR="$PROJECT_ROOT/.tmp-mkdocs-build"
DOCS_DIR="$PROJECT_ROOT/docs"

# Activate venv if not already activated
if [ -f "$VENV_BIN/activate" ]; then
. "$VENV_BIN/activate"
fi

#Checking internal links in markdown files only
echo "Checking internal links in markdown files..."

set +e
#Using custom python script for internal link checking:
python "$PROJECT_ROOT/check-internal-links.py" "$DOCS_DIR"
RESULT_INTERNAL=$?

set -e

if [ $RESULT_INTERNAL -ne 0 ]; then
echo "Internal linkcheck failed! Please fix Markdown links before pushing."
exit 1
fi

echo "Internal linkcheck passed!"

# Check if linkchecker exists
if ! command -v linkchecker >/dev/null 2>&1; then
echo "Error: linkchecker executable not found in PATH"
exit 1
fi

# Remove old temp build if it exists
rm -rf "$TMP_BUILD_DIR"

# Build MkDocs into temporary directory
echo "Building MkDocs locally into $TMP_BUILD_DIR..."
python -m mkdocs build -d "$TMP_BUILD_DIR"

echo "Running LinkChecker on external links against local build..."
set +e
# Only report broken links
linkchecker "file://$TMP_BUILD_DIR/index.html" \
--no-status \
--check-extern \
--recursion-level=2 \
--ignore-url='sitemap\.xml\.gz' \
--ignore-url='https://github.com/.*/edit/' \
--ignore-url='https://www.youtube.com/' \
--ignore-url='.*/assets/.*' \
--ignore-url='.*/images/.*'


RESULT_EXTERNAL=$?
set -e

# Clean up
rm -rf "$TMP_BUILD_DIR"

if [ $RESULT_EXTERNAL -ne 0 ]; then
echo "External linkcheck failed! Please fix broken links before pushing."
exit 1
fi

echo "External linkcheck passed!"
exit 0
2 changes: 1 addition & 1 deletion docs/dvl/axes.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ By default, the body frame and vehicle frame is the same and align with the DVL'

The DVL can be mounted at an angle to the forward direction of a vehicle to which it is attached.

To be precise, the clockwise angle θ in degrees around the Z axis (i.e. in the X-Y plane) from the forward axis of the vehicle to the forward axis of the DVL can be entered as a 'mounting rotation offset' in the [GUI](../gui/configuration), or via the TCP or serial [protocols](../dvl-protocol/).
To be precise, the clockwise angle θ in degrees around the Z axis (i.e. in the X-Y plane) from the forward axis of the vehicle to the forward axis of the DVL can be entered as a 'mounting rotation offset' in the [GUI](../dvl/gui/configuration.md), or via the TCP or serial [protocols](../dvl-protocol/).

The DVL will then output data in the vehicle frame obtained by rotating the [DVL body frame](#body-frame) anti-clockwise around the Z-axis by θ degrees: the X-axis of the velocities outputted by the DVL will be aligned with the forward axis of the vehicle, and, at time zero, the X-axis of the DVL's [frame](../dead-reckoning#frame) for dead reckoning will be aligned with the forward axis of the vehicle.

Expand Down
2 changes: 1 addition & 1 deletion docs/dvl/bluerov-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ You will also need:
* Utility knife
* Wire stripping tool
* Zip ties
* 4 x 30 cm (12") wires (options discussed under [Connect DVL-A50 and BlueROV2](#connect-dvl-a50-and-bluerov2)). If possible, use 2 x red and 2 x black wires.
* 4 x 30 cm (12") wires (options discussed under [Connect DVL-A50 and BlueROV2](#attach-dvl-a50-to-bluerov2)). If possible, use 2 x red and 2 x black wires.
* 15 cm (6") ethernet cable

## Preparation
Expand Down
8 changes: 4 additions & 4 deletions docs/dvl/dead-reckoning.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ The DVL-A50 and DVL-A125 run a dead reckoning algorithm which estimates the orie

## Starting dead reckoning

1. Calibrate the gyroscope by pressing *More -> Calibrate gyro* in the [GUI](../gui/dashboard) whilst the DVL is stationary.
2. Click the 'Reset' button ![](../img/dvl_gui_icon_reset.png) in the [GUI](../gui/dashboard), or send a reset command over the TCP or serial [protocol](../dvl-protocol).
1. Calibrate the gyroscope by pressing *More -> Calibrate gyro* in the [GUI](../dvl/gui/dashboard.md) whilst the DVL is stationary.
2. Click the 'Reset' button ![](../img/dvl_gui_icon_reset.png) in the [GUI](../dvl/gui/dashboard.md), or send a reset command over the TCP or serial [protocol](../dvl-protocol).

Failure to perform gyro calibration will result in less accurate dead reckoning.

Expand All @@ -22,11 +22,11 @@ When the DVL fails in its determination of velocity, speed and position are pred
!!! note
When the DVL is powered on in the air, its position will drift significantly. This should be ignored, and dead reckoning should be [started](#starting-dead-reckoning) in water when ready.

The position of the DVL can be viewed in the GUI [dashboard](../gui/dashboard/) or be fetched by [API](../dvl-protocol/#dead-reckoning-report).
The position of the DVL can be viewed in the GUI [dashboard](../dvl/gui/dashboard.md) or be fetched by [API](../dvl-protocol/#dead-reckoning-report).

## Orientation

The calculation of the orientation of the DVL is based upon the accelerometer and gyroscope measurements of its IMU. The orientation is represented by roll, pitch, and yaw angles, and can be viewed in the GUI [dashboard](../gui/dashboard/) or be fetched by [API](../dvl-protocol/#dead-reckoning-report).
The calculation of the orientation of the DVL is based upon the accelerometer and gyroscope measurements of its IMU. The orientation is represented by roll, pitch, and yaw angles, and can be viewed in the GUI [dashboard](../dvl/gui/dashboard.md) or be fetched by [API](../dvl-protocol/#dead-reckoning-report).

- Roll is a rotation around the X axis of the DVL
- Pitch is a rotation around the Y axis of the DVL
Expand Down
6 changes: 3 additions & 3 deletions docs/dvl/dvl-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ The new configuration will not be returned in the response, but can be obtained

### Velocity report, old format (wrx) [Deprecated]

Same purpose as the [velocity report](#velocity-report), but in an older format:
Same purpose as the [velocity report](#velocity-report-wrz), but in an older format:

`wrx,`*[time],[vx],[vy],[vz],[fom],[altitude],[valid],[status]*

Expand Down Expand Up @@ -571,7 +571,7 @@ wrx,1164.94,0.000,0.000,0.000,2.707,-1.00,n,1*39

### Transducer report, old format (wrt) [Deprecated]

Same purpose as the [transducer report](#transducer-report), but in an older format, and combining the data of all four transducers:
Same purpose as the [transducer report](#transducer-report-wru), but in an older format, and combining the data of all four transducers:

`wrt,`*[dist_1],[dist_2],[dist_3],[dist_4]*

Expand Down Expand Up @@ -605,7 +605,7 @@ Checksum is formatted as a hexadecimal number using 2 lower-case characters (ex:
Compatible implementations:

* Python 3: [crcmod](https://pypi.org/project/crcmod/) `crcmod.predefined.mkPredefinedCrcFun("crc-8")`
* Golang: [github.com/sigurn/crc8](github.com/sigurn/crc8) `crc8.MakeTable(crc8.CRC8)`
* Golang: [github.com/sigurn/crc8](https://github.com/sigurn/crc8) `crc8.MakeTable(crc8.CRC8)`

Example for how to verify checksum using Python 3 and [crcmod](https://pypi.org/project/crcmod/):

Expand Down
4 changes: 2 additions & 2 deletions docs/dvl/dvl-troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ Below is a small list to check if your DVL is not performing as expected or you

In the Support Portal please describe the test environment or test setup you have used and how the DVL is mounted. If possible, open the DVL GUI and go to Diagnostic Log in the left menu. Press the Create Diagnostic Report button. This will automatically record a log file that you can download to your computer. Please add the [Diagnostic reports](gui/diagnostic-report.md) and pictures of the test setup in the support portal. This will help our support team greatly!

If the troubleshooting guide do not help, please check out [FAQ DVL](faq.md#faq-dvl)!
If the troubleshooting guide do not help, please check out [FAQ DVL](../dvl/faq.md)!

### Check if DVL is powered
1. Put the DVL under water and power it on with an adequate power supply.
* Does the DVL pull any current? Expected current consumption: Approximate average 260mA at 18V
* Does the LED turn on?

### LED
1. For LED status, see [LED Signals](../interfaces#LED-Signals).
1. For LED status, see [LED Signals](../dvl/interfaces.md#led-signals).

### Wiring
1. Check that you have wired your DVL correctly, see [Wiring interface](../interfaces#wiring-interface).
Expand Down
4 changes: 2 additions & 2 deletions docs/dvl/gui/dashboard.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Dashboard

Both the DVL-A50 and the DVL-A125 have a web-based GUI. In your favorite web browser, simply navigate to the DVL's [IP address](../../networking).
Both the DVL-A50 and the DVL-A125 have a web-based GUI. In your favorite web browser, simply navigate to the DVL's [IP address](../networking.md).

The default page is a dashboard which provides a summary and visualisation of both the velocity and [dead reckoning](../../dead-reckoning) data outputted by the DVL, as well as an indication of current status.
The default page is a dashboard which provides a summary and visualisation of both the velocity and [dead reckoning](../dead-reckoning.md) data outputted by the DVL, as well as an indication of current status.

## Screenshot

Expand Down
2 changes: 1 addition & 1 deletion docs/dvl/interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ See the DVL's [serial protocol](../dvl-protocol#serial-protocol).

## Ethernet Interface

See [networking](../networking) and the DVL's [TCP protocol](../dvl-protocol#ethernet-protocol-tcp).
See [networking](../networking) and the DVL's [TCP protocol](../dvl/dvl-protocol.md#json-protocol-tcp).


## Code examples
Expand Down
10 changes: 5 additions & 5 deletions docs/dvl/networking.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
The DVL has several services available over ethernet:

* [Multicast DNS](#multicast-dns-mdns) (mDNS) for easy discovery
* Web [GUI](../gui/dashboard), providing a visualisation of the data outputted by the DVL, as well as configuration, [updating of software](../sw-update), diagnostics, and more.
* Web [GUI](../dvl/gui/dashboard.md), providing a visualisation of the data outputted by the DVL, as well as configuration, [updating of software](../sw-update), diagnostics, and more.
* TCP [data stream](#tcp-data-stream)
* [Software updates](../sw-update)

## Network configuration

### Multicast DNS (mDNS)

The DVL runs a DHCP client which will attempt to obtain an IP address from a DHCP server (e.g. in a router) on the same network, and supports mDNS: the mDNS name of the DVL is `waterlinked-dvl`. On a computer which supports mDNS, one can then simply access the web [GUI](../gui/dashboard) at [http://waterlinked-dvl](http://waterlinked-dvl).
The DVL runs a DHCP client which will attempt to obtain an IP address from a DHCP server (e.g. in a router) on the same network, and supports mDNS: the mDNS name of the DVL is `waterlinked-dvl`. On a computer which supports mDNS, one can then simply access the web [GUI](../dvl/gui/dashboard.md) at [http://waterlinked-dvl](http://waterlinked-dvl).

!!! note
If no DHCP server is available on the network, it is recommended to use the [fallback IP](#fallback-ip) or configure a [static IP address](#via-the-gui), as the DVL can spend up to 5 minutes searching for a DHCP server.
Expand All @@ -23,11 +23,11 @@ The DVL will always be available with the static IP address: **192.168.194.95**.
* Connect an ethernet cable directly from the DVL to your computer.
* For an/the ethernet interface of your computer, configure it to have a static IP address in the same subnet as 192.168.194.95, e.g. 192.168.194.90 or anything else of the form 192.168.194.xxx if using a subnet of the form 255.255.255.0 (aka 192.168.194.0/24).
* Activate the ethernet interface of your computer which you configured in the previous step.
* In a web browser, open http://192.168.194.95 to access the web [GUI](../gui/dashboard).
* In a web browser, open http://192.168.194.95 to access the web [GUI](../dvl/gui/dashboard.md).

### Via the GUI

The IP address of the DVL can be configured in two ways via the web [GUI](../gui/dashboard):
The IP address of the DVL can be configured in two ways via the web [GUI](../dvl/gui/dashboard.md):

* DCHP client: an IP address is obtained from a DHCP server (e.g. in a router) on the same network, as in the discussion of [mDNS](#multicast-dns-mdns) above.
* Static: equip the DVL with a certain static IP address.
Expand All @@ -39,4 +39,4 @@ After the IP configuration of the DVL is modified, the DVL needs to be rebooted

## TCP data stream

The DVL can output data and receive commands over Transmission Control Protocol (TCP). A TCP server is available on port **16171** which outputs the latest data to all connected clients, and which listens to commands from any of these connected clients. See the DVL's TCP [protocol](./dvl-protocol.md#ethernet-protocol-tcp).
The DVL can output data and receive commands over Transmission Control Protocol (TCP). A TCP server is available on port **16171** which outputs the latest data to all connected clients, and which listens to commands from any of these connected clients. See the DVL's TCP [protocol](../dvl/dvl-protocol.md#json-protocol-tcp).
Loading