satnogs-predict 1.0__tar.gz → 1.2__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.
- {satnogs_predict-1.0 → satnogs_predict-1.2}/CHANGELOG.md +16 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/CONTRIBUTING.md +88 -10
- {satnogs_predict-1.0 → satnogs_predict-1.2}/PKG-INFO +1 -1
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/tle.py +19 -5
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict.egg-info/PKG-INFO +1 -1
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict.egg-info/scm_file_list.json +33 -33
- satnogs_predict-1.2/src/satnogs_predict.egg-info/scm_version.json +8 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/tests/integration/test_fake_tle_integration.py +7 -4
- satnogs_predict-1.2/tests/test_tle.py +114 -0
- satnogs_predict-1.0/src/satnogs_predict.egg-info/scm_version.json +0 -8
- satnogs_predict-1.0/tests/test_tle.py +0 -53
- {satnogs_predict-1.0 → satnogs_predict-1.2}/.gitignore +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/.gitlab-ci.yml +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/.pre-commit-config.yaml +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/.python-version +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/LICENSE +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/README.md +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/docs/docs.md +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/pyproject.toml +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/setup.cfg +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/setup.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/__init__.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/constraints/__init__.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/constraints/constraints.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/core/__init__.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/core/engine.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/domain/__init__.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/domain/constraints.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/domain/geometry.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/domain/observer.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/domain/orbit.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/domain/planner.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/domain/time.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/domain/validation.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/domain/window.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/planning/__init__.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/planning/planner.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/propagation/__init__.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/propagation/propagator.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/py.typed +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict/utils.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict.egg-info/SOURCES.txt +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict.egg-info/dependency_links.txt +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict.egg-info/requires.txt +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict.egg-info/top_level.txt +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/tests/__init__.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/tests/conftest.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/tests/constraints/test_constraints.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/tests/core/test_engine.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/tests/domain/test_observer.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/tests/domain/test_orbit.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/tests/domain/test_planner_domain.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/tests/domain/test_required_fields.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/tests/domain/test_time.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/tests/domain/test_window.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/tests/helpers.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/tests/planning/test_planner.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/tests/propagation/test_propagator.py +0 -0
- {satnogs_predict-1.0 → satnogs_predict-1.2}/uv.lock +0 -0
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `generate_fake_tle()` encodes catalog numbers 100000–339999 in Alpha-5 format.
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- `generate_fake_tle()` raises `ValueError` for catalog numbers outside 0–339999. Previously 6-digit numbers were silently truncated to their first five digits.
|
|
12
|
+
|
|
13
|
+
## 1.1
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- `generate_fake_tle()` writes the epoch year as the last two digits of the year. Previously it used the first two digits (e.g. `20` for 2026), so every generated TLE had an epoch in 2020.
|
|
18
|
+
|
|
3
19
|
## 1.0
|
|
4
20
|
|
|
5
21
|
### Breaking Changes
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* [Respond to review comments](#respond-to-review-comments)
|
|
12
12
|
* [Don’t get discouraged - or impatient](#dont-get-discouraged-or-impatient)
|
|
13
13
|
* [Sign your work - the Developer’s Certificate of Origin](#sign-your-work-the-developers-certificate-of-origin)
|
|
14
|
+
* [Generative AI / LLM Policy](#generative-ai--llm-policy)
|
|
14
15
|
* [References](#references)
|
|
15
16
|
|
|
16
17
|
## Introduction ##
|
|
@@ -22,7 +23,7 @@ It is based on Linux kernel patch submission guidelines.
|
|
|
22
23
|
|
|
23
24
|
All Libre Space Foundation projects code is maintained under Git.
|
|
24
25
|
Obtain a copy of the source code using `git clone`.
|
|
25
|
-
Development usually takes place under `master` or `dev` branch.
|
|
26
|
+
Development usually takes place under `main`, `master` or `dev` branch.
|
|
26
27
|
|
|
27
28
|
## Separate the changes ##
|
|
28
29
|
|
|
@@ -39,16 +40,16 @@ The point to remember is that each commit should make an easily understood chang
|
|
|
39
40
|
Each commit should be justifiable on its own merits.
|
|
40
41
|
|
|
41
42
|
When creating a merge request from a series of commits, take special care to ensure that the software builds and runs properly after each commit in the series.
|
|
42
|
-
Developers using `git bisect` to track down a problem can end up splitting your merge request at any point;
|
|
43
|
+
Developers using `git bisect` to track down a problem can end up splitting your merge request at any point; not thanking you if you introduce bugs in the middle.
|
|
43
44
|
|
|
44
45
|
If you cannot condense your merge request into a smaller set of commits, then only submit say 15 or so at a time and wait for review and integration.
|
|
45
46
|
|
|
46
|
-
Never commit binary files.
|
|
47
|
+
Never commit binary files or statically generated code.
|
|
47
48
|
Once they are part of Git repository history they cannot be removed.
|
|
48
49
|
|
|
49
50
|
## Quality check of the changes ##
|
|
50
51
|
|
|
51
|
-
Continuous Integration
|
|
52
|
+
Continuous Integration checks your changes automatically once you create a merge request.
|
|
52
53
|
Nevertheless, local quality check should also be done before submitting a merge request.
|
|
53
54
|
The checks can include:
|
|
54
55
|
* Static code analysis and coverage
|
|
@@ -78,10 +79,10 @@ See [Separate the changes](#separate-the-changes).
|
|
|
78
79
|
|
|
79
80
|
Create commit or merge requests descriptions that are self-contained.
|
|
80
81
|
This benefits both the maintainers and reviewers.
|
|
81
|
-
Always use
|
|
82
|
+
Always use a known identity and email address for committing changes.
|
|
82
83
|
|
|
83
84
|
If the commit fixes a reported issue, refer to that bug entry by issue number.
|
|
84
|
-
The issue
|
|
85
|
+
The issue closes automatically after merge, if it matches [this regular expression](https://docs.gitlab.com/ce/user/project/issues/automatic_issue_closing.html).
|
|
85
86
|
|
|
86
87
|
## Create a merge request ##
|
|
87
88
|
|
|
@@ -91,7 +92,7 @@ Merge requests must not contain other merge requests; re-base your commits if ne
|
|
|
91
92
|
|
|
92
93
|
## Respond to review comments ##
|
|
93
94
|
|
|
94
|
-
Your merge request
|
|
95
|
+
Your merge request almost certainly gets comments from reviewers on ways in which it can be improved.
|
|
95
96
|
You must respond to those comments; ignoring reviewers is a good way to get ignored in return.
|
|
96
97
|
Be sure to tell the reviewers what changes you are making and to thank them for their time.
|
|
97
98
|
Code review is a tiring and time-consuming process, and reviewers sometimes get grumpy.
|
|
@@ -106,9 +107,9 @@ Wait for a minimum of one week before pinging reviewers - possibly longer during
|
|
|
106
107
|
|
|
107
108
|
## Sign your work - the Developer’s Certificate of Origin ##
|
|
108
109
|
|
|
109
|
-
To improve tracking of who did what, especially with commits that can percolate to their final resting place in Libre Space Foundation projects code-base through several layers of maintainers,
|
|
110
|
+
To improve tracking of who did what, especially with commits that can percolate to their final resting place in Libre Space Foundation projects code-base through several layers of maintainers, a “sign-off” procedure on commits is enforced.
|
|
110
111
|
|
|
111
|
-
The sign-off is a simple line at the end of the explanation for the commit, which certifies that you wrote it or otherwise have the right to pass it on as an open
|
|
112
|
+
The sign-off is a simple line at the end of the explanation for the commit, which certifies that you wrote it or otherwise have the right to pass it on as an open source patch.
|
|
112
113
|
The rules are pretty simple:
|
|
113
114
|
|
|
114
115
|
if you can certify the below:
|
|
@@ -138,7 +139,7 @@ then you just add a line saying:
|
|
|
138
139
|
|
|
139
140
|
`Signed-off-by: Random J Developer <random@developer.example.org>`
|
|
140
141
|
|
|
141
|
-
using
|
|
142
|
+
using a known identity (sorry, no anonymous contributions.)
|
|
142
143
|
|
|
143
144
|
If you are a maintainer, sometimes you need to slightly modify commits order to merge them.
|
|
144
145
|
If you stick strictly to rule (c), you should ask the submitter to rediff, but this is a totally counter-productive waste of time and energy.
|
|
@@ -152,6 +153,83 @@ Signed-off-by: Random J Developer <random@developer.example.org>
|
|
|
152
153
|
Signed-off-by: Lucky K Maintainer <lucky@maintainer.example.org>
|
|
153
154
|
```
|
|
154
155
|
|
|
156
|
+
## Generative AI / LLM Policy ##
|
|
157
|
+
|
|
158
|
+
We appreciate that we can't realistically police how you author your merge requests, which includes whether you employ large-language model (LLM)-based development tools.
|
|
159
|
+
So, we don't.
|
|
160
|
+
|
|
161
|
+
However, due to both legal and human reasons, we have to establish boundaries.
|
|
162
|
+
|
|
163
|
+
### Overview ###
|
|
164
|
+
|
|
165
|
+
- We take the responsibility for this project very seriously and we expect you to take your responsibility for your contributions seriously, too.
|
|
166
|
+
This used to be a given, but it changed now that a merge request is just one prompt away.
|
|
167
|
+
- Every contribution has to be backed by a human who unequivocally owns the copyright for all changes.
|
|
168
|
+
No LLM bots in `Co-authored-by:`s.
|
|
169
|
+
- Repeated slop contributions are unacceptable, and will be closed without review.
|
|
170
|
+
We reserve the right to exclude contributors who continue to submit such material.
|
|
171
|
+
- Absolutely no unsupervised agentic tools like OpenClaw.
|
|
172
|
+
|
|
173
|
+
By submitting a merge request, you certify that:
|
|
174
|
+
|
|
175
|
+
- You are the author of the contribution or have the legal right to submit it.
|
|
176
|
+
- You either hold the copyright to the changes or have explicit legal authorization to contribute them under this project's license.
|
|
177
|
+
- You understand the code.
|
|
178
|
+
- You accept full responsibility for it.
|
|
179
|
+
|
|
180
|
+
### Legal ###
|
|
181
|
+
|
|
182
|
+
There is ongoing legal uncertainty regarding the copyright status of LLM-generated works and their provenance.
|
|
183
|
+
Because of this, allowing contributions by LLMs has unpredictable consequences for the copyright status of this project - even when leaving aside possible copyright violations due to plagiarism.
|
|
184
|
+
|
|
185
|
+
### Human ###
|
|
186
|
+
|
|
187
|
+
As the makers of software that is used by many people worldwide and with a reputation for high-quality maintenance, we take our responsibility to our users very seriously.
|
|
188
|
+
No matter what LLM vendors or boosters on LinkedIn tell you, we have to manually review every change before merging, because it's our responsibility to keep the project stable.
|
|
189
|
+
|
|
190
|
+
Please understand that by opening low-quality merge requests you're not helping anyone.
|
|
191
|
+
Worse, you're [poisoning the open source ecosystem](https://lwn.net/Articles/1058266/) that was precarious even before the arrival of LLM tools.
|
|
192
|
+
Having to wade through plausible-looking-but-low-quality merge requests and trying to determine which ones are legit is extremely demoralizing and has already burned out many good maintainers.
|
|
193
|
+
|
|
194
|
+
In particular, note that [code reviews are a social process](https://blog.glyph.im/2026/03/what-is-code-review-for.html) and are *not* intended to catch bugs in PRs created by an LLM.
|
|
195
|
+
If you submit a merge request, we expect you to engage with reviewers and learn from the review, not
|
|
196
|
+
simply update the prompt that generated the PR and regenerate the code.
|
|
197
|
+
|
|
198
|
+
Put bluntly, we have no time or interest to become part of your vibe coding loop where you drop LLM slop at our door, we spend time and energy to review it, and you just feed it back into the LLM for another iteration.
|
|
199
|
+
|
|
200
|
+
This dynamic is especially pernicious because it poisons the well for mentoring new contributors which we are committed to.
|
|
201
|
+
|
|
202
|
+
### Summary ###
|
|
203
|
+
|
|
204
|
+
In practice, this means:
|
|
205
|
+
|
|
206
|
+
- Merge requests that have an LLM product listed as co-author can't be merged and will be closed without further discussion.
|
|
207
|
+
We cannot risk the copyright status of this project.
|
|
208
|
+
|
|
209
|
+
If you used LLM tools during development, you may still submit - but you must remove any LLM co-author tags and take full ownership of every line.
|
|
210
|
+
|
|
211
|
+
- By submitting a merge request, you take full technical and legal responsibility for the contents of the merge request and promise that you hold the copyright for the changes submitted.
|
|
212
|
+
|
|
213
|
+
"An LLM wrote it" is *not* an acceptable response to questions or critique.
|
|
214
|
+
If you cannot explain and defend the changes you submit, do not submit them and open a high-quality bug report or feature request instead.
|
|
215
|
+
|
|
216
|
+
- Accounts that exercise bot-like behavior, like automated mass merge requests, will be permanently banned, whether they belong to a human or not.
|
|
217
|
+
|
|
218
|
+
- LLM-generated comments must be concise and accurate, and you must be prepared to stand by them.
|
|
219
|
+
Do not post summaries unless you are certain that they add value to the discussion.
|
|
220
|
+
Remember that all LLM output *looks* plausible.
|
|
221
|
+
When using these tools, it's your responsibility to ensure that the output is correct, and useful.
|
|
222
|
+
|
|
223
|
+
- Remember that LLM generated content is generally easier for you to produce, but *harder* for others to read, review or interpret.
|
|
224
|
+
Prioritising your time over that of others is contrary to the project's code of conduct.
|
|
225
|
+
Verbose, repetitive, or off topic comments may be marked as spam.
|
|
226
|
+
|
|
227
|
+
### Thanks ###
|
|
228
|
+
|
|
229
|
+
This LLM policy was based on the policy of the `pip` and `attrs` project.
|
|
230
|
+
Many thanks to them for developing it.
|
|
231
|
+
|
|
155
232
|
## References ##
|
|
156
233
|
|
|
157
234
|
* Linux kernel documentation - [Submitting patches: the essential guide to getting your code into the kernel](https://www.kernel.org/doc/html/latest/process/submitting-patches.html)
|
|
235
|
+
* pip AI policy - [Generative AI / LLM Policy](https://github.com/pypa/pip/blob/main/AI_POLICY.md)
|
|
@@ -29,6 +29,19 @@ class FakeTLEParameters:
|
|
|
29
29
|
|
|
30
30
|
DEFAULT_FAKE_TLE_PARAMETERS = FakeTLEParameters()
|
|
31
31
|
|
|
32
|
+
# Alpha-5 prefix letters for 10..33; I and O are skipped to avoid confusion with 1 and 0.
|
|
33
|
+
_ALPHA5_LETTERS = "ABCDEFGHJKLMNPQRSTUVWXYZ"
|
|
34
|
+
_MAX_ALPHA5_CATALOG_NUM = 339_999
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _format_catalog_number(catalog_num: int) -> str:
|
|
38
|
+
if 0 <= catalog_num <= 99_999:
|
|
39
|
+
return f"{catalog_num:05d}"
|
|
40
|
+
if 100_000 <= catalog_num <= _MAX_ALPHA5_CATALOG_NUM:
|
|
41
|
+
prefix, remainder = divmod(catalog_num, 10_000)
|
|
42
|
+
return f"{_ALPHA5_LETTERS[prefix - 10]}{remainder:04d}"
|
|
43
|
+
raise ValueError(f"catalog_num must be between 0 and {_MAX_ALPHA5_CATALOG_NUM}")
|
|
44
|
+
|
|
32
45
|
|
|
33
46
|
def _tle_checksum(line_without_checksum: str) -> int:
|
|
34
47
|
total = 0
|
|
@@ -71,17 +84,18 @@ def generate_fake_tle(
|
|
|
71
84
|
right_ascension = (observer_location.lst_hours_at(skyfield_time) * 15.0) % 360.0
|
|
72
85
|
mean_anomaly = latitude % 360.0
|
|
73
86
|
|
|
74
|
-
|
|
87
|
+
catalog_number = _format_catalog_number(params.catalog_num)
|
|
88
|
+
epoch_year = epoch.year % 100
|
|
75
89
|
epoch_day = _epoch_day_of_year_fraction(epoch)
|
|
76
90
|
|
|
77
91
|
line0 = params.name.ljust(24)
|
|
78
92
|
|
|
79
93
|
line1_no_checksum = (
|
|
80
|
-
"1 {:5.5}{:.1} {:8.8} {
|
|
81
|
-
|
|
94
|
+
"1 {:5.5}{:.1} {:8.8} {:02d}{:012.8f} {:>10.10} {:>8.8} {:>8.8} 0 {:>4.4}".format(
|
|
95
|
+
catalog_number,
|
|
82
96
|
params.classifier,
|
|
83
97
|
params.cospar_id,
|
|
84
|
-
|
|
98
|
+
epoch_year,
|
|
85
99
|
epoch_day,
|
|
86
100
|
params.mean_motion_first,
|
|
87
101
|
params.mean_motion_second,
|
|
@@ -92,7 +106,7 @@ def generate_fake_tle(
|
|
|
92
106
|
|
|
93
107
|
line2_no_checksum = (
|
|
94
108
|
"2 {:>5.5} {:>8.8} {:>8.4f} {:>7.7} {:>8.8} {:>8.4f} {:>11.11}{:>5.5}".format(
|
|
95
|
-
|
|
109
|
+
catalog_number,
|
|
96
110
|
params.inclination,
|
|
97
111
|
right_ascension,
|
|
98
112
|
params.eccentricity,
|
|
@@ -3,50 +3,50 @@
|
|
|
3
3
|
".gitignore",
|
|
4
4
|
".gitlab-ci.yml",
|
|
5
5
|
".pre-commit-config.yaml",
|
|
6
|
-
"CHANGELOG.md",
|
|
7
6
|
".python-version",
|
|
8
|
-
"
|
|
7
|
+
"CHANGELOG.md",
|
|
9
8
|
"CONTRIBUTING.md",
|
|
10
|
-
"uv.lock",
|
|
11
|
-
"pyproject.toml",
|
|
12
9
|
"LICENSE",
|
|
10
|
+
"README.md",
|
|
11
|
+
"docs/docs.md",
|
|
12
|
+
"pyproject.toml",
|
|
13
13
|
"setup.py",
|
|
14
|
-
"tests/conftest.py",
|
|
15
|
-
"tests/__init__.py",
|
|
16
|
-
"tests/helpers.py",
|
|
17
|
-
"tests/test_tle.py",
|
|
18
|
-
"tests/integration/test_fake_tle_integration.py",
|
|
19
|
-
"tests/domain/test_time.py",
|
|
20
|
-
"tests/domain/test_planner_domain.py",
|
|
21
|
-
"tests/domain/test_required_fields.py",
|
|
22
|
-
"tests/domain/test_orbit.py",
|
|
23
|
-
"tests/domain/test_observer.py",
|
|
24
|
-
"tests/domain/test_window.py",
|
|
25
|
-
"tests/planning/test_planner.py",
|
|
26
|
-
"tests/propagation/test_propagator.py",
|
|
27
|
-
"tests/core/test_engine.py",
|
|
28
|
-
"tests/constraints/test_constraints.py",
|
|
29
14
|
"src/satnogs_predict/__init__.py",
|
|
30
|
-
"src/satnogs_predict/py
|
|
31
|
-
"src/satnogs_predict/
|
|
32
|
-
"src/satnogs_predict/
|
|
15
|
+
"src/satnogs_predict/constraints/__init__.py",
|
|
16
|
+
"src/satnogs_predict/constraints/constraints.py",
|
|
17
|
+
"src/satnogs_predict/core/__init__.py",
|
|
18
|
+
"src/satnogs_predict/core/engine.py",
|
|
19
|
+
"src/satnogs_predict/domain/__init__.py",
|
|
33
20
|
"src/satnogs_predict/domain/constraints.py",
|
|
34
|
-
"src/satnogs_predict/domain/
|
|
21
|
+
"src/satnogs_predict/domain/geometry.py",
|
|
35
22
|
"src/satnogs_predict/domain/observer.py",
|
|
36
|
-
"src/satnogs_predict/domain/
|
|
23
|
+
"src/satnogs_predict/domain/orbit.py",
|
|
37
24
|
"src/satnogs_predict/domain/planner.py",
|
|
38
|
-
"src/satnogs_predict/domain/
|
|
25
|
+
"src/satnogs_predict/domain/time.py",
|
|
26
|
+
"src/satnogs_predict/domain/validation.py",
|
|
39
27
|
"src/satnogs_predict/domain/window.py",
|
|
40
|
-
"src/satnogs_predict/domain/geometry.py",
|
|
41
|
-
"src/satnogs_predict/domain/orbit.py",
|
|
42
|
-
"src/satnogs_predict/planning/planner.py",
|
|
43
28
|
"src/satnogs_predict/planning/__init__.py",
|
|
29
|
+
"src/satnogs_predict/planning/planner.py",
|
|
44
30
|
"src/satnogs_predict/propagation/__init__.py",
|
|
45
31
|
"src/satnogs_predict/propagation/propagator.py",
|
|
46
|
-
"src/satnogs_predict/
|
|
47
|
-
"src/satnogs_predict/
|
|
48
|
-
"src/satnogs_predict/
|
|
49
|
-
"
|
|
50
|
-
"
|
|
32
|
+
"src/satnogs_predict/py.typed",
|
|
33
|
+
"src/satnogs_predict/tle.py",
|
|
34
|
+
"src/satnogs_predict/utils.py",
|
|
35
|
+
"tests/__init__.py",
|
|
36
|
+
"tests/conftest.py",
|
|
37
|
+
"tests/constraints/test_constraints.py",
|
|
38
|
+
"tests/core/test_engine.py",
|
|
39
|
+
"tests/domain/test_observer.py",
|
|
40
|
+
"tests/domain/test_orbit.py",
|
|
41
|
+
"tests/domain/test_planner_domain.py",
|
|
42
|
+
"tests/domain/test_required_fields.py",
|
|
43
|
+
"tests/domain/test_time.py",
|
|
44
|
+
"tests/domain/test_window.py",
|
|
45
|
+
"tests/helpers.py",
|
|
46
|
+
"tests/integration/test_fake_tle_integration.py",
|
|
47
|
+
"tests/planning/test_planner.py",
|
|
48
|
+
"tests/propagation/test_propagator.py",
|
|
49
|
+
"tests/test_tle.py",
|
|
50
|
+
"uv.lock"
|
|
51
51
|
]
|
|
52
52
|
}
|
|
@@ -82,12 +82,15 @@ class TestFakeTleIntegration:
|
|
|
82
82
|
TLE_EPOCH + timedelta(hours=3),
|
|
83
83
|
)
|
|
84
84
|
passes = find_passes(satellite, observer, time_range)
|
|
85
|
+
# Use a pass away from zenith with a gradual azimuth sweep.
|
|
85
86
|
thepass = next(
|
|
86
87
|
thepass
|
|
87
88
|
for thepass in passes
|
|
88
|
-
if thepass.
|
|
89
|
+
if thepass.max_altitude_sample.altitude_deg < 80.0
|
|
90
|
+
and thepass.start_sample.azimuth_deg < 260.0
|
|
91
|
+
and thepass.end_sample.azimuth_deg > 300.0
|
|
89
92
|
)
|
|
90
|
-
constraint = AzimuthWindowConstraint(az_start=
|
|
93
|
+
constraint = AzimuthWindowConstraint(az_start=260.0, az_end=300.0)
|
|
91
94
|
context = ConstraintContext(satellite=satellite, observer=observer)
|
|
92
95
|
|
|
93
96
|
constrained = constraint.apply(thepass, context=context)
|
|
@@ -95,5 +98,5 @@ class TestFakeTleIntegration:
|
|
|
95
98
|
assert constrained.constraint_applied_result == ConstraintAppliedResult.MODIFIED
|
|
96
99
|
assert constrained.start_sample.instant > thepass.start_sample.instant
|
|
97
100
|
assert constrained.end_sample.instant < thepass.end_sample.instant
|
|
98
|
-
assert
|
|
99
|
-
assert
|
|
101
|
+
assert 260.0 <= constrained.start_sample.azimuth_deg <= 300.0
|
|
102
|
+
assert 260.0 <= constrained.end_sample.azimuth_deg <= 300.0
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from skyfield.api import EarthSatellite, load # type: ignore[import-untyped]
|
|
7
|
+
|
|
8
|
+
from satnogs_predict import generate_fake_tle
|
|
9
|
+
from satnogs_predict.tle import FakeTLEParameters
|
|
10
|
+
|
|
11
|
+
ts = load.timescale()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _checksum(line_without_checksum: str) -> int:
|
|
15
|
+
total = 0
|
|
16
|
+
for char in line_without_checksum:
|
|
17
|
+
if char.isdigit():
|
|
18
|
+
total += int(char)
|
|
19
|
+
elif char == "-":
|
|
20
|
+
total += 1
|
|
21
|
+
return total % 10
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TestGenerateFakeTle:
|
|
25
|
+
@pytest.mark.parametrize(
|
|
26
|
+
("date_iso", "expected_epoch"),
|
|
27
|
+
[
|
|
28
|
+
("2026-10-01T12:00:00+00:00", "26274.50000000"),
|
|
29
|
+
("2006-01-01T00:00:00+00:00", "06001.00000000"),
|
|
30
|
+
("1999-12-31T12:00:00+00:00", "99365.50000000"),
|
|
31
|
+
("2024-02-29T06:00:00+00:00", "24060.25000000"),
|
|
32
|
+
("2026-01-01T01:00:00+02:00", "25365.95833333"),
|
|
33
|
+
],
|
|
34
|
+
)
|
|
35
|
+
def test_given_aware_date_when_generated_then_preserves_epoch(
|
|
36
|
+
self, date_iso: str, expected_epoch: str
|
|
37
|
+
) -> None:
|
|
38
|
+
date = datetime.fromisoformat(date_iso)
|
|
39
|
+
tle = generate_fake_tle(latitude=37.9838, longitude=23.7275, date=date)
|
|
40
|
+
|
|
41
|
+
assert tle.line1[18:32] == expected_epoch
|
|
42
|
+
sat = EarthSatellite(tle.line1, tle.line2, tle.line0, ts)
|
|
43
|
+
assert abs((sat.epoch.utc_datetime() - date).total_seconds()) < 0.001
|
|
44
|
+
|
|
45
|
+
def test_given_valid_input_when_generated_then_returns_parseable_tle(self) -> None:
|
|
46
|
+
tle = generate_fake_tle(
|
|
47
|
+
latitude=37.9838,
|
|
48
|
+
longitude=23.7275,
|
|
49
|
+
date=datetime(2026, 3, 6, 12, 0, 0, tzinfo=timezone.utc),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
assert len(tle.line1) == 69
|
|
53
|
+
assert len(tle.line2) == 69
|
|
54
|
+
assert tle.line1[-1] == str(_checksum(tle.line1[:-1]))
|
|
55
|
+
assert tle.line2[-1] == str(_checksum(tle.line2[:-1]))
|
|
56
|
+
|
|
57
|
+
sat = EarthSatellite(tle.line1, tle.line2, tle.line0, ts)
|
|
58
|
+
assert sat.name == tle.line0.strip()
|
|
59
|
+
|
|
60
|
+
def test_given_same_input_when_generated_twice_then_result_is_deterministic(self) -> None:
|
|
61
|
+
date = datetime(2026, 3, 6, 12, 0, 0, tzinfo=timezone.utc)
|
|
62
|
+
|
|
63
|
+
tle1 = generate_fake_tle(latitude=10.0, longitude=20.0, date=date)
|
|
64
|
+
tle2 = generate_fake_tle(latitude=10.0, longitude=20.0, date=date)
|
|
65
|
+
|
|
66
|
+
assert tle1 == tle2
|
|
67
|
+
|
|
68
|
+
def test_given_naive_date_when_generated_then_raises(self) -> None:
|
|
69
|
+
with pytest.raises(ValueError, match="timezone-aware"):
|
|
70
|
+
generate_fake_tle(
|
|
71
|
+
latitude=0.0,
|
|
72
|
+
longitude=0.0,
|
|
73
|
+
date=datetime(2026, 3, 6, 12, 0, 0),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
@pytest.mark.parametrize(
|
|
77
|
+
("catalog_num", "expected_field"),
|
|
78
|
+
[
|
|
79
|
+
(0, "00000"),
|
|
80
|
+
(25544, "25544"),
|
|
81
|
+
(99999, "99999"),
|
|
82
|
+
(100000, "A0000"),
|
|
83
|
+
(148493, "E8493"),
|
|
84
|
+
(182345, "J2345"),
|
|
85
|
+
(234567, "P4567"),
|
|
86
|
+
(339999, "Z9999"),
|
|
87
|
+
],
|
|
88
|
+
)
|
|
89
|
+
def test_given_catalog_num_when_generated_then_encodes_alpha5(
|
|
90
|
+
self, catalog_num: int, expected_field: str
|
|
91
|
+
) -> None:
|
|
92
|
+
tle = generate_fake_tle(
|
|
93
|
+
latitude=0.0,
|
|
94
|
+
longitude=0.0,
|
|
95
|
+
date=datetime(2026, 3, 6, 12, 0, 0, tzinfo=timezone.utc),
|
|
96
|
+
params=FakeTLEParameters(catalog_num=catalog_num),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
assert tle.line1[2:7] == expected_field
|
|
100
|
+
assert tle.line2[2:7] == expected_field
|
|
101
|
+
sat = EarthSatellite(tle.line1, tle.line2, tle.line0, ts)
|
|
102
|
+
assert sat.model.satnum == catalog_num
|
|
103
|
+
|
|
104
|
+
@pytest.mark.parametrize("catalog_num", [-1, 340000, 1000000])
|
|
105
|
+
def test_given_out_of_range_catalog_num_when_generated_then_raises(
|
|
106
|
+
self, catalog_num: int
|
|
107
|
+
) -> None:
|
|
108
|
+
with pytest.raises(ValueError, match="catalog_num"):
|
|
109
|
+
generate_fake_tle(
|
|
110
|
+
latitude=0.0,
|
|
111
|
+
longitude=0.0,
|
|
112
|
+
date=datetime(2026, 3, 6, 12, 0, 0, tzinfo=timezone.utc),
|
|
113
|
+
params=FakeTLEParameters(catalog_num=catalog_num),
|
|
114
|
+
)
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from datetime import datetime, timezone
|
|
4
|
-
|
|
5
|
-
import pytest
|
|
6
|
-
from skyfield.api import EarthSatellite, load # type: ignore[import-untyped]
|
|
7
|
-
|
|
8
|
-
from satnogs_predict import generate_fake_tle
|
|
9
|
-
|
|
10
|
-
ts = load.timescale()
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def _checksum(line_without_checksum: str) -> int:
|
|
14
|
-
total = 0
|
|
15
|
-
for char in line_without_checksum:
|
|
16
|
-
if char.isdigit():
|
|
17
|
-
total += int(char)
|
|
18
|
-
elif char == "-":
|
|
19
|
-
total += 1
|
|
20
|
-
return total % 10
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class TestGenerateFakeTle:
|
|
24
|
-
def test_given_valid_input_when_generated_then_returns_parseable_tle(self) -> None:
|
|
25
|
-
tle = generate_fake_tle(
|
|
26
|
-
latitude=37.9838,
|
|
27
|
-
longitude=23.7275,
|
|
28
|
-
date=datetime(2026, 3, 6, 12, 0, 0, tzinfo=timezone.utc),
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
assert len(tle.line1) == 69
|
|
32
|
-
assert len(tle.line2) == 69
|
|
33
|
-
assert tle.line1[-1] == str(_checksum(tle.line1[:-1]))
|
|
34
|
-
assert tle.line2[-1] == str(_checksum(tle.line2[:-1]))
|
|
35
|
-
|
|
36
|
-
sat = EarthSatellite(tle.line1, tle.line2, tle.line0, ts)
|
|
37
|
-
assert sat.name == tle.line0.strip()
|
|
38
|
-
|
|
39
|
-
def test_given_same_input_when_generated_twice_then_result_is_deterministic(self) -> None:
|
|
40
|
-
date = datetime(2026, 3, 6, 12, 0, 0, tzinfo=timezone.utc)
|
|
41
|
-
|
|
42
|
-
tle1 = generate_fake_tle(latitude=10.0, longitude=20.0, date=date)
|
|
43
|
-
tle2 = generate_fake_tle(latitude=10.0, longitude=20.0, date=date)
|
|
44
|
-
|
|
45
|
-
assert tle1 == tle2
|
|
46
|
-
|
|
47
|
-
def test_given_naive_date_when_generated_then_raises(self) -> None:
|
|
48
|
-
with pytest.raises(ValueError, match="timezone-aware"):
|
|
49
|
-
generate_fake_tle(
|
|
50
|
-
latitude=0.0,
|
|
51
|
-
longitude=0.0,
|
|
52
|
-
date=datetime(2026, 3, 6, 12, 0, 0),
|
|
53
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{satnogs_predict-1.0 → satnogs_predict-1.2}/src/satnogs_predict.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|