Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,13 @@ jobs:

- name: Run Python tests
run: pytest python/test/

# Verify the hand-written .pyi stubs still match the compiled bindings.
# --ignore-positional-only / --ignore-disjoint-bases switch off two whole
# PyO3-idiom categories; the rest is handled by the allowlist.
- name: Check type stubs match bindings (stubtest)
run: |
python -m mypy.stubtest satkit \
--allowlist python/stubtest_allowlist.txt \
--ignore-positional-only \
--ignore-disjoint-bases
50 changes: 50 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,56 @@
# Changelog


## Unreleased

### Added

- **`SGP4InitArgs::from_mean_elements`** — a constructor that performs the
rev/day → rad/min and degree → radian conversions from catalog units. Both
the `TLE` and CCSDS `OMM` SGP4 sources now build their init args through it,
so the conversion factors are defined in one place.
- **`time` and `duration` are now hashable.** Both define `__eq__` but
previously lacked `__hash__`, which made them unhashable (unusable as `dict`
keys or `set` members). The hash is derived from the underlying microsecond
count, so it is consistent with equality.
- **Equality and `repr` on more value types.** `TLE`, `kepler`, and `itrfcoord`
now implement `__eq__`; `TLE`, `kepler`, `satstate`, and `propsettings` now
implement `__repr__` (delegating to their `str` form). The three float-backed
types that gained `__eq__` are intentionally left unhashable — a failed
`hash()` is clearer than silently-wrong float-keyed lookups.
- **`mypy.stubtest` in CI.** The `python bindings test` job now verifies that
the hand-written `.pyi` type stubs match the compiled PyO3 bindings. Two CLI
flags (`--ignore-positional-only`, `--ignore-disjoint-bases`) plus
`python/stubtest_allowlist.txt` suppress systematic PyO3 idioms (constructors,
final classes, native submodules); everything else must agree.

### Fixed

- **Type stubs now type-check and match the runtime.** The `.pyi` files had
never been checked and contained illegal overload-implementation blocks, a
`time.__add__` overload group split by other methods, and `time`-as-annotation
shadowing. Filled in stub gaps (`itrfcoord.height`, `time.add_utc_days`,
`weekday.Invalid`) and removed/fixed a phantom `time.as_gregorian(scale=)`
parameter and a mis-declared `sgp4_opsmode.improved` property.

### Changed

- **`itrfcoord(...)`, `sgp4(...)`, and `satstate.propagate(...)` now reject
unknown keyword arguments** instead of silently ignoring them. Previously a
typo such as `itrfcoord(..., alttiude=100)` was dropped, leaving the ground
station at 0 m altitude; it now raises `ValueError`. Callers passing only
documented keywords are unaffected.
- **Renamed keyword arguments on several bindings** so the accepted Python
keyword matches its documentation (the stubs previously advertised names the
runtime rejected). Positional calls are unaffected; only callers passing these
by the *old* keyword must update:
- `duration.from_hours` / `from_minutes` / `from_seconds`: `d` → `hours` / `minutes` / `seconds`
- `time.from_string`: `s` → `string`; `time.from_unixtime`: `t` → `unixtime`
- `time.from_rfc3339`: `s` → `rfc3339`; `time.from_datetime`: `tm` → `dt`
- `time.strftime`: `fmt` → `format`; `time.strptime`: `(s, fmt)` → `(date_string, format)`
- `kepler.from_pv`: `(r, v)` → `(pos, vel)`


## 0.20.2 - 2026-07-03

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ classifiers = [
"Programming Language :: Python :: 3.14",
]
[project.optional-dependencies]
test = ["pytest", "xmltodict"]
test = ["pytest", "xmltodict", "mypy"]

[tool.setuptools]
license-files = ["LICENSE-MIT", "LICENSE-APACHE"]
Expand Down
30 changes: 0 additions & 30 deletions python/satkit/density.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -70,33 +70,3 @@ def nrlmsise(
"""
...

def nrlmsise(*args, **kwargs):
"""
NRL MSISE-00 Atmosphere Density Model

<https://en.wikipedia.org/wiki/NRLMSISE-00>

or for more detail:
<https://agupubs.onlinelibrary.wiley.com/doi/full/10.1029/2002JA009430>

Args:

itrf (satkit.itrfcoord): position at which to compute density & temperature
time (satkit.time|numpy.ndarray|list): Optional instant(s) at which to compute density & temperature.
"Space weather" data at this time will be used in model
computation. Note: at satellite altitudes, density can
change by > 10 X depending on solar cycle

Returns:
(rho, T) where rho is mass density in kg/m^3 and T is temperature in Kelvin

Example:
```python
t = satkit.time(2024, 1, 1)
coord = satkit.itrfcoord(latitude_deg=0, longitude_deg=0, altitude=400e3)
rho, temp = satkit.density.nrlmsise(coord, t)
print(f"Density: {rho:.2e} kg/m^3")
print(f"Temperature: {temp:.1f} K")
```
"""
...
Loading
Loading