Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
f632acb
spec: specify train path calculation
MathiasVDA Jan 8, 2026
e312859
specs: created and reviewed technical plan for path calculation
MathiasVDA Jan 9, 2026
10e0450
specs: added implementation tasks
MathiasVDA Jan 9, 2026
f3ceeed
specs: consistency check fixes
MathiasVDA Jan 9, 2026
105c985
feat: implemented phase 1 and 2 of path calculation
MathiasVDA Jan 9, 2026
69f3cf6
feat: implemented phase 3
MathiasVDA Jan 9, 2026
5f48256
fix: properly load network geojson
MathiasVDA Jan 9, 2026
e5bf2b1
chore: refactored models
MathiasVDA Jan 9, 2026
ec2115d
feat: implemented next phases of path calculation
MathiasVDA Jan 9, 2026
1dbca08
ci: add code coverage calculation
MathiasVDA Jan 9, 2026
bf85be3
chore: increase code coverage with unit tests
MathiasVDA Jan 9, 2026
b3bdc43
chore: increase code coverage with more unit tests
MathiasVDA Jan 10, 2026
cf61602
chore: increase code coverage with more unit tests
MathiasVDA Jan 10, 2026
c0143b4
chore: adding benchmark tasks for path calculation
MathiasVDA Jan 10, 2026
b613e7e
chore: small refactoring
MathiasVDA Jan 10, 2026
90a5885
doc: fix clone link
MathiasVDA Mar 12, 2026
f200fce
docs: add table of content
MathiasVDA Mar 12, 2026
332f934
chore: improve debugging experience
MathiasVDA Mar 12, 2026
df58588
feat: default to UTC time when no timezone is provided in GNSS coordi…
MathiasVDA Mar 13, 2026
853ab6d
fix: simple projection didn't work with multiple netelements in close…
MathiasVDA Mar 13, 2026
63adb98
feat: rework path calculation algorithm to improve real data results
MathiasVDA Mar 14, 2026
90a58a3
chore: add test data for first real data test with documentation
MathiasVDA Mar 14, 2026
703d01f
chore: added test on L36 track B
MathiasVDA Mar 14, 2026
59a3779
chore: restructured test-data folder
MathiasVDA Mar 14, 2026
10ef372
chore: debug improvements
MathiasVDA Mar 14, 2026
bc442e1
chore: added more test datasets
MathiasVDA Mar 14, 2026
e783582
feat: add debug option to CLI so that the user gets intermediate arti…
MathiasVDA Mar 14, 2026
24be904
docs: update readme with latest cli changes
MathiasVDA Mar 14, 2026
046a070
feat: calculate heading if not available and discard candidate netele…
MathiasVDA Mar 15, 2026
ad83fe5
fix: start/end netelement coverage probability was not taking into ac…
MathiasVDA Mar 16, 2026
7b2ba11
feat: implement HMM method
MathiasVDA Mar 18, 2026
8b8c258
fix: viterbi algorithm was very susceptive to a noisy observation
MathiasVDA Mar 18, 2026
e162d65
chore: update test-data results
MathiasVDA Mar 18, 2026
a70a194
chore: added test information about 2 more files
MathiasVDA Mar 19, 2026
ea90256
feat: increase distance cutoff to let the HMM model better do its work
MathiasVDA Mar 19, 2026
d2318f2
chore: add new test case
MathiasVDA Mar 19, 2026
54bf6a1
chore: tested and described another test dataset
MathiasVDA Mar 19, 2026
14a36f0
feat: add path continuity and navigability check
MathiasVDA Mar 20, 2026
88e5daf
chore: add test dataset
MathiasVDA Mar 20, 2026
25c39e9
chore: add test dataset
MathiasVDA Mar 20, 2026
da2985c
chore: update previous datasets with new path calculations
MathiasVDA Mar 20, 2026
cf2cbdf
chore: added test dataset
MathiasVDA Mar 20, 2026
56957af
chore: remove test dataset because very similar to another (has good …
MathiasVDA Mar 20, 2026
2156c26
docs: updated debug output docs
MathiasVDA Mar 20, 2026
55136d1
feat: improve path generation to better rely on more confident netele…
MathiasVDA Mar 20, 2026
ea1cafe
chore: update previous datasets with new path algorithm
MathiasVDA Mar 20, 2026
32b1b59
chore: add another test-data set result
MathiasVDA Mar 20, 2026
715e9a3
chore: added another test dataset
MathiasVDA Mar 20, 2026
111a5d7
chore: finished processing all test-data sets
MathiasVDA Mar 20, 2026
b160dd6
fix: resolve CI failures (clippy, fmt, tests, python binding)
MathiasVDA Mar 20, 2026
9bebcbc
chore: remove redundant tests
MathiasVDA Mar 20, 2026
9696e5a
fix(ci): create virtualenv before maturin develop
MathiasVDA Mar 20, 2026
1fd9b2d
fix(tp-py): add missing README.md required by pyproject.toml
MathiasVDA Mar 20, 2026
6bbbff5
docs: address PR review feedback — consistency, grammar, and architec…
Copilot Mar 20, 2026
3ceed4d
fix(tp-py): implement CRS validation and target_crs coordinate transform
MathiasVDA Mar 20, 2026
9932b70
spec: update with latest algorithm changes
MathiasVDA Mar 20, 2026
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
29 changes: 29 additions & 0 deletions .github/agents/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# tp-lib Development Guidelines
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line 1 appears to include a BOM/zero-width character before # (often rendered as ). This can cause tooling and markdown linters to behave oddly; consider removing the BOM so the file starts with a plain #.

Suggested change
# tp-lib Development Guidelines
# tp-lib Development Guidelines

Copilot uses AI. Check for mistakes.

Auto-generated from all feature plans. Last updated: 2026-01-09

## Active Technologies

- Rust 1.75+ (edition 2021) (002-train-path-calculation)

## Project Structure

```text
src/
tests/
```

## Commands

cargo test; cargo clippy

## Code Style

Rust 1.75+ (edition 2021): Follow standard conventions

## Recent Changes

- 002-train-path-calculation: Added Rust 1.75+ (edition 2021)

<!-- MANUAL ADDITIONS START -->
<!-- MANUAL ADDITIONS END -->
16 changes: 10 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,21 @@ jobs:
uses: dtolnay/rust-toolchain@stable

- name: Install maturin
run: pip install maturin pytest

run: pip install maturin

- name: Create virtual environment
run: python -m venv .venv

- name: Build Python package
run: |
cd tp-py
maturin develop


- name: Install test dependencies
run: .venv/bin/pip install pytest

- name: Run Python tests
run: |
cd tp-py
pytest python/tests/
run: .venv/bin/pytest tp-py/python/tests/

lint:
name: Linting
Expand Down
54 changes: 54 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Coverage

on:
push:
branches: [main, develop]
pull_request:
branches: [main]

jobs:
coverage:
name: Generate Coverage Report
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
components: llvm-tools-preview

- name: Install cargo-llvm-cov
uses: taiki-e/install-action@v2
with:
tool: cargo-llvm-cov

- name: Generate coverage report
run: |
cargo llvm-cov --lib -p tp-lib-core \
--lcov --output-path lcov.info

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
with:
files: lcov.info
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
Comment on lines +32 to +38
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On pull_request events from forks, secrets.CODECOV_TOKEN is typically unavailable, and with fail_ci_if_error: true this job will fail the workflow. Consider gating the upload step (or setting fail_ci_if_error conditionally) when the token is missing, or use tokenless uploads if the repo configuration supports it.

Copilot uses AI. Check for mistakes.
flags: unittests
name: tp-lib-core-coverage

- name: Generate HTML report (for artifacts)
if: always()
run: |
cargo llvm-cov --lib -p tp-lib-core \
--html --output-dir coverage-report

- name: Upload HTML report as artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage-report/
retention-days: 30
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,7 @@ htmlcov/
# OS
Thumbs.db

.cargo
.cargo
test-data/best_position_geom_*.csv
test-data/TP-Lib_attachments.zip
test-data/TP-Lib.qgs
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"rust-analyzer.debug.engine": "ms-vscode.cpptools",
"chat.promptFilesRecommendations": {
"speckit.constitution": true,
"speckit.specify": true,
Expand Down
82 changes: 82 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ tp-lib/
│ ├── src/
│ │ ├── models/ # Data models
│ │ ├── projection/ # Projection algorithms
│ │ ├── path/ # Train path calculation
│ │ ├── io/ # Input/output parsers
│ │ ├── crs/ # Coordinate transformations
│ │ └── temporal/ # Timezone utilities
Expand All @@ -282,6 +283,87 @@ tp-lib/
└── python/tests/ # Python tests
```

## Train Path Calculation Development

When working on path calculation features (`tp-core/src/path/`):

### Architecture Overview

The path calculation module consists of several submodules:

- **`candidate.rs`**: Find candidate netelements for each GNSS position
- **`probability.rs`**: Calculate HMM-related probabilities (e.g., emission/transition) using distance, heading, and network context
- **`viterbi.rs`**: Run the HMM/Viterbi algorithm to compute the most likely train path from the candidate sequences
- **`graph.rs`**: Network topology graph operations
- **`spacing.rs`**: GNSS resampling for consistent spacing

### Key Functions

```rust
// Main entry point
calculate_train_path(gnss_positions, netelements, netrelations, config) -> PathResult

// Project onto pre-calculated path
project_onto_path(gnss_positions, path, netelements, config) -> Vec<ProjectedPosition>
```

### Algorithm Flow

1. **Candidate Selection** (`find_candidate_netelements`)
- Uses R-tree spatial index for O(log n) lookup
- Filters by `cutoff_distance` and `max_candidates`

2. **Probability Calculation** (`calculate_combined_probability`)
- Distance probability: `exp(-distance / distance_scale)`
- Heading probability: `exp(-heading_diff / heading_scale)`
- Combined: `distance_prob * heading_prob`

3. **Path Construction** (`construct_forward_path`, `construct_backward_path`)
- Traverses network using netrelations (topology)
- Builds path in both directions for validation

4. **Path Selection** (`select_best_path`)
- Validates bidirectional agreement
- Returns highest probability path

### Testing Path Calculation

```bash
# Run path calculation tests
cargo test --workspace path

# Run integration tests
cargo test --test tests path_calculation

# Run benchmarks
cargo bench --bench projection_bench
cargo bench --bench naive_baseline_bench
```

### Creating coverage report

```bash
cargo llvm-cov --lib -p tp-lib-core --html --output-dir target/coverage
```

### Performance Targets

- **SC-001**: 1000 positions × 50 netelements in <10 seconds
- **Current**: ~900μs (11,000× faster than target)
- **Memory**: <500MB for 10k+ positions

### Configuration Parameters

| Parameter | Default | Purpose |
|-----------|---------|---------|
| `distance_scale` | 10.0 | Distance decay rate (meters) |
| `heading_scale` | 2.0 | Heading decay rate (degrees) |
| `cutoff_distance` | 500.0 | Max search distance (meters) |
| `heading_cutoff` | 10.0 | Max heading difference (degrees) |
| `probability_threshold` | 0.02 | Min probability for inclusion |
| `max_candidates` | 3 | Max candidates per position |
| `resampling_distance` | None | Optional GNSS resampling |

## Constitution Principles

TP-Lib follows strict architectural principles (see Constitution v1.1.0):
Expand Down
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ crs-definitions = "0.3.1"
rstar = "0.12"
geojson = "0.24"

# Graph algorithms
petgraph = "0.6"

# Data processing
arrow = "53.0"
polars = "0.44"
Expand All @@ -52,3 +55,8 @@ pyo3 = { version = "0.21", features = ["extension-module"] }
# Testing
criterion = { version = "0.5", features = ["html_reports"] }
quickcheck = "1.0"

[profile.dev]
debug = 2
opt-level = 0
split-debuginfo = "unpacked"
Loading
Loading