Metadata-Version: 2.4
Name: gnssroute
Version: 0.1.1
Summary: Zero-dependency GNSS drone-route toolkit for DJI survey files (.OBS/.NAV/.MRK): SPP positioning, waypoint optimisation, and Cesium export.
Author-email: jia <jiapengfei@mail.com>
Maintainer-email: jia <jiapengfei@mail.com>
License: MIT
Project-URL: Homepage, https://github.com/Jia-SH
Project-URL: Repository, https://github.com/Jia-SH/dji-gnssroute
Project-URL: Issues, https://github.com/Jia-SH/dji-gnssroute/issues
Keywords: gnss,gps,spp,drone,uav,dji,rtk,route,trajectory,cesium,czml,geojson,rinex,gis
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: GIS
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: yaml
Requires-Dist: pyyaml; extra == "yaml"
Provides-Extra: toml-write
Requires-Dist: tomli_w; extra == "toml-write"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=4.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Dynamic: license-file

# gnssroute · 无人机航线 GNSS 工具库

[![PyPI](https://img.shields.io/pypi/v/gnssroute.svg)](https://pypi.org/project/gnssroute/)
[![Python](https://img.shields.io/badge/python-%3E%3D3.11-blue.svg)](https://www.python.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![English](https://img.shields.io/badge/🇬🇧%20English-README_EN.md-lightgrey.svg)](README_EN.md)

一个**零第三方依赖**（仅用 Python 标准库）的轻量级库，专门适配**大疆（DJI）无人机航测文件**（`.OBS` / `.NAV` / `.MRK`），用于从 GNSS 观测数据反演无人机航线、按精度重采样、用真值航点优化，并导出 Cesium 可视化格式。

> 🎯 **典型场景**：用大疆自动航线跑完航测后，本工具可直接读取其生成的 `OBS / NAV / MRK` 三类文件，
> 快速得到可在 Cesium 中回放的高精度航线动画——无需额外转换，开箱即用。

---

## ✨ 核心特性

1. **大疆文件直读** — 专门适配 DJI 自动航线生成的 RINEX 3.05 `.OBS` / `.NAV` 文件和 `.MRK` RTK 真值航点，无需格式转换；
2. **单点定位 SPP** — 从 RINEX 观测文件（`.obs` + `.nav`）或简易 CSV 反演无人机实时航线；
3. **按精度重采样** — 按固定时间间隔（1 s / 0.2 s …）或固定距离（≤1 m / 5 m …）生成指定精度的航线；
4. **航点优化** — 用 `.mrk` 真值航点修正 SPP 实时航线（MRK 视为真解），并可再次下采样；
5. **Cesium 可视化** — 输出 CZML / GeoJSON 航线文件，直接在 Cesium 中加载（可在 [Cesium Sandcastle](https://sandcastle.cesium.com/) 中直接预览）；
6. **配置文件驱动** — 支持 TOML / JSON / INI 配置，Python 可读取并按配置运行整条流水线。

---

## 📦 安装

```bash
pip install gnssroute
```

库本身**无需任何第三方包**（仅 Python 标准库）。仅当你想用 TOML 写配置时才需要 `tomli_w`，想用 YAML 配置时需要 `pyyaml`：

```bash
pip install gnssroute[toml-write,yaml]
```

从源码安装（开发模式）：

```bash
git clone https://github.com/Jia-SH/dji-gnssroute.git
cd gnssroute
pip install -e .
```

---

## 🚀 快速开始

### 完整流水线（配置文件驱动）

```python
from gnssroute import pipeline

result = pipeline.process("config.toml")
print(result["final"])        # 优化后的 Trajectory
print(result["written"])      # 输出的 CZML / GeoJSON / CSV 路径
```

### 分步调用（更灵活）

```python
from gnssroute.io import csv_io, rinex_nav, rinex_obs
from gnssroute.positioning import spp, satellite
from gnssroute.route import resample, optimize
from gnssroute.export import cesium

# ① SPP：从观测反演实时航线
epochs = csv_io.read_obs_csv("sample_data/obs.csv")
provider = satellite.SyntheticConstellation()     # 真实场景用 EphemerisProvider(read_nav(...))
traj = spp.solve_spp(epochs, provider, el_mask_deg=10.0, name="spp_realtime")

# ② 按精度重采样（距离 ≤ 1 m）
traj_1m = resample.resample_by_distance(traj, max_dist=1.0)

# ③ 用 MRK 真值航点优化
waypoints = csv_io.read_waypoints_csv("sample_data/mrk.csv")
traj_opt = optimize.optimize_with_waypoints(traj_1m, waypoints)

# ④ 导出 Cesium 可加载格式
cesium.write_czml(traj_opt, "route.czml")
cesium.write_geojson(traj_opt, "route.geojson")
```

### 命令行走通示例

```bash
# 生成自包含示例数据
python examples/generate_sample_data.py sample_data

# 跑完整 ①~④ 并打印误差对比
python examples/run_full_pipeline.py

# 配置驱动的 CLI 调用
python -m gnssroute.cli examples/output/demo_config.toml --print-points
```

**示例输出**（端到端仿真验证）：

```
[1] SPP recovered 300 points
[2] resampled to 450 points (<=1 m spacing)
[3] optimised with 15 MRK waypoints
[4] wrote Cesium/GeoJSON/CSV to examples/output
[5] RMS error vs truth: SPP=5.58 m  ->  optimised=0.85 m
```

航点优化将 RMS 误差从 **5.58 m** 降至 **0.85 m**，精度提升约 6.6 倍。

### 🚁 真实大疆航测端到端案例

`examples/run_dji_real.py` + `examples/config_dji_real.toml` 是一条**端到端真实案例**，
直接读取大疆自动航线生成的三类文件：

| DJI 文件 | 用途 | 本库处理方式 |
|----------|------|-------------|
| `*.OBS` | RINEX 3.05 多系统观测（伪距） | `rinex_obs.read_obs()` — 已适配 DJI 的 PRN 格式 |
| `*.NAV` | RINEX 3.05 广播星历 | `rinex_nav.read_nav()` — 已适配 DJI 的单/双位 PRN 写法 |
| `*.MRK` | RTK 固定解真值航点 | `mrk.read_mrk()` — 专门解析 DJI 的制表符分隔格式 |

跑通「SPP 单点定位 → 1 Hz 重采样 → MRK 真值精化 → Cesium / GeoJSON / CSV 导出」，并打印各阶段实测精度：

```bash
python examples/run_dji_real.py path/to/your/dji_survey_folder
```

📖 完整数据说明、处理流程、精度结论与踩坑笔记见 [examples/case_study_dji_mehu_bridge.md](examples/case_study_dji_mehu_bridge.md)（基于真实的湖州梅湖大桥桥梁巡检飞行）。

---

## 🧩 核心 API

| 模块 | 关键函数 | 说明 |
|------|----------|------|
| `gnssroute.positioning.spp` | `solve_spp(epochs, provider, el_mask_deg, name)` | 对每历元做加权最小二乘 SPP，返回 `Trajectory` |
| `gnssroute.positioning.satellite` | `SatEphemeris`, `EphemerisProvider`, `SyntheticConstellation` | 卫星位置/钟差计算；真实数据走 `EphemerisProvider`，示例走 `SyntheticConstellation` |
| `gnssroute.route.resample` | `resample_by_interval(traj, sec)`, `resample_by_distance(traj, m)` | 按时间/距离重采样，保证点距不超过阈值 |
| `gnssroute.route.optimize` | `optimize_with_waypoints(traj, wps)`, `downsample(...)` | 用 MRK 残差场（ECEF 分段线性）修正航线 |
| `gnssroute.export.cesium` | `to_czml`, `to_geojson`, `write_czml`, `write_geojson` | 输出 Cesium/CesiumJS 可加载航线 |
| `gnssroute.export.writers` | `write_trajectory_csv`, `write_trajectory_json` | 通用数据写出 |
| `gnssroute.io` | `read_obs`, `read_nav`, `read_mrk`, `read_obs_csv`, `read_trajectory_csv`, `read_waypoints_csv` | RINEX / MRK / 简易 CSV 读取 |
| `gnssroute.config` | `load_config`, `save_config`, `normalize` | 配置读写与默认值合并校验 |
| `gnssroute.pipeline` | `process`, `run_steps` | 端到端流水线编排 |

---

## ⚙️ 配置文件格式（TOML）

`examples/config_demo.toml` 给出了完整示例，要点如下：

```toml
[input]
obs_path   = "sample_data/observation.obs"   # RINEX 观测
nav_path   = "sample_data/brdc.nav"          # RINEX 星历
mrk_path   = "sample_data/markers.mrk"       # 真值航点
# directory = "sample_data"                  # 也可按后缀自动发现文件

[spp]
el_mask_deg = 10.0                           # 高度角截止

[resample]
by_interval = 0.2     # 秒（与 by_distance 二选一）
# by_distance = 1.0   # 米

[optimize]
enabled = true
downsample_distance = 1.0   # 优化后再次下采样（可选）

[export]
czml    = "output/route.czml"
geojson = "output/route.geojson"
csv     = "output/route.csv"
base_datetime = "2026-04-16T03:11:00Z"
```

读取 / 写出配置：

```python
from gnssroute.config import load_config, save_config
cfg = load_config("config.toml")
cfg["resample"]["by_distance"] = 5.0
save_config(cfg, "config_new.toml")
```

---

## 🗺️ 输出与 Cesium 加载

- **`route.czml`**：CZML 文档，含 `cartographicDegrees` 采样位置 + 发光路径 `path`，
  在 CesiumJS 中直接 `Cesium.CzmlDataSource.load('route.czml')` 即可看到动画航线。
- **`route.geojson`**：`LineString` 要素，可在 Cesium / QGIS 中加载。
- **`route.csv`**：`t, lat, lon, height` 纯数据，便于二次处理。

---

## 📂 工程结构

```
gnssroute/
├── gnssroute/
│   ├── common/      # 地球模型(WGS84)、线性代数、时间、数据类型、插值
│   ├── io/          # RINEX obs/nav、MRK、简易 CSV 读写
│   ├── positioning/ # 卫星星历 + SPP 最小二乘解算
│   ├── route/       # 重采样（时间/距离）、航点优化
│   ├── export/      # Cesium(CZML/GeoJSON)、通用写出
│   ├── config/      # 配置加载/默认值/校验
│   ├── pipeline.py  # 端到端编排
│   └── cli.py       # 命令行入口
├── examples/        # 自包含示例与完整演示（含真实大疆案例）
└── tests/           # 单元测试（零依赖 runner：tests/run_tests.py）
```

---

## 🧪 测试

```bash
python tests/run_tests.py        # 零依赖运行全部用例
# 或（已安装 pytest）：
python -m pytest tests
```

测试覆盖：地球模型往返、最小二乘、重采样精度、MRK 优化误差下降、CZML/GeoJSON
结构，以及**端到端 SPP 仿真**（自生成观测 → SPP 反演 → 优化，验证误差显著下降）。

---

## 📄 许可证

本项目基于 [MIT License](LICENSE) 开源。

---

## 🙏 说明

> 本库内置的 `SyntheticConstellation` 用于无外部 RINEX 时的自包含演示与测试；
> 接入真实数据时，用 `EphemerisProvider(rinex_nav.read_nav("brdc.nav"))` 提供卫星位置即可。
