Skip to content

Commit 579cf4d

Browse files
authored
Merge pull request #1 from Matdata-eu/002-train-path-calculation
002 train path calculation
2 parents ec8eff6 + 9932b70 commit 579cf4d

File tree

238 files changed

+2549481
-3385
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

238 files changed

+2549481
-3385
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# tp-lib Development Guidelines
2+
3+
Auto-generated from all feature plans. Last updated: 2026-01-09
4+
5+
## Active Technologies
6+
7+
- Rust 1.75+ (edition 2021) (002-train-path-calculation)
8+
9+
## Project Structure
10+
11+
```text
12+
src/
13+
tests/
14+
```
15+
16+
## Commands
17+
18+
cargo test; cargo clippy
19+
20+
## Code Style
21+
22+
Rust 1.75+ (edition 2021): Follow standard conventions
23+
24+
## Recent Changes
25+
26+
- 002-train-path-calculation: Added Rust 1.75+ (edition 2021)
27+
28+
<!-- MANUAL ADDITIONS START -->
29+
<!-- MANUAL ADDITIONS END -->

.github/workflows/ci.yml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,21 @@ jobs:
6868
uses: dtolnay/rust-toolchain@stable
6969

7070
- name: Install maturin
71-
run: pip install maturin pytest
72-
71+
run: pip install maturin
72+
73+
- name: Create virtual environment
74+
run: python -m venv .venv
75+
7376
- name: Build Python package
7477
run: |
7578
cd tp-py
7679
maturin develop
77-
80+
81+
- name: Install test dependencies
82+
run: .venv/bin/pip install pytest
83+
7884
- name: Run Python tests
79-
run: |
80-
cd tp-py
81-
pytest python/tests/
85+
run: .venv/bin/pytest tp-py/python/tests/
8286

8387
lint:
8488
name: Linting

.github/workflows/coverage.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Coverage
2+
3+
on:
4+
push:
5+
branches: [main, develop]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
coverage:
11+
name: Generate Coverage Report
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Install Rust toolchain
17+
uses: actions-rust-lang/setup-rust-toolchain@v1
18+
with:
19+
toolchain: stable
20+
components: llvm-tools-preview
21+
22+
- name: Install cargo-llvm-cov
23+
uses: taiki-e/install-action@v2
24+
with:
25+
tool: cargo-llvm-cov
26+
27+
- name: Generate coverage report
28+
run: |
29+
cargo llvm-cov --lib -p tp-lib-core \
30+
--lcov --output-path lcov.info
31+
32+
- name: Upload coverage to Codecov
33+
uses: codecov/codecov-action@v4
34+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
35+
with:
36+
files: lcov.info
37+
token: ${{ secrets.CODECOV_TOKEN }}
38+
fail_ci_if_error: true
39+
flags: unittests
40+
name: tp-lib-core-coverage
41+
42+
- name: Generate HTML report (for artifacts)
43+
if: always()
44+
run: |
45+
cargo llvm-cov --lib -p tp-lib-core \
46+
--html --output-dir coverage-report
47+
48+
- name: Upload HTML report as artifact
49+
if: always()
50+
uses: actions/upload-artifact@v4
51+
with:
52+
name: coverage-report
53+
path: coverage-report/
54+
retention-days: 30

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,7 @@ htmlcov/
6464
# OS
6565
Thumbs.db
6666

