diff --git a/.github/workflows/release-wheels.yml b/.github/workflows/release-wheels.yml index b1cb8bf..166b9a0 100644 --- a/.github/workflows/release-wheels.yml +++ b/.github/workflows/release-wheels.yml @@ -5,12 +5,12 @@ name: Release-Wheels on: release: - types: + types: - published push: - tags: 'v[0-9]+.[0-9]+.[0-9]+' + tags: "v[0-9]+.[0-9]+.[0-9]+" workflow_dispatch: - + concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true @@ -40,9 +40,19 @@ jobs: platform_id: macosx_arm64 steps: - uses: actions/checkout@v4 + - name: Cache downloaded files + id: cache-kete + uses: actions/cache@v4 + env: + cache-name: cache-kete + with: + # kete stores all files in `~/.kete/` by default + path: ~/.kete + # These files are valid long term, and are not expected to change + # between runs. So there is no need to a changing name. + key: kete-cache - name: Set up Rust uses: dtolnay/rust-toolchain@stable - - run: cargo test --all-features - name: Set up Python uses: actions/setup-python@v5 with: diff --git a/.github/workflows/test-lint.yml b/.github/workflows/test-lint.yml index d896612..7f6828d 100644 --- a/.github/workflows/test-lint.yml +++ b/.github/workflows/test-lint.yml @@ -5,12 +5,11 @@ name: Tests and Lint on: push: - branches: [ "main" ] + branches: ["main"] pull_request: - branches: [ "main" ] + branches: ["main"] env: - KETE_CACHE_DIR: ${{ github.workspace }}/docs/data RUSTFLAGS: "-D warnings" RUSTDOCFLAGS: "-D warnings" @@ -24,72 +23,96 @@ jobs: runs-on: ubuntu-latest strategy: fail-fast: false + steps: - - uses: actions/checkout@v4 - with: - lfs: true - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - name: Test with cargo - run: | - cargo test - - name: Cargo Docs - run: | - cargo doc - - name: Install dependencies - run: | - python3 -m pip install --upgrade pip setuptools black mypy types-requests numpy - - name: Black Formatting - run: | - python3 -m black . --check - - name: Lint with mypy - run: | - python3 -m mypy src/kete/ - - # Build and run pytest - - name: Build kete - run: | - python3 -m pip install '.[dev]' -v - - name: Test with pytest - run: | - python3 -m pytest --cov-report term-missing --cov=kete - - # Build documentation and push artifact - - name: Build Docs - run: | - cd docs - make clean - make doctest - make html - - name: Fix permissions - run: | - chmod -c -R +rX "docs/" | while read line; do - echo "::warning title=Invalid file permissions automatically fixed::$line" - done - - name: Upload Docs artifact - uses: actions/upload-artifact@v4 - with: - name: "docs" - path: - docs/html/ - - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v3 - with: - name: "github-pages" - path: - docs/html/ - - + - uses: actions/checkout@v4 + + - name: Cache downloaded files + id: cache-kete + uses: actions/cache@v4 + env: + cache-name: cache-kete + with: + # kete stores all files in `~/.kete/` by default + path: ~/.kete + # These files are valid long term, and are not expected to change + # between runs. So there is no need to a changing name. + key: kete-cache + + - name: Setup Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: stable + cache-key: ${{ runner.os }}-stable + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: | + python3 -m pip install --upgrade pip setuptools ruff mypy types-requests numpy + + - name: Ruff Formatting + run: | + python3 -m ruff check + + - name: Lint with mypy + run: | + python3 -m mypy src/kete/ + + # Build and run pytest + - name: Build kete + run: | + python3 -m pip install '.[dev]' -v + + - name: Test with pytest + run: | + python3 -m pytest --cov-report term-missing --cov=kete + + - name: Test with cargo + run: | + cargo test + + - name: Cargo Docs + run: | + cargo doc + + # Build documentation and push artifact + - name: Build Docs + run: | + cd docs + make clean + make doctest + make html + + - name: Fix permissions + run: | + chmod -c -R +rX "docs/" | while read line; do + echo "::warning title=Invalid file permissions automatically fixed::$line" + done + + - name: Upload Docs artifact + uses: actions/upload-artifact@v4 + with: + name: "docs" + path: docs/html/ + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + name: "github-pages" + path: docs/html/ + deploy: needs: build if: success() && github.ref == 'refs/heads/main' # Grant GITHUB_TOKEN the permissions required to make a Pages deployment permissions: - pages: write # to deploy to Pages - id-token: write # to verify the deployment originates from an appropriate source + pages: write # to deploy to Pages + id-token: write # to verify the deployment originates from an appropriate source # Deploy to the github-pages environment environment: diff --git a/CHANGELOG.md b/CHANGELOG.md index dd08c34..dec18fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v1.1.1] + + ### Added + - Add in support for MPC Extended Packed format + ## [v1.1.0] Announcement: Author of Kete (Dar Dahlen) has left IPAC Caltech to begin a PhD at diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 2dce254..ddf0ed5 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -60,7 +60,7 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -ddahlen@ipac.caltech.edu. +aehler@ipac.caltech.edu. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the @@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within diff --git a/README.md b/README.md index a75ee2b..ba3c691 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,11 @@ for Cetaceans (Whales). ## Note: -The original version of this repo was developed at Caltech IPAC. This is a fork of that repo -where the work will continue as a personal project. Work done while at IPAC is of course owned -by Caltech, but all future work will have well marked as owned by Dar Dahlen & future contributors. +The original version of this repo was developed at Caltech IPAC. This +version includes additions made on a fork of this repo that was +continued as a personal project by the original author D. Dahlen. Work +done while at IPAC is of course owned by Caltech, but that additional +work will be marked as owned by Dar Dahlen & future contributors. License will remain as BSD-3 Clause to simplify things. ## Introduction diff --git a/pyproject.toml b/pyproject.toml index c8ef968..0392c71 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ readme = "README.md" authors = [ { name = "Dar Dahlen", email = "ddahlen@ipac.caltech.edu" }, { name = "Joe Masiero", email = "jmasiero@ipac.caltech.edu" }, + { name = "Annie Ehler", email = "aehler@ipac.caltech.edu" }, ] license = { text = "BSD" } requires-python = ">=3.9" diff --git a/src/examples/plot_comet.py b/src/examples/plot_comet.py index 3f294c1..674a3bc 100644 --- a/src/examples/plot_comet.py +++ b/src/examples/plot_comet.py @@ -33,7 +33,7 @@ # Grab frame information from this file jd = kete.Time(frame.header["OBSJD"], scaling="utc").jd -frame_wcs = WCS(frame.header) +frame_wcs = WCS(frame.header, minerr=1e-8) corners = [] dx, dy = frame_wcs.pixel_shape @@ -125,6 +125,8 @@ def plot_syndyne(wcs, state, fov, beta, back_days=90, day_step=1, **kwargs): shape = wcs.array_shape pix = [] for x, y in zip(*wcs.world_to_pixel_values(ras, decs)): + if not np.isfinite(x) or not np.isfinite(y): + continue pix.append([x, y]) plt.xlim(0, shape[0]) plt.ylim(0, shape[1]) @@ -159,6 +161,8 @@ def plot_synchrone( shape = wcs.array_shape pix = [] for x, y in zip(*wcs.world_to_pixel_values(ras, decs)): + if not np.isfinite(x) or not np.isfinite(y): + continue pix.append([x, y]) plt.xlim(0, shape[0]) plt.ylim(0, shape[1]) @@ -173,57 +177,63 @@ def plot_synchrone( # plot syndynes for beta in [0.002, 0.004, 0.01, 0.04, 0.2]: + try: + plot_syndyne( + wcs, + vis[0], + fov, + beta, + day_step=0.1, + lw=0.6, + c=(1, 0.0, 0.3), + label=f"{beta:0.2g}", + ) + except Exception as err: + print(f"failure in B={beta:f} syndyne plotting, {err}") +try: plot_syndyne( wcs, vis[0], fov, - beta, + 1, + back_days=10, day_step=0.1, lw=0.6, c=(1, 0.0, 0.3), - label=f"{beta:0.2g}", + label=f"{1:0.2g}", ) -plot_syndyne( - wcs, - vis[0], - fov, - 1, - back_days=10, - day_step=0.1, - lw=0.6, - c=(1, 0.0, 0.3), - label=f"{1:0.2g}", -) +except Exception as err: + print(f"failure in B=1 syndyne, {err}") # plot synchrones for days in [-10, -15, -20, -25]: + try: + plot_synchrone( + wcs, vis[0], fov, days, 0.1, ls="--", c=(0, 0.5, 1), lw=0.6, label=str(days) + ) + except Exception as err: + print(f"failure in {days:d} synchrone plotting {err}") +try: + plot_synchrone(wcs, vis[0], fov, -5, 0.8, ls="--", c=(0, 0.5, 1), lw=0.6, label=-5) +except Exception as err: + print(f"failure in -5 synchrone, {err}") + +try: plot_synchrone( wcs, vis[0], fov, - days, - 0.2, + 0.01, + 1200000.0, ls="--", + beta_steps=2000, c=(0, 0.5, 1), lw=0.6, - label=str(days), - beta_steps=2000, + label=0, ) -plot_synchrone(wcs, vis[0], fov, -5, 0.8, ls="--", c=(0, 0.5, 1), lw=0.6, label=-5) - -plot_synchrone( - wcs, - vis[0], - fov, - 0.01, - 1200000.0, - ls="--", - beta_steps=2000, - c=(0, 0.5, 1), - lw=0.6, - label=0, -) +except Exception as err: + print(f"Failure in 0 day synchrone {err}") # Fancy plotting of labels around the edge shape = wcs.array_shape @@ -256,7 +266,7 @@ def plot_synchrone( label_pos = [line._x[idx], 0 - offset] ha = "center" va = "bottom" - else: + elif edge == 3: label_pos = [line._x[idx], shape[0] + offset] ha = "center" va = "top" @@ -265,8 +275,10 @@ def plot_synchrone( ) # add vectors -plot_vectors(frame_wcs, vis[0], vis.fov, y=0.85) - +try: + plot_vectors(frame_wcs, vis[0], vis.fov, y=0.85) +except Exception as err: + print(f"Failure in vector plot, {err}") # for some reason astropy for this frame is plotting the y-axis inverted # this just un-inverts it. plt.gca().invert_yaxis() diff --git a/src/examples/plot_elevation.py b/src/examples/plot_elevation.py index c32c9b8..2068a62 100644 --- a/src/examples/plot_elevation.py +++ b/src/examples/plot_elevation.py @@ -124,12 +124,23 @@ max_elev = [] for idx, line in zip(np.argmax(np.array(elevation).T, axis=1), lines): max_elev.append(line._x[idx]) -labelLines(lines, xvals=max_elev, zorder=2.5) +try: + labelLines(lines, xvals=max_elev, zorder=2.5) +except Exception as e: + print("Problem with plotting labels on the elevation plot", e) line = plt.plot( dates, moon_elevation, label=f"Moon ({moon_frac:0.0%})", ls="--", c="k", lw=1 )[0] -labelLines([line], xvals=line._x[np.argmax(moon_elevation)], zorder=2.45, fontsize=8) +try: + labelLines( + [line], + xvals=line._x[np.argmax(moon_elevation)], + zorder=2.45, + fontsize=8, + ) +except Exception as e: + print("Problem with plotting labels on the elevation plot", e) plt.axvline(midnight, c="k", zorder=2) diff --git a/src/kete/mpc.py b/src/kete/mpc.py index 8f0291b..7f34575 100644 --- a/src/kete/mpc.py +++ b/src/kete/mpc.py @@ -34,7 +34,10 @@ "normalize_names", ] +# base 62 counter used by the MPC _mpc_hex = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +# base 25 counter used by the MPC for provisional designation counting +_mpc_cnt = "ABCDEFGHJKLMNOPQRSTUVWXYZ" logger = logging.getLogger(__name__) @@ -226,6 +229,24 @@ def unpack_provisional_designation(packed: str): return unpack_comet_designation(packed) if len(packed) != 7: raise ValueError("Packed designation is not correctly formatted.") + if packed[0] == "_": + # new MPC extended packed format - 2025-07-24 + year = "20" + str(_mpc_hex.index(packed[1])) + halfmonth = packed[2] + odometer = ( + 15501 + + 62**3 * _mpc_hex.index(packed[3]) + + 62**2 * _mpc_hex.index(packed[4]) + + 62**1 * _mpc_hex.index(packed[5]) + + _mpc_hex.index(packed[6]) + ) + return ( + year + + " " + + halfmonth + + _mpc_cnt[((odometer - 1) % 25)] + + str((odometer - 1) // 25) + ) if packed[:3] in ["PLS", "T1S", "T2S", "T3S"]: return packed[3:] + " " + packed[0] + "-" + packed[1] year = str(_mpc_hex.index(packed[0]) * 100 + int(packed[1:3])) @@ -258,19 +279,27 @@ def pack_provisional_designation(unpacked: str): year, designation = unpacked.split() if designation[:3] in ["P-L", "T-1", "T-2", "T-3"]: return designation[0] + designation[2] + "S" + year - order = designation[1] + half_month = designation[0] if order.isnumeric() or "/" in unpacked: # its a comet return pack_comet_designation(unpacked) else: num = 0 if len(designation) == 2 else int(designation[2:]) + if num > 619: + # use MPC extended packed format + odometer = (_mpc_cnt.index(order) + 1) + num * 25 + hex_odo = odometer - 15501 + extpackout = "" + for ii in range(4): + extpackout = _mpc_hex[hex_odo % 62] + extpackout + hex_odo = hex_odo // 62 + return "_" + _mpc_hex[int(year) % 100] + half_month + extpackout loop = _mpc_hex[int(num / 10)] subloop = str(int(num % 10)) year_lookup = {"18": "I", "19": "J", "20": "K", "A9": "J", "A8": "I"} century = year_lookup[year[:2]] decade = year[2:] - half_month = designation[0] return century + decade + half_month + loop + subloop + order diff --git a/src/kete_core/data/wise.bsp b/src/kete_core/data/wise.bsp new file mode 100644 index 0000000..64a5476 Binary files /dev/null and b/src/kete_core/data/wise.bsp differ diff --git a/src/tests/test_mpc.py b/src/tests/test_mpc.py index 4011e9f..f70aad4 100644 --- a/src/tests/test_mpc.py +++ b/src/tests/test_mpc.py @@ -46,6 +46,15 @@ ["J18W010", "1918 W1"], ["J51G020", "1951 G2"], ["K16J01b", "2016 J1-B"], + # extended provisional packing + ["K23B00A", "2023 BA"], + ["K24C03Z", "2024 CZ3"], + ["K25Dz9Z", "2025 DZ619"], + ["_PD0000", "2025 DA620"], + ["_QD000N", "2026 DY620"], + ["_RD0aEM", "2027 DZ6190"], + ["_SEZZZZ", "2028 EA339749"], + ["_TFzzzz", "2029 FL591673"], ], ) def test_provisional(packed, unpacked):