orbit-formats 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- orbit_formats-0.1.0/.gitattributes +5 -0
- orbit_formats-0.1.0/.github/CODEOWNERS +2 -0
- orbit_formats-0.1.0/.github/ISSUE_TEMPLATE/bug.yml +59 -0
- orbit_formats-0.1.0/.github/ISSUE_TEMPLATE/chore.yml +25 -0
- orbit_formats-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- orbit_formats-0.1.0/.github/ISSUE_TEMPLATE/feature.yml +41 -0
- orbit_formats-0.1.0/.github/pull_request_template.md +34 -0
- orbit_formats-0.1.0/.github/workflows/ci.yml +126 -0
- orbit_formats-0.1.0/.github/workflows/docs.yml +57 -0
- orbit_formats-0.1.0/.github/workflows/release.yml +74 -0
- orbit_formats-0.1.0/.gitignore +40 -0
- orbit_formats-0.1.0/.python-version +1 -0
- orbit_formats-0.1.0/CHANGELOG.md +47 -0
- orbit_formats-0.1.0/CITATION.cff +26 -0
- orbit_formats-0.1.0/CONTRIBUTING.md +57 -0
- orbit_formats-0.1.0/LICENSE +21 -0
- orbit_formats-0.1.0/PKG-INFO +140 -0
- orbit_formats-0.1.0/README.md +104 -0
- orbit_formats-0.1.0/docs/api.md +3 -0
- orbit_formats-0.1.0/docs/canonical-representation.md +112 -0
- orbit_formats-0.1.0/docs/cli.md +40 -0
- orbit_formats-0.1.0/docs/conversion-matrix.md +63 -0
- orbit_formats-0.1.0/docs/formats.md +81 -0
- orbit_formats-0.1.0/docs/getting-started.md +114 -0
- orbit_formats-0.1.0/docs/index.md +58 -0
- orbit_formats-0.1.0/docs/lossy-conversions.md +72 -0
- orbit_formats-0.1.0/mkdocs.yml +81 -0
- orbit_formats-0.1.0/pyproject.toml +134 -0
- orbit_formats-0.1.0/src/orbit_formats/__init__.py +88 -0
- orbit_formats-0.1.0/src/orbit_formats/adapters/__init__.py +11 -0
- orbit_formats-0.1.0/src/orbit_formats/adapters/base.py +38 -0
- orbit_formats-0.1.0/src/orbit_formats/api.py +137 -0
- orbit_formats-0.1.0/src/orbit_formats/canonical/__init__.py +33 -0
- orbit_formats-0.1.0/src/orbit_formats/canonical/attitude.py +18 -0
- orbit_formats-0.1.0/src/orbit_formats/canonical/base.py +178 -0
- orbit_formats-0.1.0/src/orbit_formats/canonical/conjunction.py +18 -0
- orbit_formats-0.1.0/src/orbit_formats/canonical/elements.py +124 -0
- orbit_formats-0.1.0/src/orbit_formats/canonical/ephemeris.py +108 -0
- orbit_formats-0.1.0/src/orbit_formats/canonical/fidelity.py +38 -0
- orbit_formats-0.1.0/src/orbit_formats/canonical/metadata.py +61 -0
- orbit_formats-0.1.0/src/orbit_formats/canonical/state.py +89 -0
- orbit_formats-0.1.0/src/orbit_formats/canonical/tracking.py +18 -0
- orbit_formats-0.1.0/src/orbit_formats/cli.py +107 -0
- orbit_formats-0.1.0/src/orbit_formats/convert/__init__.py +46 -0
- orbit_formats-0.1.0/src/orbit_formats/convert/elements.py +197 -0
- orbit_formats-0.1.0/src/orbit_formats/convert/graph.py +61 -0
- orbit_formats-0.1.0/src/orbit_formats/convert/time.py +78 -0
- orbit_formats-0.1.0/src/orbit_formats/detect.py +95 -0
- orbit_formats-0.1.0/src/orbit_formats/errors.py +111 -0
- orbit_formats-0.1.0/src/orbit_formats/formats.py +271 -0
- orbit_formats-0.1.0/src/orbit_formats/py.typed +0 -0
- orbit_formats-0.1.0/src/orbit_formats/readers/__init__.py +12 -0
- orbit_formats-0.1.0/src/orbit_formats/readers/ccsds.py +598 -0
- orbit_formats-0.1.0/src/orbit_formats/readers/gmat_report.py +400 -0
- orbit_formats-0.1.0/src/orbit_formats/readers/tle.py +212 -0
- orbit_formats-0.1.0/src/orbit_formats/registry.py +80 -0
- orbit_formats-0.1.0/src/orbit_formats/source.py +93 -0
- orbit_formats-0.1.0/src/orbit_formats/units.py +35 -0
- orbit_formats-0.1.0/src/orbit_formats/warnings.py +168 -0
- orbit_formats-0.1.0/src/orbit_formats/writers/__init__.py +12 -0
- orbit_formats-0.1.0/src/orbit_formats/writers/oem.py +217 -0
- orbit_formats-0.1.0/tests/conftest.py +44 -0
- orbit_formats-0.1.0/tests/data/oem/golden_roundtrip.oem +43 -0
- orbit_formats-0.1.0/tests/test_api.py +210 -0
- orbit_formats-0.1.0/tests/test_ccsds_oem.py +492 -0
- orbit_formats-0.1.0/tests/test_ccsds_oem_oracle.py +82 -0
- orbit_formats-0.1.0/tests/test_cli.py +185 -0
- orbit_formats-0.1.0/tests/test_convert_elements.py +164 -0
- orbit_formats-0.1.0/tests/test_convert_graph.py +126 -0
- orbit_formats-0.1.0/tests/test_convert_time.py +87 -0
- orbit_formats-0.1.0/tests/test_detect.py +131 -0
- orbit_formats-0.1.0/tests/test_elements.py +98 -0
- orbit_formats-0.1.0/tests/test_ephemeris.py +132 -0
- orbit_formats-0.1.0/tests/test_errors.py +40 -0
- orbit_formats-0.1.0/tests/test_fidelity_adapter.py +88 -0
- orbit_formats-0.1.0/tests/test_formats.py +72 -0
- orbit_formats-0.1.0/tests/test_future_stubs.py +13 -0
- orbit_formats-0.1.0/tests/test_gmat_report.py +387 -0
- orbit_formats-0.1.0/tests/test_metadata.py +58 -0
- orbit_formats-0.1.0/tests/test_oem_writer.py +217 -0
- orbit_formats-0.1.0/tests/test_package.py +8 -0
- orbit_formats-0.1.0/tests/test_source.py +74 -0
- orbit_formats-0.1.0/tests/test_state.py +88 -0
- orbit_formats-0.1.0/tests/test_tle.py +157 -0
- orbit_formats-0.1.0/tests/test_warnings.py +239 -0
- orbit_formats-0.1.0/uv.lock +1705 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Golden / reference fixtures under tests/data are compared byte-for-byte by the
|
|
2
|
+
# round-trip tests. Git's line-ending normalisation (autocrlf on Windows checks out LF as
|
|
3
|
+
# CRLF) would otherwise rewrite them and break those byte-exact assertions, so treat the
|
|
4
|
+
# fixtures as binary — checked out verbatim, identical on every platform.
|
|
5
|
+
tests/data/** -text
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: Bug
|
|
2
|
+
description: Report a defect in orbit-formats.
|
|
3
|
+
title: "<short summary>"
|
|
4
|
+
labels: ["type:bug"]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: what_happened
|
|
8
|
+
attributes:
|
|
9
|
+
label: What happened
|
|
10
|
+
description: Observed behavior. Include error messages and tracebacks verbatim.
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: what_expected
|
|
15
|
+
attributes:
|
|
16
|
+
label: What you expected
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
- type: textarea
|
|
20
|
+
id: repro
|
|
21
|
+
attributes:
|
|
22
|
+
label: Minimal reproduction
|
|
23
|
+
description: >-
|
|
24
|
+
Smallest snippet that triggers the bug — the Python (or CLI) call and, if a
|
|
25
|
+
file fails to parse or convert, the smallest sample file that reproduces it.
|
|
26
|
+
render: python
|
|
27
|
+
validations:
|
|
28
|
+
required: true
|
|
29
|
+
- type: input
|
|
30
|
+
id: orbit_formats_version
|
|
31
|
+
attributes:
|
|
32
|
+
label: orbit-formats version
|
|
33
|
+
placeholder: "e.g. 0.1.0, or git sha"
|
|
34
|
+
validations:
|
|
35
|
+
required: true
|
|
36
|
+
- type: input
|
|
37
|
+
id: python_version
|
|
38
|
+
attributes:
|
|
39
|
+
label: Python version
|
|
40
|
+
placeholder: "e.g. 3.12.3"
|
|
41
|
+
validations:
|
|
42
|
+
required: true
|
|
43
|
+
- type: dropdown
|
|
44
|
+
id: os
|
|
45
|
+
attributes:
|
|
46
|
+
label: Operating system
|
|
47
|
+
options:
|
|
48
|
+
- Linux
|
|
49
|
+
- Windows
|
|
50
|
+
- macOS
|
|
51
|
+
- Other
|
|
52
|
+
validations:
|
|
53
|
+
required: true
|
|
54
|
+
- type: textarea
|
|
55
|
+
id: logs
|
|
56
|
+
attributes:
|
|
57
|
+
label: Logs
|
|
58
|
+
description: Relevant output — tracebacks, warnings, pytest output.
|
|
59
|
+
render: shell
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Chore
|
|
2
|
+
description: Tooling, infra, hygiene, or other non-feature work.
|
|
3
|
+
title: "<short summary>"
|
|
4
|
+
labels: ["type:chore"]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: what
|
|
8
|
+
attributes:
|
|
9
|
+
label: What
|
|
10
|
+
description: What needs to happen.
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: why
|
|
15
|
+
attributes:
|
|
16
|
+
label: Why
|
|
17
|
+
description: Why now. What's blocked or painful without it.
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
- type: textarea
|
|
21
|
+
id: done
|
|
22
|
+
attributes:
|
|
23
|
+
label: Definition of done
|
|
24
|
+
value: |
|
|
25
|
+
- [ ]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Feature
|
|
2
|
+
description: Propose a new capability or a non-trivial change.
|
|
3
|
+
title: "<short summary>"
|
|
4
|
+
labels: ["type:feature"]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: summary
|
|
8
|
+
attributes:
|
|
9
|
+
label: Summary
|
|
10
|
+
description: One or two sentences describing what this feature is.
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: motivation
|
|
15
|
+
attributes:
|
|
16
|
+
label: Motivation
|
|
17
|
+
description: Why this matters. What can't users do today that they should be able to do?
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
- type: textarea
|
|
21
|
+
id: acceptance
|
|
22
|
+
attributes:
|
|
23
|
+
label: Acceptance criteria
|
|
24
|
+
description: >-
|
|
25
|
+
Concrete, testable checkboxes. An outside reviewer should be able to read these
|
|
26
|
+
and tell whether the feature is done.
|
|
27
|
+
value: |
|
|
28
|
+
- [ ]
|
|
29
|
+
- [ ]
|
|
30
|
+
validations:
|
|
31
|
+
required: true
|
|
32
|
+
- type: textarea
|
|
33
|
+
id: out_of_scope
|
|
34
|
+
attributes:
|
|
35
|
+
label: Out of scope / non-goals
|
|
36
|
+
description: What this issue deliberately does not cover. Helps keep scope honest.
|
|
37
|
+
- type: textarea
|
|
38
|
+
id: notes
|
|
39
|
+
attributes:
|
|
40
|
+
label: Notes
|
|
41
|
+
description: Design notes, links to charter sections, references to prior art, open questions.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Thanks for contributing to orbit-formats. Keep PRs small and scoped to one issue.
|
|
3
|
+
-->
|
|
4
|
+
|
|
5
|
+
## Summary
|
|
6
|
+
|
|
7
|
+
<!-- One or two sentences: what this PR changes and why. -->
|
|
8
|
+
|
|
9
|
+
Closes #
|
|
10
|
+
|
|
11
|
+
## Changes
|
|
12
|
+
|
|
13
|
+
<!-- Short bullet list of the substantive changes. Omit trivia (formatting, imports). -->
|
|
14
|
+
|
|
15
|
+
-
|
|
16
|
+
|
|
17
|
+
## Tested with
|
|
18
|
+
|
|
19
|
+
<!-- Tick what you ran locally. CI will re-run on Ubuntu, Windows, and macOS. -->
|
|
20
|
+
|
|
21
|
+
- [ ] `pytest`
|
|
22
|
+
- [ ] `ruff check`
|
|
23
|
+
- [ ] `ruff format --check`
|
|
24
|
+
- [ ] `mypy`
|
|
25
|
+
|
|
26
|
+
## Breaking changes
|
|
27
|
+
|
|
28
|
+
<!-- "None" if none. Otherwise: what breaks, who is affected, migration notes. -->
|
|
29
|
+
|
|
30
|
+
None.
|
|
31
|
+
|
|
32
|
+
## Notes for reviewers
|
|
33
|
+
|
|
34
|
+
<!-- Optional. Things to look at first, known rough edges, questions for the reviewer. -->
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
name: test (${{ matrix.os }}, py${{ matrix.python-version }})
|
|
15
|
+
runs-on: ${{ matrix.os }}
|
|
16
|
+
timeout-minutes: 20
|
|
17
|
+
strategy:
|
|
18
|
+
fail-fast: false
|
|
19
|
+
# 3 OSes × 3 Python minors = 9 cells. orbit-formats is pure Python with no
|
|
20
|
+
# native or GMAT dependency, so the matrix is the platform/interpreter spread,
|
|
21
|
+
# nothing more.
|
|
22
|
+
matrix:
|
|
23
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
24
|
+
python-version: ['3.10', '3.11', '3.12']
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
|
|
28
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
29
|
+
uses: actions/setup-python@v5
|
|
30
|
+
with:
|
|
31
|
+
python-version: ${{ matrix.python-version }}
|
|
32
|
+
|
|
33
|
+
- name: Install uv
|
|
34
|
+
uses: astral-sh/setup-uv@v4
|
|
35
|
+
with:
|
|
36
|
+
enable-cache: true
|
|
37
|
+
cache-dependency-glob: uv.lock
|
|
38
|
+
|
|
39
|
+
- name: Install project
|
|
40
|
+
run: uv sync --all-groups
|
|
41
|
+
|
|
42
|
+
- name: Run pytest
|
|
43
|
+
run: uv run pytest --cov --cov-report=term-missing
|
|
44
|
+
|
|
45
|
+
lint:
|
|
46
|
+
name: lint
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
timeout-minutes: 5
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@v4
|
|
51
|
+
- uses: actions/setup-python@v5
|
|
52
|
+
with:
|
|
53
|
+
python-version: '3.12'
|
|
54
|
+
- uses: astral-sh/setup-uv@v4
|
|
55
|
+
with:
|
|
56
|
+
enable-cache: true
|
|
57
|
+
cache-dependency-glob: uv.lock
|
|
58
|
+
- run: uv sync --all-groups
|
|
59
|
+
- run: uv run ruff check
|
|
60
|
+
- run: uv run ruff format --check
|
|
61
|
+
|
|
62
|
+
typecheck:
|
|
63
|
+
name: typecheck
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
timeout-minutes: 10
|
|
66
|
+
steps:
|
|
67
|
+
- uses: actions/checkout@v4
|
|
68
|
+
- uses: actions/setup-python@v5
|
|
69
|
+
with:
|
|
70
|
+
python-version: '3.12'
|
|
71
|
+
- uses: astral-sh/setup-uv@v4
|
|
72
|
+
with:
|
|
73
|
+
enable-cache: true
|
|
74
|
+
cache-dependency-glob: uv.lock
|
|
75
|
+
- run: uv sync --all-groups
|
|
76
|
+
- run: uv run mypy
|
|
77
|
+
|
|
78
|
+
# Smoke-check that `pip install orbit-formats` (no extras, no dev group) imports
|
|
79
|
+
# cleanly. Catches an extras-only dependency leaking into the default install path.
|
|
80
|
+
minimal-install:
|
|
81
|
+
name: minimal install smoke
|
|
82
|
+
runs-on: ubuntu-latest
|
|
83
|
+
timeout-minutes: 5
|
|
84
|
+
steps:
|
|
85
|
+
- uses: actions/checkout@v4
|
|
86
|
+
- uses: actions/setup-python@v5
|
|
87
|
+
with:
|
|
88
|
+
python-version: '3.12'
|
|
89
|
+
- run: pip install .
|
|
90
|
+
- run: python -c "import orbit_formats"
|
|
91
|
+
|
|
92
|
+
# Cross-validate the OEM writer against the ccsds-ndm oracle. ccsds-ndm is GPL-3.0, so
|
|
93
|
+
# it is never a runtime, extra, or dev dependency, never in uv.lock, and never shipped:
|
|
94
|
+
# it is installed transiently here with `uv run --with` and used only by this job's
|
|
95
|
+
# tests (which `importorskip` it, so they skip everywhere else). This keeps the whole
|
|
96
|
+
# distributed/locked project GPL-free while still validating against a reference impl.
|
|
97
|
+
oracle:
|
|
98
|
+
name: oracle cross-validation (ccsds-ndm)
|
|
99
|
+
runs-on: ubuntu-latest
|
|
100
|
+
timeout-minutes: 10
|
|
101
|
+
steps:
|
|
102
|
+
- uses: actions/checkout@v4
|
|
103
|
+
- uses: actions/setup-python@v5
|
|
104
|
+
with:
|
|
105
|
+
python-version: '3.12'
|
|
106
|
+
- uses: astral-sh/setup-uv@v4
|
|
107
|
+
with:
|
|
108
|
+
enable-cache: true
|
|
109
|
+
cache-dependency-glob: uv.lock
|
|
110
|
+
- run: uv sync --all-groups
|
|
111
|
+
- run: uv run --with ccsds-ndm pytest tests/test_ccsds_oem_oracle.py -v
|
|
112
|
+
|
|
113
|
+
# Validate CITATION.cff against the Citation File Format v1.2.0 schema. cffconvert
|
|
114
|
+
# is installed standalone because it is a CI-only schema check, not part of the dev
|
|
115
|
+
# or runtime dependency graph.
|
|
116
|
+
citation:
|
|
117
|
+
name: citation file (cffconvert)
|
|
118
|
+
runs-on: ubuntu-latest
|
|
119
|
+
timeout-minutes: 5
|
|
120
|
+
steps:
|
|
121
|
+
- uses: actions/checkout@v4
|
|
122
|
+
- uses: actions/setup-python@v5
|
|
123
|
+
with:
|
|
124
|
+
python-version: '3.12'
|
|
125
|
+
- run: pip install cffconvert==2.0.0
|
|
126
|
+
- run: cffconvert --validate -i CITATION.cff
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ['v*']
|
|
7
|
+
pull_request:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: docs-${{ github.workflow }}-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
build:
|
|
16
|
+
name: build
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
timeout-minutes: 5
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: '3.12'
|
|
24
|
+
- uses: astral-sh/setup-uv@v4
|
|
25
|
+
with:
|
|
26
|
+
enable-cache: true
|
|
27
|
+
cache-dependency-glob: uv.lock
|
|
28
|
+
- run: uv sync --all-groups
|
|
29
|
+
- run: uv run mkdocs build --strict
|
|
30
|
+
|
|
31
|
+
deploy:
|
|
32
|
+
name: deploy
|
|
33
|
+
needs: build
|
|
34
|
+
if: |
|
|
35
|
+
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')) ||
|
|
36
|
+
github.event_name == 'workflow_dispatch'
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
timeout-minutes: 5
|
|
39
|
+
permissions:
|
|
40
|
+
contents: write
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
with:
|
|
44
|
+
fetch-depth: 0
|
|
45
|
+
- uses: actions/setup-python@v5
|
|
46
|
+
with:
|
|
47
|
+
python-version: '3.12'
|
|
48
|
+
- uses: astral-sh/setup-uv@v4
|
|
49
|
+
with:
|
|
50
|
+
enable-cache: true
|
|
51
|
+
cache-dependency-glob: uv.lock
|
|
52
|
+
- run: uv sync --all-groups
|
|
53
|
+
- name: Configure git identity
|
|
54
|
+
run: |
|
|
55
|
+
git config user.name "github-actions[bot]"
|
|
56
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
57
|
+
- run: uv run mkdocs gh-deploy --force --no-history
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ['v*']
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: release-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: false
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
build:
|
|
16
|
+
name: build sdist + wheel
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
timeout-minutes: 5
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: '3.12'
|
|
24
|
+
- uses: astral-sh/setup-uv@v4
|
|
25
|
+
with:
|
|
26
|
+
enable-cache: true
|
|
27
|
+
cache-dependency-glob: uv.lock
|
|
28
|
+
- run: uv build
|
|
29
|
+
- uses: actions/upload-artifact@v4
|
|
30
|
+
with:
|
|
31
|
+
name: dist
|
|
32
|
+
path: dist/
|
|
33
|
+
if-no-files-found: error
|
|
34
|
+
|
|
35
|
+
publish-pypi:
|
|
36
|
+
name: publish to PyPI
|
|
37
|
+
needs: build
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
timeout-minutes: 10
|
|
40
|
+
environment:
|
|
41
|
+
name: pypi
|
|
42
|
+
url: https://pypi.org/p/orbit-formats
|
|
43
|
+
permissions:
|
|
44
|
+
id-token: write
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/download-artifact@v4
|
|
47
|
+
with:
|
|
48
|
+
name: dist
|
|
49
|
+
path: dist/
|
|
50
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
51
|
+
with:
|
|
52
|
+
skip-existing: true
|
|
53
|
+
|
|
54
|
+
github-release:
|
|
55
|
+
name: github release
|
|
56
|
+
needs: publish-pypi
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
timeout-minutes: 5
|
|
59
|
+
permissions:
|
|
60
|
+
contents: write
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/download-artifact@v4
|
|
63
|
+
with:
|
|
64
|
+
name: dist
|
|
65
|
+
path: dist/
|
|
66
|
+
- name: Create GitHub Release
|
|
67
|
+
env:
|
|
68
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
69
|
+
run: |
|
|
70
|
+
gh release create "${GITHUB_REF_NAME}" \
|
|
71
|
+
--repo "${GITHUB_REPOSITORY}" \
|
|
72
|
+
--title "${GITHUB_REF_NAME}" \
|
|
73
|
+
--generate-notes \
|
|
74
|
+
dist/*
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
*.egg
|
|
7
|
+
*.egg-info/
|
|
8
|
+
.eggs/
|
|
9
|
+
build/
|
|
10
|
+
dist/
|
|
11
|
+
wheels/
|
|
12
|
+
*.whl
|
|
13
|
+
|
|
14
|
+
# Environments
|
|
15
|
+
.venv/
|
|
16
|
+
venv/
|
|
17
|
+
env/
|
|
18
|
+
.env
|
|
19
|
+
|
|
20
|
+
# uv
|
|
21
|
+
.uv-cache/
|
|
22
|
+
|
|
23
|
+
# Tooling caches
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
.mypy_cache/
|
|
26
|
+
.ruff_cache/
|
|
27
|
+
.coverage
|
|
28
|
+
.coverage.*
|
|
29
|
+
coverage.xml
|
|
30
|
+
htmlcov/
|
|
31
|
+
|
|
32
|
+
# Docs build
|
|
33
|
+
site/
|
|
34
|
+
.cache/
|
|
35
|
+
|
|
36
|
+
# IDE / editor
|
|
37
|
+
.vscode/
|
|
38
|
+
.idea/
|
|
39
|
+
*.swp
|
|
40
|
+
.DS_Store
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to orbit-formats are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-05-30
|
|
11
|
+
|
|
12
|
+
First release. orbit-formats reads TLE, CCSDS OEM (KVN), and GMAT report files into a
|
|
13
|
+
single canonical representation, writes CCSDS OEM, and round-trips OEM losslessly —
|
|
14
|
+
emitting an explicit, structured warning whenever a conversion cannot preserve
|
|
15
|
+
information, never a silent drop.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- Canonical representation: a federated, typed dataclass family — `StateVector`,
|
|
20
|
+
`Ephemeris`, and `MeanElementSet` — unified by a shared `Metadata` spine (reference
|
|
21
|
+
frame, time scale, central body, object id, units, provenance). State-series types
|
|
22
|
+
project to a gmat-run-identical DataFrame (`Epoch, X, Y, Z, VX, VY, VZ`, with the spine
|
|
23
|
+
on `DataFrame.attrs`) that downstream consumers can adopt without reshaping.
|
|
24
|
+
- Two-layer model: a faithful per-format fidelity layer beneath the canonical metamodel,
|
|
25
|
+
linked by an optional `source_native` handle, so a same-format round-trip stays
|
|
26
|
+
byte-identical (with `retain_source=True`) or content-lossless.
|
|
27
|
+
- Readers: TLE / 3LE (sgp4-backed mean elements, TEME / UTC), CCSDS OEM (in-house KVN
|
|
28
|
+
parser — multi-segment, with covariance and acceleration preserved on the fidelity
|
|
29
|
+
model), and GMAT report (whitespace-aligned tables into an ephemeris or state).
|
|
30
|
+
- Writer: CCSDS OEM (KVN), with byte-identical, content-lossless, and synthesised paths.
|
|
31
|
+
- Public API — `read`, `write`, `convert`, and `detect_format` — with
|
|
32
|
+
content-signature-first format auto-detection and an explicit `format=` override.
|
|
33
|
+
- Conversion graph: Cartesian ↔ Keplerian elements and time-scale conversion across UTC /
|
|
34
|
+
TAI / TT / TDB / GPS / UT1; conversions route through each format's preferred canonical
|
|
35
|
+
form.
|
|
36
|
+
- Lossy-conversion framework: structured, catchable warnings (`DroppedFieldWarning`,
|
|
37
|
+
`PrecisionLossWarning`, `ModelApproximationWarning`, `MissingFieldWarning`) that name
|
|
38
|
+
exactly what a conversion drops.
|
|
39
|
+
- Frame-rotation boundary: reference frames are tagged and preserved; a conversion that
|
|
40
|
+
would require rotating between distinct frames is refused rather than approximated.
|
|
41
|
+
- `orbit-formats convert` command-line interface for one-shot file-to-file conversion.
|
|
42
|
+
- Documentation site — the canonical-representation reference, a per-format reference, the
|
|
43
|
+
lossy-conversion semantics, and the conversion-capability matrix — and a typed
|
|
44
|
+
(`py.typed`), MIT-licensed package published to PyPI.
|
|
45
|
+
|
|
46
|
+
[Unreleased]: https://github.com/astro-tools/orbit-formats/compare/v0.1.0...HEAD
|
|
47
|
+
[0.1.0]: https://github.com/astro-tools/orbit-formats/releases/tag/v0.1.0
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use orbit-formats in academic work, please cite it using the metadata below."
|
|
3
|
+
title: orbit-formats
|
|
4
|
+
abstract: >-
|
|
5
|
+
orbit-formats is a permissively-licensed Python library for reading, writing, and
|
|
6
|
+
losslessly converting between orbital state-vector and ephemeris file formats — TLE,
|
|
7
|
+
the CCSDS Navigation Data Message family, SP3, SPICE SPK, STK ephemeris, and GMAT
|
|
8
|
+
report. It reads each format into a single canonical in-memory representation and
|
|
9
|
+
emits an explicit, structured warning whenever a conversion cannot preserve
|
|
10
|
+
information.
|
|
11
|
+
type: software
|
|
12
|
+
authors:
|
|
13
|
+
- given-names: Dimitrije
|
|
14
|
+
family-names: Jankovic
|
|
15
|
+
version: 0.1.0
|
|
16
|
+
date-released: "2026-05-30"
|
|
17
|
+
license: MIT
|
|
18
|
+
repository-code: "https://github.com/astro-tools/orbit-formats"
|
|
19
|
+
url: "https://github.com/astro-tools/orbit-formats"
|
|
20
|
+
keywords:
|
|
21
|
+
- astrodynamics
|
|
22
|
+
- orbital-mechanics
|
|
23
|
+
- ephemeris
|
|
24
|
+
- ccsds
|
|
25
|
+
- tle
|
|
26
|
+
- space
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Contributing to orbit-formats
|
|
2
|
+
|
|
3
|
+
Thanks for your interest. This page is the one place to learn the workflow.
|
|
4
|
+
|
|
5
|
+
## Getting set up
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/astro-tools/orbit-formats.git
|
|
9
|
+
cd orbit-formats
|
|
10
|
+
uv sync --all-groups
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This installs the package, its runtime dependencies, and the dev and docs groups.
|
|
14
|
+
|
|
15
|
+
## Branches and PRs
|
|
16
|
+
|
|
17
|
+
- One issue per branch. Branch names use a short prefix for type:
|
|
18
|
+
- `feat/<slug>` — new capability, tied to a `type:feature` issue.
|
|
19
|
+
- `fix/<slug>` — bug fix, tied to a `type:bug` issue.
|
|
20
|
+
- `chore/<slug>` — infra / tooling / hygiene.
|
|
21
|
+
- `docs/<slug>` — docs-only change.
|
|
22
|
+
- Open a PR against `main`. Put `Closes #<N>` in the PR description so the issue
|
|
23
|
+
auto-closes on merge and the project board advances the card to Done.
|
|
24
|
+
- Squash-merge is the only merge method. The PR title becomes the squash commit
|
|
25
|
+
subject — write it as a complete imperative sentence.
|
|
26
|
+
|
|
27
|
+
## Local checks before pushing
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
uv run pytest # tests
|
|
31
|
+
uv run ruff check # lint
|
|
32
|
+
uv run ruff format --check # formatting
|
|
33
|
+
uv run mypy # types
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
CI re-runs all four on Ubuntu, Windows, and macOS, across Python 3.10, 3.11, and 3.12.
|
|
37
|
+
|
|
38
|
+
## Commit messages
|
|
39
|
+
|
|
40
|
+
Keep them short and imperative. One subject line, optional body.
|
|
41
|
+
|
|
42
|
+
Do not include AI or tool attribution trailers in commits, PR titles, PR descriptions,
|
|
43
|
+
or comments — see the repo-level convention.
|
|
44
|
+
|
|
45
|
+
## Scope discipline
|
|
46
|
+
|
|
47
|
+
orbit-formats is an I/O and conversion library: it reads, writes, and converts orbital
|
|
48
|
+
state and ephemeris formats, and warns whenever a conversion cannot preserve
|
|
49
|
+
information. It is deliberately not a propagator, an integrator, or a general
|
|
50
|
+
frame-transformation engine. Before opening a feature issue, check the charter and the
|
|
51
|
+
existing issues to make sure the work belongs here.
|
|
52
|
+
|
|
53
|
+
## Questions
|
|
54
|
+
|
|
55
|
+
Open a [discussion](https://github.com/orgs/astro-tools/discussions) rather than an
|
|
56
|
+
issue for open-ended questions, usage help, or brainstorming. The astro-tools org runs
|
|
57
|
+
a single shared discussions space — there is no per-repo discussions board.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 astro-tools
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|