67-
.cargo
67+
.cargo
68+
test-data/best_position_geom_*.csv
69+
test-data/TP-Lib_attachments.zip
70+
test-data/TP-Lib.qgs

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"rust-analyzer.debug.engine": "ms-vscode.cpptools",
23
"chat.promptFilesRecommendations": {
34
"speckit.constitution": true,
45
"speckit.specify": true,

CONTRIBUTING.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ tp-lib/
272272
│ ├── src/
273273
│ │ ├── models/ # Data models
274274
│ │ ├── projection/ # Projection algorithms
275+
│ │ ├── path/ # Train path calculation
275276
│ │ ├── io/ # Input/output parsers
276277
│ │ ├── crs/ # Coordinate transformations
277278
│ │ └── temporal/ # Timezone utilities
@@ -282,6 +283,87 @@ tp-lib/
282283
└── python/tests/ # Python tests
283284
```
284285

286+
## Train Path Calculation Development
287+
288+
When working on path calculation features (`tp-core/src/path/`):
289+
290+
### Architecture Overview
291+
292+
The path calculation module consists of several submodules:
293+
294+
- **`candidate.rs`**: Find candidate netelements for each GNSS position
295+
- **`probability.rs`**: Calculate HMM-related probabilities (e.g., emission/transition) using distance, heading, and network context
296+
- **`viterbi.rs`**: Run the HMM/Viterbi algorithm to compute the most likely train path from the candidate sequences
297+
- **`graph.rs`**: Network topology graph operations
298+
- **`spacing.rs`**: GNSS resampling for consistent spacing
299+
300+
### Key Functions
301+
302+
```rust
303+
// Main entry point
304+
calculate_train_path(gnss_positions, netelements, netrelations, config) -> PathResult
305+
306+
// Project onto pre-calculated path
307+
project_onto_path(gnss_positions, path, netelements, config) -> Vec<ProjectedPosition>
308+
```
309+
310+
### Algorithm Flow
311+
312+
1. **Candidate Selection** (`find_candidate_netelements`)
313+
- Uses R-tree spatial index for O(log n) lookup
314+
- Filters by `cutoff_distance` and `max_candidates`
315+
316+
2. **Probability Calculation** (`calculate_combined_probability`)
317+
- Distance probability: `exp(-distance / distance_scale)`
318+
- Heading probability: `exp(-heading_diff / heading_scale)`
319+
- Combined: `distance_prob * heading_prob`
320+
321+
3. **Path Construction** (`construct_forward_path`, `construct_backward_path`)
322+
- Traverses network using netrelations (topology)
323+
- Builds path in both directions for validation
324+
325+
4. **Path Selection** (`select_best_path`)
326+
- Validates bidirectional agreement
327+
- Returns highest probability path
328+
329+
### Testing Path Calculation
330+
331+
```bash
332+
# Run path calculation tests
333+
cargo test --workspace path
334+
335+
# Run integration tests
336+
cargo test --test tests path_calculation
337+
338+
# Run benchmarks
339+
cargo bench --bench projection_bench
340+
cargo bench --bench naive_baseline_bench
341+
```
342+
343+
### Creating coverage report
344+
345+
```bash
346+
cargo llvm-cov --lib -p tp-lib-core --html --output-dir target/coverage
347+
```
348+
349+
### Performance Targets
350+
351+
- **SC-001**: 1000 positions × 50 netelements in <10 seconds
352+
- **Current**: ~900μs (11,000× faster than target)
353+
- **Memory**: <500MB for 10k+ positions
354+
355+
### Configuration Parameters
356+
357+
| Parameter | Default | Purpose |
358+
|-----------|---------|---------|
359+
| `distance_scale` | 10.0 | Distance decay rate (meters) |
360+
| `heading_scale` | 2.0 | Heading decay rate (degrees) |
361+
| `cutoff_distance` | 500.0 | Max search distance (meters) |
362+
| `heading_cutoff` | 10.0 | Max heading difference (degrees) |
363+
| `probability_threshold` | 0.02 | Min probability for inclusion |
364+
| `max_candidates` | 3 | Max candidates per position |
365+
| `resampling_distance` | None | Optional GNSS resampling |
366+
285367
## Constitution Principles
286368

287369
TP-Lib follows strict architectural principles (see Constitution v1.1.0):

Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ crs-definitions = "0.3.1"
2626
rstar = "0.12"
2727
geojson = "0.24"
2828

29+
# Graph algorithms
30+
petgraph = "0.6"
31+
2932
# Data processing
3033
arrow = "53.0"
3134
polars = "0.44"
@@ -52,3 +55,8 @@ pyo3 = { version = "0.21", features = ["extension-module"] }
5255
# Testing
5356
criterion = { version = "0.5", features = ["html_reports"] }
5457
quickcheck = "1.0"
58+
59+
[profile.dev]
60+
debug = 2
61+
opt-level = 0
62+
split-debuginfo = "unpacked"

0 commit comments

Comments
 (0)