Package: dedrunk
Architecture: amd64
Version: 0.1.1-1
Priority: optional
Section: science
Maintainer: stelzo <stelzo@steado.de>
Installed-Size: 18297
Depends: libc6 (>= 2.39)
Filename: pool/main/resolute/dedrunk_0.1.1-1_amd64.deb
Size: 5434624
MD5sum: 0bd7594e126e1163382741b7daf70826
SHA1: dda95eb97d3bf4f6ce5b05d27d523452bf642f91
SHA256: d8fb0e280b641ec3d83ad83ec4a1e938a063c5564a28bc70a6d2db42bc0d4e00
SHA512: 8043d336fa593c1435d214477e6f3ec9a174e8cc8c66ecab1e83ff8488ed3d45d6760038dd1b6157e7a20ebdf1c266d308c5f38aaada8a2ebc812f54842539c6
Homepage: https://codeberg.org/stelzo/dedrunk
Description: Kalibr IMU calibration from MCAP recordings.
 # dedrunk
 .
 Getting your IMU sober again: Kalibr calibration from MCAP recordings.
 .
 A command-line front end for the
 [imu-calib](https://codeberg.com/stelzo/imu-calib) crate for IMU intrinsics and
 extrinsics with Lidar implementing [Zhu, Ren and Zhang, "Robust Real-time
 LiDAR-inertial Initialization", IROS 2022](https://arxiv.org/pdf/2202.11006).
 .
 ## Install
 .
 ```bash
 # Install from source:
 cargo install dedrunk
 .
 # or, for a prebuilt binary with binstall:
 cargo binstall dedrunk
 .
 # PyPI
 pip install dedrunk-cli
 .
 # Arch Linux
 yay -S dedrunk-bin
 .
 # Homebrew on MacOS
 brew install uos/tools/dedrunk
 ```
 .
 ## Usage
 .
 ### Noise parameters
 .
 Record at least three hours somewhere undisturbed, away from vibration.
 .
 ```bash
 dedrunk noise recording.mcap
 ```
 .
 ### Scale, misalignment and biases
 .
 Record the sensor held still in at least nine well-spread orientations,
 rotating about a different axis between each one. Start at rest — the first
 seconds measure the noise floor that tells a hold from a move.
 .
 ```bash
 dedrunk intrinsics recording.mcap
 ```
 .
 The stationary stretches are found automatically; there is nothing to mark up.
 .
 Guidelines for a good calibration:
 - **Spread the orientations out.** Twelve attitudes covering all octants beats
 twelve small tilts. Nine is the minimum for the full model. (For only the six
 axis-aligned faces, set `intrinsics.accel_model = "scale-bias"`).
 - **Rotate about a different axis each time.** If every move is about the same
 axis, the gyroscope fit fails and `dedrunk` reports bias only.
 .
 Check the printed residuals: `accelerometer rms` should be near the sensor's
 own noise level (a few mm/s² for a decent MEMS part), and `gyroscope rms` well
 under a degree.
 .
 ### Where the IMU sits relative to the LiDAR
 .
 Recovers the rigid transform between a LiDAR and an IMU, plus the clock offset,
 gravity direction and both biases, from a recording of the two moving together.
 Follows [Zhu, Ren and Zhang, "Robust Real-time LiDAR-inertial Initialization",
 IROS 2022](https://arxiv.org/pdf/2202.11006).
 .
 Needs a LiDAR-only trajectory. Poses from LiDAR-inertial odometry already
 assumed an extrinsic, so feeding those back returns the assumption:
 .
 ```bash
 pelorus offline recording.mcap -c pelorus.rl -o imu.lidar_only=true
 --output-rosbag lidar_traj
 dedrunk extrinsics lidar_traj --imu-bag recording.mcap
 ```
 .
 #### What to record
 .
 The motion decides what the tool can measure. There is no substitute for a good
 calibration dance:
 .
 - **Rotation**: Rotate the platform about all three axes (roll, pitch, yaw). A
 single axis leaves the rotation about it unconstrained.
 - **Translation (lever arm)**: The lever arm is only observable during
 rotation. Rotate briskly; slow turns contribute almost nothing.
 - **Gravity and Bias**: To separate gravity from accelerometer bias, the
 platform must tilt significantly. A robot that only yaws on flat ground cannot
 measure its vertical translation offset.
 .
 In practice:
 .
 - Pick the platform up if you can and rotate it about all three axes. Two
 minutes of that beats an hour of driving.
 - Rotate briskly.
 - Keep enough structure in view for the LiDAR odometry to track.
 - Stay still for the first few seconds.
 - If the platform cannot leave the ground, record anyway: `dedrunk` will report
 what it *could* measure and clearly state what it couldn't.
 .
 Read the observability report before trusting the numbers:
 .
 ```
 gravity:      [+0.723, +0.239, -6.267] m/s^2  (|g| = 6.313)
 residuals:    acceleration 4.126 m/s^2
 attitude spread:                       2.1°  (separates gravity from accel
 bias)
 angular excitation (rms, Lidar frame): [0.030, 0.037, 0.143] rad/s
 warning: the fitted gravity is 6.31 m/s^2 against an expected 9.81, off by 36%.
 ...
 warning: the platform's attitude varies by only 2.1° over the recording, so
 gravity
 and the accelerometer bias cannot be told apart ...
 ```
 .
 A `|g|` far from 9.81 means the translation, gravity, and bias are unobservable
 in this recording. The rotation and time offset come from the gyroscope alone
 and remain valid.
 .
 The result is also printed as a ready-to-paste `imu.from_lidar_transform` block
 for pelorus.
 .
 ### Combine and use
 .
 ```bash
 dedrunk merge imu.yaml imu0.yaml --output calibration.yaml
 dedrunk show calibration.yaml
 ```
 .
 `dedrunk topics recording.mcap` lists the IMU topics in a bag, for when
 `input.topic` has to be set explicitly.
 .
 Then online, with [imu-calib](https://github.com/stelzo/imu-calib):
 .
 ```rust
 let intrinsics = ImuIntrinsics::from_kalibr_yaml("calibration.yaml")?;
 let corrector = intrinsics.corrector()?;          // build once
 let (accel, gyro) = corrector.correct(raw_accel, raw_gyro);
 ```
 .
 ## Shell completions
 .
 Run `dedrunk completions` to detect the current shell and install completions
 for
 the current user. Pass a shell explicitly, such as `dedrunk completions bash`,
 to
 print a completion script for packaging or a system-wide installation instead.
 .
 ## Configuration
 .
 ```bash
 dedrunk config generate recommended > dedrunk.rl   # the settings most runs
 touch
 dedrunk config generate full > dedrunk.rl    # every setting, annotated
 dedrunk config verify dedrunk.rl
 dedrunk intrinsics recording.mcap --config dedrunk.rl
 ```
 .
 Single settings can be overridden with `-o KEY=VALUE`, repeatable. The value is
 Ratslang, units and all:
 .
 ```bash
 dedrunk intrinsics recording.mcap \
   -o input.topic='"/imu/data_raw"' \
   -o intrinsics.static.min_hold=2s \
   -o intrinsics.gravity=9.8127 \
   -O my-imu.yaml
 ```
 .
 ### License
 .
 <sup>
 Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
 2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
 </sup>
 .
 <br>
 .
 <sub>
 Unless you explicitly state otherwise, any contribution intentionally submitted
 for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
 be dual licensed as above, without any additional terms or conditions.
 </sub>

Package: libpelorus-dev
Architecture: amd64
Version: 0.0.9-1
Priority: optional
Section: libs
Installed-Size: 46
Depends: libpelorus ( = 0.0.9-1 )
Filename: pool/main/resolute/libpelorus-dev_0.0.9-1_amd64.deb
Size: 8856
MD5sum: 60fa11b3bfc0e8b395d3bfc3f56040e6
SHA1: 44dd84de5ba2ecdc4fe9a9398698f9ee0d3aeab3
SHA256: 4ef7da5080f30939f302edf04f0ee0a21fdf0b877aa48215b0f775508c8eddd0
SHA512: fb0f551e58a7d429f59d39204ed00ddf43d84a70acc32ffca8bc738133e7db80ead95f412e9ab4aeb5a828607f1ddead956a1391540209cea04ca107fe54ae85
Homepage: https://codeberg.org/stelzo/pelorus
Description: Pelorus Odometry as a C library.

Package: libpelorus-vulkan-dev
Architecture: amd64
Version: 0.0.9-1
Priority: optional
Section: libs
Installed-Size: 46
Depends: libpelorus-vulkan ( = 0.0.9-1 )
Filename: pool/main/resolute/libpelorus-vulkan-dev_0.0.9-1_amd64.deb
Size: 8868
MD5sum: a17fde41e9bec1c398ca8fd884aa0da3
SHA1: 2b808cbba5f36f93c2bab903f1ccfa322e018d0f
SHA256: 486d962a14581db9e4058084de32909483496b5c9fabeca1abcf4a0027927172
SHA512: 46d31a4b3ed77142302f12a372e1479f68dedeaedf953b62cf17faea62ef1fbaa977033e5977a97f5d1e0f13560d86f6fc0703d9de778287337778c2782435ef
Homepage: https://codeberg.org/stelzo/pelorus
Description: Pelorus Odometry as a C library.

Package: libpelorus-vulkan
Architecture: amd64
Version: 0.0.9-1
Priority: optional
Section: libs
Installed-Size: 108912
Depends: libvulkan1
Filename: pool/main/resolute/libpelorus-vulkan_0.0.9-1_amd64.deb
Size: 17233224
MD5sum: e1d61b1c37b7307ab02404d0fb448b92
SHA1: 5d7c5f64def2d16bfbe26aa04be0b0281e56aa01
SHA256: edff9c5225e67861424b6441b679596937bb38b471e4bb947bbcbb5201f09c33
SHA512: 5fc3baa18859c660fee16d9e94b509fdf9ab6c8321117400fbdfa38b07e0b26c282bebd3e11e211ae3497a8eef68b9285f0e55fdefbd0590925cb10447f8c4dc
Homepage: https://codeberg.org/stelzo/pelorus
Description: Pelorus Odometry as a C library.
 # pelorus_c
 .
 C library bindings for Pelorus.
 .
 ## Building and Installation
 .
 ### CMake
 .
 The library can be built and installed using CMake:
 .
 ```bash
 cd pelorus_c
 mkdir build && cd build
 cmake ..
 make
 sudo make install
 ```
 .
 #### CMake Options
 .
 - `PELORUS_BUILD_SHARED` - Build shared library (default: ON)
 - `PELORUS_BUILD_STATIC` - Build static library (default: ON)
 - `PELORUS_BUILD_EXAMPLES` - Build C/C++ examples (default: OFF)
 - `PELORUS_INSTALL` - Install library and headers (default: ON)
 .
 #### Feature Flags
 .
 - `PELORUS_FEATURE_RAYON` - Enable Rayon CPU parallelism (default: ON)
 - `PELORUS_FEATURE_METAL` - Enable Metal acceleration (default: OFF)
 - `PELORUS_FEATURE_CUDA` - Enable CUDA acceleration (default: OFF)
 - `PELORUS_FEATURE_VULKAN` - Enable Vulkan acceleration (default: OFF)
 - `PELORUS_FEATURE_NO_IMU_SYNC` - Disable IMU synchronization (default: OFF)
 - `PELORUS_FEATURE_LARGE_PROCESSING_QUEUE` - Use larger internal processing
 queues (default: OFF)
 .
 Select at most one GPU backend. Metal is available on macOS. CUDA requires a
 supported NVIDIA CUDA installation. Vulkan requires a compatible Vulkan device
 and driver. If no GPU backend is selected, Pelorus uses the CPU backend.
 .
 Example with CUDA:
 .
 ```bash
 cmake .. -DPELORUS_FEATURE_CUDA=ON
 ```
 .
 ### Using Cargo (Direct)
 .
 You can also build directly with Cargo:
 .
 ```bash
 cargo build --package pelorus_c --release
 .
 # Select one GPU backend when needed:
 cargo build --package pelorus_c --release --features cuda
 cargo build --package pelorus_c --release --features vulkan
 cargo build --package pelorus_c --release --features metal
 ```
 .
 ## Using the Library
 .
 ### In CMake Projects
 .
 After installation, use `find_package`:
 .
 ```cmake
 find_package(pelorus REQUIRED)
 add_executable(my_app main.c)
 target_link_libraries(my_app pelorus::pelorus)
 ```
 .
 See [examples/CMAKE_USAGE.md](examples/CMAKE_USAGE.md) for more details.
 .
 ### With pkg-config
 .
 ```bash
 gcc my_app.c $(pkg-config --cflags --libs pelorus) -o my_app
 ```
 .
 ### C++ Wrapper
 .
 A modern C++ wrapper is available in `pelorus.hpp`.
 .
 ```cpp
 #include <pelorus.hpp>
 #include <iostream>
 .
 int main() {
     pelorus::Pelorus pelorus;
 .
     // Add measurements
     pelorus::ImuMsg imu;
     // ... fill IMU data ...
     pelorus.add_imu(imu);
 .
     // Receive odometry
     auto res = pelorus.try_recv();
     if (res.status == pelorus::Pelorus::STATUS_OK) {
         const auto &odom = res.odom;
         std::cout << "Position: " << odom.position()[0] << "\n";
     }
 .
     return 0;
 }
 ```
 .
 ### PCL Integration
 .
 For projects using the Point Cloud Library (PCL):
 .
 ```cpp
 #include <pelorus.hpp>
 #include <pcl/point_cloud.h>
 #include <pcl/point_types.h>
 .
 pelorus::Pelorus pelorus;
 .
 // Convert and add PCL cloud to Pelorus
 pcl::PointCloud<pelorus::pcl::PointXYZIT>::Ptr cloud(
     new pcl::PointCloud<pelorus::pcl::PointXYZIT>());
 // ... fill cloud ...
 pelorus::pcl::add_pointcloud(pelorus, cloud, timestamp_sec, timestamp_nsec);
 .
 // Get result
 auto result = pelorus.recv();
 if (result.status == pelorus::Pelorus::STATUS_OK) {
     auto result_cloud =
         result.odom.get_cloud_pcl<pcl::PointCloud<pcl::PointXYZI>::Ptr>();
 .
     // Or use the standalone function
     auto result_cloud2 = pelorus::pcl_helpers::to_pcl_cloud(
         result.odom.points(), result.odom.num_points());
 }
 ```
 .
 ### Eigen3 Integration
 .
 For projects using Eigen for transformations:
 .
 ```cpp
 #include <pelorus.hpp>
 #include <iostream>
 .
 pelorus::Pelorus pelorus;
 auto result = pelorus.recv();
 if (result.status == pelorus::Pelorus::STATUS_OK) {
     const auto &odom = result.odom;
 .
     // Get odometry as a transformation matrix
     Eigen::Isometry3d transform = pelorus::eigen::to_transform(odom);
 .
     // Transform a point from sensor to world coordinates
     Eigen::Vector3d point_sensor(1.0, 0.0, 0.0);
     Eigen::Vector3d point_world = transform * point_sensor;
 .
     // Get all odometry data as Eigen types
     auto state = pelorus::eigen::OdometryState::from_pelorus(odom);
     std::cout << "Position: "
               << state.transform.translation().transpose() << "\n";
     std::cout << "Velocity: " << state.linear_velocity.transpose() << "\n";
     std::cout << "Accelerometer bias: " << state.accel_bias.transpose() <<
 "\n";
     std::cout << "Gyroscope bias: " << state.gyro_bias.transpose() << "\n";
 .
     // Extract individual components
     Eigen::Vector3d position = pelorus::eigen::to_position(odom.position());
     Eigen::Quaterniond orientation =
         pelorus::eigen::to_quaternion(odom.orientation());
     Eigen::Matrix<double, 6, 6> pose_cov =
         pelorus::eigen::get_pose_covariance(*odom.get());
 }
 ```
 .
 ## Configuration
 .
 The library uses `.rl` configuration files:
 .
 ```c
 // Use custom configuration file to override defaults for your setup
 struct PelorusHandle* pelorus = pelorus_create("/path/to/my_config.rl");
 .
 // Or: configure creation-time behavior without editing the on-disk .rl
 pelorus_create_options opts = PELORUS_CREATE_OPTIONS_DEFAULT;
 opts.config_path = "/path/to/my_config.rl";
 // opts.device_specific_pointcloud2 = 0; // disable device-specific
 interpretation
 struct PelorusHandle* pelorus2 = pelorus_create_with_options(&opts);
 ```
 .
 You can generate a default config file with:
 .
 ```bash
 pelorus config generate recommended > my_config.rl
 .
 # Or generate the complete default configuration:
 pelorus config generate full > my_config.rl
 ```
 .
 All parameters (map resolution, IMU covariances, Lidar vendor, filtering
 options, etc.) are configurable.
 .
 ## API Design
 .
 Pelorus provides three ways to receive odometry results:
 .
 Bias-aware callers can use `pelorus_try_recv_with_bias()`,
 `pelorus_recv_with_bias()`, or `pelorus_set_callback_with_bias()`. These
 additive APIs return `pelorus_imu_bias` without changing the layout of the
 existing `pelorus_odometry` structure. The C++ wrapper uses these APIs
 automatically and exposes `Odometry::accel_bias()` and
 `Odometry::gyro_bias()`.
 .
 ### 1. Non-blocking (Polling)
 Use `pelorus_try_recv()` for non-blocking checks:
 ```c
 pelorus_odometry odom;
 int status = pelorus_try_recv(pelorus, &odom);
 if (status == PELORUS_STATUS_OK) {
     // Process odometry
     pelorus_free_odometry(&odom);
 } else if (status == PELORUS_STATUS_NO_DATA) {
     // No data available yet
 } else if (status == PELORUS_STATUS_CALLBACK_SET) {
     // Callback mode is active; polling APIs are unavailable
 }
 ```
 .
 ### 2. Blocking (Synchronous)
 Use `pelorus_recv()` to wait for results:
 ```c
 pelorus_odometry odom;
 int status = pelorus_recv(pelorus, &odom);  // Blocks until data available
 if (status == PELORUS_STATUS_OK) {
     // Process odometry
     pelorus_free_odometry(&odom);
 } else if (status == PELORUS_STATUS_CALLBACK_SET) {
     // Callback mode is active; cannot use blocking APIs
 }
 ```
 .
 ### 3. Callback (Asynchronous)
 Use `pelorus_set_callback()` for event-driven processing:
 ```c
 void odometry_callback(const pelorus_odometry *odom, void *user_data) {
     // Process odometry (called from background thread)
     // No need to call pelorus_free_odometry() - handled automatically
 }
 .
 // Register callback
 pelorus_set_callback(pelorus, odometry_callback, user_data);
 .
 // Disable callback
 pelorus_set_callback(pelorus, NULL, NULL);
 ```
 .
 **Note**: When using callbacks, `pelorus_try_recv()` and `pelorus_recv()` will
 not receive data. Callbacks and polling/blocking APIs are mutually exclusive.
 .
 ## Examples
 .
 See the `examples/` directory for complete usage examples:
 .
 - `async_callback.c` - Callback-based asynchronous example
 - `sync_blocking.c` - Synchronous blocking example
 - `sync_blocking_pcl_eigen.cpp` - C++ wrapper example with optional PCL/Eigen
 support
 - `eigen_example.cpp` - Eigen conversion example
 .
 Build and run examples:
 ```bash
 cd examples
 make
 ./async_callback
 ./sync_blocking
 ```
 .
 ### Raw point helpers
 .
 If you already have raw point buffers (similar to the Python numpy input), you
 can avoid
 building a full `pelorus_pointcloud2` and instead call one of:
 .
 - `pelorus_add_points_xyzt_f32(...)` for Nx4 `[x,y,z,t]` (`t` is ns offset,
 cast to `uint32_t`)
 - `pelorus_add_points_xyzit_f32(...)` for Nx5 `[x,y,z,intensity,t]`
 .
 All of these are internally converted into a PointCloud2 with
 `x,y,z,intensity,t` fields.
 .
 ## Threading Model
 .
 The API is designed to be simple from the caller's perspective:
 - `pelorus_add_imu()`, `pelorus_add_odom()`, and `pelorus_add_pointcloud()`
 return immediately
 - Processing happens asynchronously in background threads
 - `pelorus_try_recv()` is non-blocking and returns immediately
 - `pelorus_recv()` blocks until data is available
 - Callbacks are invoked from a background thread
 .
 ## Memory Management
 .
 - Results from `pelorus_try_recv()` and `pelorus_recv()` must be freed with
 `pelorus_free_odometry()`
 - Callback results are automatically freed after the callback returns
 - The `PelorusHandle` must be destroyed with `pelorus_destroy()`
 .
 ## Timestamp semantics
 .
 Pelorus uses two timestamp levels:
 .
 1. **IMU/Odom timestamps** (`timestamp_sec` + `timestamp_nanosec`)
     - Absolute timestamps (typically epoch / ROS time) used for
 synchronization.
 .
 2. **Lidar timestamps**
     - `pelorus_pointcloud2.header_timestamp_sec/nanosec`: absolute reference
 time for the scan.
     - Per-point field `t` (if provided): `uint32` **nanoseconds offset**,
 **non-negative** and **forward in time** relative to the scan header timestamp.
 .
 The effective point time is:
 .
 `point_time_abs = header_time_abs + (t * 1e-9)`
 .
 ### Pelorus expects a simplified, single-row (unstructured) little-endian
 `PointCloud2`.
 .
 Interpretation of per-point timing depends on the parsing mode:
 .
 - When Pelorus uses *device-specific* parsing (the default), field meanings —
 including any `t` field — are interpreted according to the vendor/device
 configuration and may vary.
 - When using the generic parsing path (`device_specific_pointcloud2 == false`),
 Pelorus expects `t` to be a **non-negative** (`uint32`) nanoseconds offset
 relative to the scan header timestamp.
 .
 For generic point input, prefer `pelorus_add_points_xyzt_f32()` or
 `pelorus_add_points_xyzit_f32()`.
 .
 ## License
 .
 Dual-licensed under MIT and Apache 2.0, consistent with the Pelorus project.

Package: libpelorus
Architecture: amd64
Version: 0.0.9-1
Priority: optional
Section: libs
Installed-Size: 74717
Depends: libc6 (>= 2.43)
Filename: pool/main/resolute/libpelorus_0.0.9-1_amd64.deb
Size: 11617288
MD5sum: dd19d03696a240aa574d25c1301035e3
SHA1: 40666af68d27bc8f32c7a6c8da2e0364d38b3a25
SHA256: 15baead31b3f2ed2e7a847c8916d89be95076d28eaf6c6fdd43779bc98f1d69d
SHA512: 8fbfdc535510ff88d1b649d4bc3014d9a541c582efce2bf83b489780babe674e441f08f40f19461253b0d8b627cb1716c74265dc011f86b9bc551558631b7559
Homepage: https://codeberg.org/stelzo/pelorus
Description: Pelorus Odometry as a C library.
 # pelorus_c
 .
 C library bindings for Pelorus.
 .
 ## Building and Installation
 .
 ### CMake
 .
 The library can be built and installed using CMake:
 .
 ```bash
 cd pelorus_c
 mkdir build && cd build
 cmake ..
 make
 sudo make install
 ```
 .
 #### CMake Options
 .
 - `PELORUS_BUILD_SHARED` - Build shared library (default: ON)
 - `PELORUS_BUILD_STATIC` - Build static library (default: ON)
 - `PELORUS_BUILD_EXAMPLES` - Build C/C++ examples (default: OFF)
 - `PELORUS_INSTALL` - Install library and headers (default: ON)
 .
 #### Feature Flags
 .
 - `PELORUS_FEATURE_RAYON` - Enable Rayon CPU parallelism (default: ON)
 - `PELORUS_FEATURE_METAL` - Enable Metal acceleration (default: OFF)
 - `PELORUS_FEATURE_CUDA` - Enable CUDA acceleration (default: OFF)
 - `PELORUS_FEATURE_VULKAN` - Enable Vulkan acceleration (default: OFF)
 - `PELORUS_FEATURE_NO_IMU_SYNC` - Disable IMU synchronization (default: OFF)
 - `PELORUS_FEATURE_LARGE_PROCESSING_QUEUE` - Use larger internal processing
 queues (default: OFF)
 .
 Select at most one GPU backend. Metal is available on macOS. CUDA requires a
 supported NVIDIA CUDA installation. Vulkan requires a compatible Vulkan device
 and driver. If no GPU backend is selected, Pelorus uses the CPU backend.
 .
 Example with CUDA:
 .
 ```bash
 cmake .. -DPELORUS_FEATURE_CUDA=ON
 ```
 .
 ### Using Cargo (Direct)
 .
 You can also build directly with Cargo:
 .
 ```bash
 cargo build --package pelorus_c --release
 .
 # Select one GPU backend when needed:
 cargo build --package pelorus_c --release --features cuda
 cargo build --package pelorus_c --release --features vulkan
 cargo build --package pelorus_c --release --features metal
 ```
 .
 ## Using the Library
 .
 ### In CMake Projects
 .
 After installation, use `find_package`:
 .
 ```cmake
 find_package(pelorus REQUIRED)
 add_executable(my_app main.c)
 target_link_libraries(my_app pelorus::pelorus)
 ```
 .
 See [examples/CMAKE_USAGE.md](examples/CMAKE_USAGE.md) for more details.
 .
 ### With pkg-config
 .
 ```bash
 gcc my_app.c $(pkg-config --cflags --libs pelorus) -o my_app
 ```
 .
 ### C++ Wrapper
 .
 A modern C++ wrapper is available in `pelorus.hpp`.
 .
 ```cpp
 #include <pelorus.hpp>
 #include <iostream>
 .
 int main() {
     pelorus::Pelorus pelorus;
 .
     // Add measurements
     pelorus::ImuMsg imu;
     // ... fill IMU data ...
     pelorus.add_imu(imu);
 .
     // Receive odometry
     auto res = pelorus.try_recv();
     if (res.status == pelorus::Pelorus::STATUS_OK) {
         const auto &odom = res.odom;
         std::cout << "Position: " << odom.position()[0] << "\n";
     }
 .
     return 0;
 }
 ```
 .
 ### PCL Integration
 .
 For projects using the Point Cloud Library (PCL):
 .
 ```cpp
 #include <pelorus.hpp>
 #include <pcl/point_cloud.h>
 #include <pcl/point_types.h>
 .
 pelorus::Pelorus pelorus;
 .
 // Convert and add PCL cloud to Pelorus
 pcl::PointCloud<pelorus::pcl::PointXYZIT>::Ptr cloud(
     new pcl::PointCloud<pelorus::pcl::PointXYZIT>());
 // ... fill cloud ...
 pelorus::pcl::add_pointcloud(pelorus, cloud, timestamp_sec, timestamp_nsec);
 .
 // Get result
 auto result = pelorus.recv();
 if (result.status == pelorus::Pelorus::STATUS_OK) {
     auto result_cloud =
         result.odom.get_cloud_pcl<pcl::PointCloud<pcl::PointXYZI>::Ptr>();
 .
     // Or use the standalone function
     auto result_cloud2 = pelorus::pcl_helpers::to_pcl_cloud(
         result.odom.points(), result.odom.num_points());
 }
 ```
 .
 ### Eigen3 Integration
 .
 For projects using Eigen for transformations:
 .
 ```cpp
 #include <pelorus.hpp>
 #include <iostream>
 .
 pelorus::Pelorus pelorus;
 auto result = pelorus.recv();
 if (result.status == pelorus::Pelorus::STATUS_OK) {
     const auto &odom = result.odom;
 .
     // Get odometry as a transformation matrix
     Eigen::Isometry3d transform = pelorus::eigen::to_transform(odom);
 .
     // Transform a point from sensor to world coordinates
     Eigen::Vector3d point_sensor(1.0, 0.0, 0.0);
     Eigen::Vector3d point_world = transform * point_sensor;
 .
     // Get all odometry data as Eigen types
     auto state = pelorus::eigen::OdometryState::from_pelorus(odom);
     std::cout << "Position: "
               << state.transform.translation().transpose() << "\n";
     std::cout << "Velocity: " << state.linear_velocity.transpose() << "\n";
     std::cout << "Accelerometer bias: " << state.accel_bias.transpose() <<
 "\n";
     std::cout << "Gyroscope bias: " << state.gyro_bias.transpose() << "\n";
 .
     // Extract individual components
     Eigen::Vector3d position = pelorus::eigen::to_position(odom.position());
     Eigen::Quaterniond orientation =
         pelorus::eigen::to_quaternion(odom.orientation());
     Eigen::Matrix<double, 6, 6> pose_cov =
         pelorus::eigen::get_pose_covariance(*odom.get());
 }
 ```
 .
 ## Configuration
 .
 The library uses `.rl` configuration files:
 .
 ```c
 // Use custom configuration file to override defaults for your setup
 struct PelorusHandle* pelorus = pelorus_create("/path/to/my_config.rl");
 .
 // Or: configure creation-time behavior without editing the on-disk .rl
 pelorus_create_options opts = PELORUS_CREATE_OPTIONS_DEFAULT;
 opts.config_path = "/path/to/my_config.rl";
 // opts.device_specific_pointcloud2 = 0; // disable device-specific
 interpretation
 struct PelorusHandle* pelorus2 = pelorus_create_with_options(&opts);
 ```
 .
 You can generate a default config file with:
 .
 ```bash
 pelorus config generate recommended > my_config.rl
 .
 # Or generate the complete default configuration:
 pelorus config generate full > my_config.rl
 ```
 .
 All parameters (map resolution, IMU covariances, Lidar vendor, filtering
 options, etc.) are configurable.
 .
 ## API Design
 .
 Pelorus provides three ways to receive odometry results:
 .
 Bias-aware callers can use `pelorus_try_recv_with_bias()`,
 `pelorus_recv_with_bias()`, or `pelorus_set_callback_with_bias()`. These
 additive APIs return `pelorus_imu_bias` without changing the layout of the
 existing `pelorus_odometry` structure. The C++ wrapper uses these APIs
 automatically and exposes `Odometry::accel_bias()` and
 `Odometry::gyro_bias()`.
 .
 ### 1. Non-blocking (Polling)
 Use `pelorus_try_recv()` for non-blocking checks:
 ```c
 pelorus_odometry odom;
 int status = pelorus_try_recv(pelorus, &odom);
 if (status == PELORUS_STATUS_OK) {
     // Process odometry
     pelorus_free_odometry(&odom);
 } else if (status == PELORUS_STATUS_NO_DATA) {
     // No data available yet
 } else if (status == PELORUS_STATUS_CALLBACK_SET) {
     // Callback mode is active; polling APIs are unavailable
 }
 ```
 .
 ### 2. Blocking (Synchronous)
 Use `pelorus_recv()` to wait for results:
 ```c
 pelorus_odometry odom;
 int status = pelorus_recv(pelorus, &odom);  // Blocks until data available
 if (status == PELORUS_STATUS_OK) {
     // Process odometry
     pelorus_free_odometry(&odom);
 } else if (status == PELORUS_STATUS_CALLBACK_SET) {
     // Callback mode is active; cannot use blocking APIs
 }
 ```
 .
 ### 3. Callback (Asynchronous)
 Use `pelorus_set_callback()` for event-driven processing:
 ```c
 void odometry_callback(const pelorus_odometry *odom, void *user_data) {
     // Process odometry (called from background thread)
     // No need to call pelorus_free_odometry() - handled automatically
 }
 .
 // Register callback
 pelorus_set_callback(pelorus, odometry_callback, user_data);
 .
 // Disable callback
 pelorus_set_callback(pelorus, NULL, NULL);
 ```
 .
 **Note**: When using callbacks, `pelorus_try_recv()` and `pelorus_recv()` will
 not receive data. Callbacks and polling/blocking APIs are mutually exclusive.
 .
 ## Examples
 .
 See the `examples/` directory for complete usage examples:
 .
 - `async_callback.c` - Callback-based asynchronous example
 - `sync_blocking.c` - Synchronous blocking example
 - `sync_blocking_pcl_eigen.cpp` - C++ wrapper example with optional PCL/Eigen
 support
 - `eigen_example.cpp` - Eigen conversion example
 .
 Build and run examples:
 ```bash
 cd examples
 make
 ./async_callback
 ./sync_blocking
 ```
 .
 ### Raw point helpers
 .
 If you already have raw point buffers (similar to the Python numpy input), you
 can avoid
 building a full `pelorus_pointcloud2` and instead call one of:
 .
 - `pelorus_add_points_xyzt_f32(...)` for Nx4 `[x,y,z,t]` (`t` is ns offset,
 cast to `uint32_t`)
 - `pelorus_add_points_xyzit_f32(...)` for Nx5 `[x,y,z,intensity,t]`
 .
 All of these are internally converted into a PointCloud2 with
 `x,y,z,intensity,t` fields.
 .
 ## Threading Model
 .
 The API is designed to be simple from the caller's perspective:
 - `pelorus_add_imu()`, `pelorus_add_odom()`, and `pelorus_add_pointcloud()`
 return immediately
 - Processing happens asynchronously in background threads
 - `pelorus_try_recv()` is non-blocking and returns immediately
 - `pelorus_recv()` blocks until data is available
 - Callbacks are invoked from a background thread
 .
 ## Memory Management
 .
 - Results from `pelorus_try_recv()` and `pelorus_recv()` must be freed with
 `pelorus_free_odometry()`
 - Callback results are automatically freed after the callback returns
 - The `PelorusHandle` must be destroyed with `pelorus_destroy()`
 .
 ## Timestamp semantics
 .
 Pelorus uses two timestamp levels:
 .
 1. **IMU/Odom timestamps** (`timestamp_sec` + `timestamp_nanosec`)
     - Absolute timestamps (typically epoch / ROS time) used for
 synchronization.
 .
 2. **Lidar timestamps**
     - `pelorus_pointcloud2.header_timestamp_sec/nanosec`: absolute reference
 time for the scan.
     - Per-point field `t` (if provided): `uint32` **nanoseconds offset**,
 **non-negative** and **forward in time** relative to the scan header timestamp.
 .
 The effective point time is:
 .
 `point_time_abs = header_time_abs + (t * 1e-9)`
 .
 ### Pelorus expects a simplified, single-row (unstructured) little-endian
 `PointCloud2`.
 .
 Interpretation of per-point timing depends on the parsing mode:
 .
 - When Pelorus uses *device-specific* parsing (the default), field meanings —
 including any `t` field — are interpreted according to the vendor/device
 configuration and may vary.
 - When using the generic parsing path (`device_specific_pointcloud2 == false`),
 Pelorus expects `t` to be a **non-negative** (`uint32`) nanoseconds offset
 relative to the scan header timestamp.
 .
 For generic point input, prefer `pelorus_add_points_xyzt_f32()` or
 `pelorus_add_points_xyzit_f32()`.
 .
 ## License
 .
 Dual-licensed under MIT and Apache 2.0, consistent with the Pelorus project.

Package: marina
Architecture: amd64
Version: 0.5.4-1
Priority: optional
Section: science
Installed-Size: 20862
Depends: libc6 (>= 2.39)
Filename: pool/main/resolute/marina_0.5.4-1_amd64.deb
Size: 5628004
MD5sum: 1db1553f15b307cdb858f14f2c879212
SHA1: 0d063a31fb775cf1a112aac5f82c5405bf0316df
SHA256: bc9b3481a976f81e8532cc5d1dba1de1694ce73fc9f963bfb734532446197517
SHA512: 605fd620a8f91ab5ba3a390753ab4430e7b3bd8c62687bdbe83f2e0f03b3efdb09cbc1ef4aca41fa4d0b57ed5ca7e9d2c9ce7dfc528671ca42c32252e277e27e
Homepage: https://marina.steado.tech
Description: A dataset manager for robotics to organize, share, and discover datasets and
 metadata across storage backends.
 <img src="assets/marina.svg" alt="Marina logo" width="160" align="right">
 .
 # marina
 .
 **Marina** is a dataset manager for robotics built to organize, share, and
 discover bags and datasets across teams and storage backends.
 .
 See the [Web Documentation](https://marina.steado.tech/docs/) or `ssh
 marina@steado.tech` for more detailed information and setup.
 .
 ## ROS 2 CLI
 .
 While Marina's dynamic autocomplete for modern shells is designed to work as a
 standalone tool, we provide a ROS 2 CLI mapping to ensure a more familiar user
 experience.
 .
 ```bash
 # Pull a dataset from any configured registry...
 ros2 bag pull dlg_feldtage_24:cut
 .
 # and pass it straight to playback.
 ros2 bag play $(ros2 bag resolve dlg_feldtage_24:cut)
 .
 # See available bags from configured remotes...
 ros2 bag datasets --remote
 # or use the alias.
 ros2 bag ds --remote
 ```
 .
 Datasets are pulled from remote registries. You can easily create your own
 using SSH, Google Drive, HTTP or any filesystem folder.
 .
 ```bash
 # Record to local cache...
 ros2 bag record --all -o $(ros2 bag import my_recording:session1)
 .
 # and push to your registry.
 ros2 bag push my-run:v1 /path/to/bag/ --registry team_ssh
 ```
 .
 ## Standalone
 .
 Marina supports Linux and MacOS environments without additional requirements.
 Just [install](https://marina.steado.tech/docs/installation/packages.html) and
 run.
 .
 ```bash
 # Explore datasets and registries interactively.
 marina
 .
 # Pull a dataset from any configured registry.
 marina pull dlg_feldtage_24:cut
 .
 # Get and use the data from the local cache.
 cat $(marina resolve dlg_feldtage_24:cut)/metadata.yaml
 .
 # See available bags from configured remotes...
 marina list --remote
 # or use the alias.
 marina ls --remote
 .
 # Export the most verbose remote catalog JSON for the static explorer.
 marina list --remote --format json > web/catalog.json
 .
 # Push any folder or bags to your registry.
 marina bag push my-run:v1 /path/to/dataset/ --registry team_ssh
 ```
 .
 ---
 .
 ### License
 .
 <sup>
 Licensed under either of <a
 href="https://codeberg.org/stelzo/marina/src/branch/main/LICENSE-APACHE">Apache
 License, Version
 2.0</a> or <a
 href="https://codeberg.org/stelzo/marina/src/branch/main/LICENSE-MIT">MIT
 license</a> at your option.
 </sup>
 .
 <br>
 .
 <sub>
 Unless you explicitly state otherwise, any contribution intentionally submitted
 for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
 be dual licensed as above, without any additional terms or conditions.
 </sub>

Package: pelorus-vulkan
Architecture: amd64
Version: 0.0.9-1
Priority: optional
Section: science
Installed-Size: 46913
Depends: libvulkan1, minot (<< 0.12.0~), minot (>= 0.11.0)
Filename: pool/main/resolute/pelorus-vulkan_0.0.9-1_amd64.deb
Size: 11469556
MD5sum: 3a58a2d8cd0fbed79d0cf6ea72cfb8bd
SHA1: d7d37df595618821b229a3cb4767d24a5948fc74
SHA256: 71b9d778e3d5ad87437f6798e0c9988184138da63586e99e7d9e6b184953fc9a
SHA512: b2aec07b6c1df76faba32c5150df3e5d610be68e9874fd348fda1b0535d4f4bcaa175c660b4f72938ef889e3571a894c63eb01b8d703bde27fa73660258b42e4
Homepage: https://codeberg.org/stelzo/pelorus
Description: Resource-aware Lidar-Inertial Odometry.

Package: pelorus
Architecture: amd64
Version: 0.0.9-1
Priority: optional
Section: science
Installed-Size: 39887
Depends: dedrunk, marina, minot (<< 0.15.0~), minot (>= 0.14.0)
Filename: pool/main/resolute/pelorus_0.0.9-1_amd64.deb
Size: 9493468
MD5sum: f2cbf848cc232be44e37de7e8e9d9567
SHA1: f316577bdd83597565ef7423a0dbe30007cfde92
SHA256: e366fe702ee6a519aaf120aca84ae9deafa7d3fda1792edf6129391c23b4b339
SHA512: 94485047aa39373009775f0b4cb22a8cf464796167cced61a8979d20d20ab7fc92c6e1173f07c344cbf87986fd54844890ebb097f7c3c6a5711c2097fd03ba4a
Homepage: https://codeberg.org/stelzo/pelorus
Description: Resource-aware Lidar-Inertial Odometry.

Package: ratslang
Architecture: amd64
Version: 0.5.0-1
Priority: optional
Section: devel
Installed-Size: 10911
Depends: libc6 (>= 2.34)
Filename: pool/main/resolute/ratslang_0.5.0-1_amd64.deb
Size: 3079104
MD5sum: 99365ffb3c0f8b47c3b839e9d06283e1
SHA1: 5186df7d2d7df9461649e9332b30c5e8d8f19786
SHA256: c843092fb61735c594ae471cf4c0554a54b40cb880c222dec1c956d010e6ea68
SHA512: e9877975b11f4b90ffb91d29e30541e3404e9ae3213d23b45a41296c2b7f0d7c8b0f263b2d435a5e3b648df05f54b0e5dc2bc341fc24b633ec1dd37328839fac
Homepage: https://stelzo.codeberg.page/ratslang/tools/cli/
Description: Check, format, explain and migrate Ratslang configuration files.
 The ratslang command checks, formats, explains and migrates Ratslang
 configuration files. ratslang-lsp is the language server for editors.

Package: ros-lyrical-marina
Architecture: amd64
Version: 0.5.4-0resolute
Priority: optional
Section: misc
Maintainer: "stelzo" <stelzo@steado.de>
Installed-Size: 43133
Depends: libc6 (>= 2.39), libgcc-s1 (>= 4.2), libclang-dev, ros-lyrical-ros2bag, ros-lyrical-ros2cli
Filename: pool/main/resolute/ros-lyrical-marina_0.5.4-0resolute_amd64.deb
Size: 12834550
MD5sum: 396bbe8831ccb3df9e470cd12ce1aede
SHA1: d802926dd801c5b7c87e6105f70bec07a45160c5
SHA256: 5b9bb4139090f9abeff83a67e5dce01d92091f0a7666bfa00413d38f8c8aad1f
SHA512: 33f737609fdad5a322f7ad2cd9b0b67f9c74320f94dc909377cd209c3e103aebac991302a18b07810a716cdfcf3823ec7a226704991a47699f1a7f85c4173ca0
Homepage: https://index.ros.org/p/marina/#lyrical
Description: A dataset manager for robotics to organize, share, and discover datasets and metadata across storage backends.

Package: ros-lyrical-minot
Architecture: amd64
Version: 0.14.1-0resolute
Priority: optional
Section: misc
Maintainer: "stelzo" <stelzo@steado.de>
Installed-Size: 28623
Depends: libc6 (>= 2.39), libgcc-s1 (>= 4.2), ros-lyrical-builtin-interfaces, ros-lyrical-geometry-msgs, ros-lyrical-nav-msgs, ros-lyrical-rcl, ros-lyrical-rcl-action, ros-lyrical-rosgraph-msgs, ros-lyrical-sensor-msgs, ros-lyrical-std-msgs, ros-lyrical-tf2-msgs
Filename: pool/main/resolute/ros-lyrical-minot_0.14.1-0resolute_amd64.deb
Size: 8041234
MD5sum: 5052fb27073106be0b3b27bec8dcb37b
SHA1: 385a0da6146c1683bd8376afd4df729435c1e197
SHA256: 57b72bbc16b6e41dd30697c788a545dcf4b3020a94070d3a1b4a69917ce6b28e
SHA512: 95b950cf5e615d5e4289c2b94238011b56e2a4ce927ab79d7c58b9a6fb0e78c00f62b803415bf195ec62061ad86ac0e778c783e6b0f60821ff08a01c95557f76
Homepage: https://index.ros.org/p/minot/#lyrical
Description: A versatile toolset for debugging and verifying stateful robot perception software.

Package: ros-lyrical-pelorus-cuda13-4
Architecture: amd64
Version: 0.0.9-0resolute
Priority: optional
Section: misc
Maintainer: "stelzo" <stelzo@steado.de>
Installed-Size: 61082
Depends: ros-lyrical-launch, ros-lyrical-launch-ros, ros-lyrical-nav-msgs, ros-lyrical-rcl, ros-lyrical-rviz2, ros-lyrical-sensor-msgs, ros-lyrical-tf2-msgs, ros-lyrical-visualization-msgs
Filename: pool/main/resolute/ros-lyrical-pelorus-cuda13-4_0.0.9-0resolute_amd64.deb
Size: 18699700
MD5sum: 9067cb68aa8aef897ee166cd8b1ce4a3
SHA1: abc15939537e0f3854592b453317b1a33a160222
SHA256: c5e07e71e3667a72354fde5b7ce5b7cbb12a9f078e1f7ec34f9965c5450a0323
SHA512: 1db065cd3870e3b1cdded202ba1f4baf3e7a1f5ca7818783a631f899bb78667a43ddf1c7faf0bdf7a020dee5a7098f09937417e77ed0d3fe302056425116c8ed
Homepage: https://index.ros.org/p/pelorus-cuda13-4/#lyrical
Description: Pelorus ROS2

Package: ros-lyrical-pelorus-vulkan
Architecture: amd64
Version: 0.0.9-0resolute
Priority: optional
Section: misc
Maintainer: "stelzo" <stelzo@steado.de>
Installed-Size: 66978
Depends: libc6 (>= 2.43), libgcc-s1 (>= 4.2), libstdc++6 (>= 11), libvulkan1 (>= 1.2.131.2), ros-lyrical-launch, ros-lyrical-launch-ros, ros-lyrical-nav-msgs, ros-lyrical-rcl, ros-lyrical-rviz2, ros-lyrical-sensor-msgs, ros-lyrical-tf2-msgs, ros-lyrical-visualization-msgs
Filename: pool/main/resolute/ros-lyrical-pelorus-vulkan_0.0.9-0resolute_amd64.deb
Size: 18672050
MD5sum: 8dc5db028096c82757a73462d532b055
SHA1: e5c2ed395879f948247d5e288dbffea0c97c1c43
SHA256: 8ca295b6855b7ffc6c817b3a4ffbf9a3ae4504261b860bd7e02cbc8c6bb8c860
SHA512: 8336804b2f26a5e9cf58456013049fb5768717beef81208ad45f180aa8184b891f7055c1340e7726f71145c0d2433c28a3e91399fccaa6cd79d315a76740ae74
Homepage: https://index.ros.org/p/pelorus-vulkan/#lyrical
Description: Pelorus ROS2

Package: ros-lyrical-pelorus
Architecture: amd64
Version: 0.0.9-0resolute
Priority: optional
Section: misc
Maintainer: "stelzo" <stelzo@steado.de>
Installed-Size: 52836
Depends: libc6 (>= 2.43), libgcc-s1 (>= 4.2), ros-lyrical-launch, ros-lyrical-launch-ros, ros-lyrical-nav-msgs, ros-lyrical-rcl, ros-lyrical-rviz2, ros-lyrical-sensor-msgs, ros-lyrical-tf2-msgs, ros-lyrical-visualization-msgs
Filename: pool/main/resolute/ros-lyrical-pelorus_0.0.9-0resolute_amd64.deb
Size: 14367730
MD5sum: e268ba7b5adae9b7b487d39789f7abf2
SHA1: e877770e39143010945b85b315454c65aae4adc8
SHA256: d993dcc5b217c4b6bf179cebf6246e5329395584c3c6203075f750e8cbaa3127
SHA512: a1ab2842a25e90fdea73a42bf61a320a83e6cd09ebc32e62c50ca5ba5f85de9b2533a48b5a2778b960bbfd14fa80dec0d5c6a3851e1c1a36e082363b80d3a57a
Homepage: https://index.ros.org/p/pelorus/#lyrical
Description: Pelorus ROS2

Package: sounding
Architecture: amd64
Version: 0.4.1-1
Priority: optional
Section: science
Maintainer: stelzo <stelzo@steado.de>
Installed-Size: 1844
Depends: libc6 (>= 2.39)
Filename: pool/main/resolute/sounding_0.4.1-1_amd64.deb
Size: 644680
MD5sum: b3ca97fe2792b802e2593d1f4af0244a
SHA1: e8f45b6a8b70f2334248dee6d6fd265178c65423
SHA256: c973960843f679acaa28e069b9fad3a4986581ce1a49ab9224c61226f05395bf
SHA512: 2e51a8f8498b49f90d8491a04e93627b6ff9adcf311c15e8c8ad0e1e8541eb50fba798ec4fffb560755bab274587c88459e501d639278eb6ea995b304f91da0a
Homepage: https://codeberg.org/stelzo/sounding
Description: A SLAM evaluation tool.
 # sounding
 .
 CLI tool for:
 - trajectory evaluation (`trajectory`)
 - process/system profiling (`probe`)
 .
 ## Install
 .
 We need the fontconfig library. For Ubuntu, you can `sudo apt install
 libfontconfig-dev`.
 .
 ### From crates.io (recommended)
 .
 ```bash
 cargo install sounding
 ```
 .
 Or with rerun for visualisation:
 .
 ```bash
 cargo install sounding --features rerun
 ```
 .
 ### Fast binary install with cargo-binstall
 .
 ```bash
 cargo binstall sounding
 ```
 .
 ### Linux/macOS release archives
 .
 Download the matching `tar.gz` from releases, then install:
 .
 ```bash
 tar -xzf sounding-<VERSION>-<TARGET>.tar.gz
 sudo install -m 755 sounding-<VERSION>-<TARGET>/sounding
 /usr/local/bin/sounding
 ```
 .
 ### Arch Linux (btw) (AUR)
 .
 Source build package:
 .
 ```bash
 yay -S sounding
 ```
 .
 Prebuilt binary package:
 .
 ```bash
 yay -S sounding-bin
 ```
 .
 ### Homebrew (macOS)
 .
 ```bash
 brew tap uos/sounding
 brew install sounding
 ```
 .
 ### Debian/Ubuntu packages
 .
 `.deb` packages are published in the [PPA repository](https://ppa.steado.tech).
 .
 ## Shell completions
 .
 Run `sounding completions` to detect the current shell and install completions
 for the current user. Pass a shell explicitly, such as
 `sounding completions bash`, to print a completion script for packaging or a
 system-wide installation instead.
 .
 ## Build from source
 .
 ```bash
 cargo build --release
 ```
 .
 ## Run
 .
 ```bash
 # all commands
 cargo run -- --help
 ```
 .
 ## Trajectory command
 .
 ```bash
 cargo run -- trajectory <ESTIMATED_TUM> <REFERENCE_TUM>
 ```
 .
 Common options:
 .
 ```bash
 --json
 --export-summary-json [JSON_FILE]
 --export-plots-dir [DIR]
 --reference-body-extrinsic <QX,QY,QZ,QW,X,Y,Z>
 --delta <SECONDS>  # RPE interval in seconds (default: 1)
 --rerun
 ```
 .
 `--reference-body-extrinsic` converts the reference trajectory into another
 body frame before
 evaluation. Pass the transform from the desired comparison frame into the
 current reference frame;
 it is right-multiplied onto each reference pose. For example, if the reference
 poses are IMU poses
 and the estimate is in the LiDAR frame, pass the LiDAR-to-IMU extrinsic.
 .
 ## Probe command
 .
 ```bash
 cargo run -- probe <TARGET_PID_OR_NAME> --output-dir <DIR>
 ```
 .
 Common options:
 .
 ```bash
 --json
 --output-dir <DIR>
 --interval <SECONDS>
 --strict
 --rerun
 ```

