From 655ae8cd83cd9431ed8ef18a52e3e4e34d7ff065 Mon Sep 17 00:00:00 2001 From: Ryan Ridden Date: Thu, 6 Aug 2026 10:45:25 +1200 Subject: [PATCH 1/3] Remove stray tracked files and stale MANIFEST.in entry tessreduce/#__init__.py# (an editor backup file) and tessreduce/web path were accidentally committed and aren't part of the package. calspec_mags.npy is listed in MANIFEST.in but no longer exists on disk. None of these are referenced by any code. --- MANIFEST.in | 1 - tessreduce/#__init__.py# | 1 - tessreduce/web path | 2 -- 3 files changed, 4 deletions(-) delete mode 100755 tessreduce/#__init__.py# delete mode 100755 tessreduce/web path diff --git a/MANIFEST.in b/MANIFEST.in index 3fd346e..b513324 100755 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,5 @@ include README.md LICENSE include tessreduce/tess_straps.csv -include tessreduce/calspec_mags.npy include tessreduce/Tonry_splines.txt include tessreduce/SMspline.txt include tessreduce/sector_mjd.csv diff --git a/tessreduce/#__init__.py# b/tessreduce/#__init__.py# deleted file mode 100755 index 0da3e83..0000000 --- a/tessreduce/#__init__.py# +++ /dev/null @@ -1 +0,0 @@ -from .tessreduce import * \ No newline at end of file diff --git a/tessreduce/web path b/tessreduce/web path deleted file mode 100755 index 3b9d168..0000000 --- a/tessreduce/web path +++ /dev/null @@ -1,2 +0,0 @@ -web path -/grp/websites/stsci-transients.stsci.edu/ \ No newline at end of file From 34b8a9d5c2df7b26c2442f71df27659d47d65738 Mon Sep 17 00:00:00 2001 From: Ryan Ridden Date: Thu, 6 Aug 2026 11:08:17 +1200 Subject: [PATCH 2/3] Replace tess-point and sector_mjd.csv with tesswcs sector_mjd.csv required manual updates every time new sectors were scheduled (most recently extended through sector 107 by hand). It's replaced by tesswcs.pointings, which ships an actively maintained sector pointing table (currently covering archived and predicted sectors through 121) so this no longer needs repo maintenance. Also replaces tess_stars2px_function_entry (the tess-point package) with a tesswcs-based sector/camera/CCD lookup (WCS.from_sector + footprint_contains), since tesswcs supersedes tess-point and this avoids depending on both. Verified against tess-point's output for a reference target (same sectors/camera/CCD, pixel coords within ~2px). Adds tests for the two new helpers (_tess_pointing_table, _target_sectors) covering table structure, a known sector's start time, expected sector matches for a reference target, and array alignment/pixel bounds. --- MANIFEST.in | 1 - setup.py | 2 +- tessreduce/helpers.py | 88 +++++++++++++++++++++++++------ tessreduce/sector_mjd.csv | 108 -------------------------------------- tests/test_helpers.py | 40 ++++++++++++++ 5 files changed, 112 insertions(+), 127 deletions(-) delete mode 100755 tessreduce/sector_mjd.csv diff --git a/MANIFEST.in b/MANIFEST.in index b513324..85c57a5 100755 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,4 +2,3 @@ include README.md LICENSE include tessreduce/tess_straps.csv include tessreduce/Tonry_splines.txt include tessreduce/SMspline.txt -include tessreduce/sector_mjd.csv diff --git a/setup.py b/setup.py index 9f277f7..43327b7 100755 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ 'sep', 'tqdm', 'alerce', - 'tess-point', + 'tesswcs', 'tabulate', 'TESS_PRF'] diff --git a/tessreduce/helpers.py b/tessreduce/helpers.py index c911f85..57892c5 100644 --- a/tessreduce/helpers.py +++ b/tessreduce/helpers.py @@ -38,7 +38,7 @@ from photutils.detection import StarFinder from PRF import TESS_PRF -from tess_stars2px import tess_stars2px_function_entry as focal_plane +import tesswcs from tabulate import tabulate package_directory = os.path.dirname(os.path.abspath(__file__)) + '/' @@ -520,6 +520,62 @@ def grad_flux_rad(flux): return rad +def _tess_pointing_table(): + """ + Sector start/end times (MJD), sourced from tesswcs.pointings so the table + stays current with tesswcs releases rather than a file pinned in this repo. + + Returns + ------- + sec_times : pd.DataFrame + Indexed by Sector, with mjd_start and mjd_end columns. + """ + pointings = tesswcs.pointings.to_pandas()[['Sector','Start','End']] + pointings = pointings.rename(columns={'Start':'mjd_start','End':'mjd_end'}) + pointings['mjd_start'] = Time(pointings['mjd_start'].values,format='jd').mjd + pointings['mjd_end'] = Time(pointings['mjd_end'].values,format='jd').mjd + return pointings.set_index('Sector').sort_index() + + +def _target_sectors(ra,dec): + """ + Find which TESS sectors, cameras and CCDs observe a given coordinate. + Replaces tess_stars2px_function_entry (tess-point) with tesswcs, which + covers both archived and predicted sector pointings. + + Returns + ------- + outSecs, outCam, outCcd, outColPix, outRowPix : np.array + Sector number, camera, CCD, and pixel column/row for each match, + sorted by sector. + """ + import logging + coord = SkyCoord(ra,dec,unit='deg') + + level = tesswcs.log.level + tesswcs.log.setLevel(logging.ERROR) + secs, cams, ccds, cols, rows = [], [], [], [], [] + try: + for sector in tesswcs.pointings['Sector']: + sector = int(sector) + for camera in range(1,5): + for ccd in range(1,5): + try: + wcs = tesswcs.WCS.from_sector(sector,camera,ccd) + except ValueError: + continue + if wcs.footprint_contains(coord): + col, row = wcs.world_to_pixel(coord) + secs += [sector]; cams += [camera]; ccds += [ccd] + cols += [float(col)]; rows += [float(row)] + finally: + tesswcs.log.setLevel(level) + + order = np.argsort(secs) + return (np.array(secs)[order], np.array(cams)[order], np.array(ccds)[order], + np.array(cols)[order], np.array(rows)[order]) + + def sn_lookup(name,time='disc',buffer=0,print_table=True, df = False): """ Check for overlapping TESS ovservations for a transient. Uses the Open SNe Catalog for @@ -589,16 +645,15 @@ def sn_lookup(name,time='disc',buffer=0,print_table=True, df = False): ra = c.ra.deg dec = c.dec.deg - outID, outEclipLong, outEclipLat, outSecs, outCam, outCcd, outColPix, \ - outRowPix, scinfo = focal_plane(0, ra, dec) - - sec_times = pd.read_csv(package_directory + 'sector_mjd.csv') - if len(outSecs) > 0: - ind = outSecs - 1 + outSecs, outCam, outCcd, outColPix, outRowPix = _target_sectors(ra, dec) - new_ind = [i for i in ind if i < len(sec_times)] + sec_times = _tess_pointing_table() + if len(outSecs) > 0: + keep = np.isin(outSecs, sec_times.index) + outSecs, outCam, outCcd, outColPix, outRowPix = \ + outSecs[keep], outCam[keep], outCcd[keep], outColPix[keep], outRowPix[keep] - secs = sec_times.iloc[new_ind] + secs = sec_times.loc[outSecs].reset_index() if type(time) == str: if (time.lower() == 'disc') | (time.lower() == 'discovery'): disc_start = secs['mjd_start'].values - disc_t.mjd @@ -679,16 +734,15 @@ def spacetime_lookup(ra,dec,time=None,buffer=0,print_table=True, df = False, pri ra = c.ra.deg dec = c.dec.deg - outID, outEclipLong, outEclipLat, outSecs, outCam, outCcd, outColPix, \ - outRowPix, scinfo = focal_plane(0, ra, dec) - - sec_times = pd.read_csv(package_directory + 'sector_mjd.csv') - if len(outSecs) > 0: - ind = outSecs - 1 + outSecs, outCam, outCcd, outColPix, outRowPix = _target_sectors(ra, dec) - new_ind = [i for i in ind if i < len(sec_times)] + sec_times = _tess_pointing_table() + if len(outSecs) > 0: + keep = np.isin(outSecs, sec_times.index) + outSecs, outCam, outCcd, outColPix, outRowPix = \ + outSecs[keep], outCam[keep], outCcd[keep], outColPix[keep], outRowPix[keep] - secs = sec_times.iloc[new_ind] + secs = sec_times.loc[outSecs].reset_index() disc_start = secs['mjd_start'].values - time disc_end = secs['mjd_end'].values - time diff --git a/tessreduce/sector_mjd.csv b/tessreduce/sector_mjd.csv deleted file mode 100755 index 2ca36ed..0000000 --- a/tessreduce/sector_mjd.csv +++ /dev/null @@ -1,108 +0,0 @@ -Sector,mjd_start,mjd_end -1,58324.81597222222,58352.68402777778 -2,58353.60763888889,58381.020833333336 -3,58382.225694444445,58408.875 -4,58410.40625,58436.35763888889 -5,58437.48263888889,58463.79513888889 -6,58464.711805555555,58489.552083333336 -7,58491.131944444445,58515.59375 -8,58516.84722222222,58541.506944444445 -9,58542.72222222222,58567.98263888889 -10,58568.9375,58595.1875 -11,58596.27777777778,58623.399305555555 -12,58624.45486111111,58652.399305555555 -13,58653.42013888889,58681.864583333336 -14,58682.854166666664,58709.711805555555 -15,58710.864583333336,58736.916666666664 -16,58738.15277777778,58762.82638888889 -17,58764.18402777778,58789.20138888889 -18,58790.15625,58814.538194444445 -19,58815.583333333336,58840.65625 -20,58842.00347222222,58868.32986111111 -21,58869.93402777778,58897.288194444445 -22,58898.805555555555,58926.0 -23,58927.604166666664,58954.381944444445 -24,58955.29513888889,58981.788194444445 -25,58983.131944444445,59008.8125 -26,59009.76736111111,59034.64236111111 -27,59035.77777777778,59060.149305555555 -28,59061.350694444445,59086.604166666664 -29,59087.739583333336,59113.94097222222 -30,59115.385416666664,59142.729166666664 -31,59144.01388888889,59171.53472222222 -32,59172.572916666664,59199.739583333336 -33,59201.23263888889,59227.07986111111 -34,59228.25,59253.572916666664 -35,59254.489583333336,59279.48611111111 -36,59280.40277777778,59305.49652777778 -37,59306.739583333336,59332.086805555555 -38,59333.354166666664,59360.05902777778 -39,59361.270833333336,59389.225694444445 -40,59390.15277777778,59418.36111111111 -41,59419.489583333336,59446.086805555555 -42,59447.19097222222,59472.666666666664 -43,59473.666666666664,59498.395833333336 -44,59499.6875,59523.947916666664 -45,59525.006944444445,59550.131944444445 -46,59551.06597222222,59578.27777777778 -47,59579.302083333336,59606.447916666664 -48,59607.43402777778,59635.493055555555 -49,59636.97222222222,59663.822916666664 -50,59664.770833333336,59691.01736111111 -51,59692.447916666664,59717.041666666664 -52,59718.135416666664,59742.583333333336 -53,59743.49652777778,59768.48611111111 -54,59769.399305555555,59795.635416666664 -55,59796.600694444445,59823.770833333336 -56,59824.756944444445,59852.645833333336 -57,59852.854166666664,59881.62152777778 -58,59881.82986111111,59909.555555555555 -59,59909.76388888889,59936.194444444445 -60,59936.40277777778,59962.09027777778 -61,59962.29861111111,59987.73263888889 -62,59987.94097222222,60013.65972222222 -63,60013.868055555555,60040.40625 -64,60040.614583333336,60068.03125 -65,60068.239583333336,60096.96527777778 -66,60097.17361111111,60125.93402777778 -67,60126.14236111111,60153.90277777778 -68,60154.11111111111,60181.645833333336 -69,60181.854166666664,60207.645833333336 -70,60207.854166666664,60233.34375 -71,60233.53472222222,60259.475694444445 -72,60259.68402777778,60285.09027777778 -73,60285.29861111111,60312.15625 -74,60312.364583333336,60339.07638888889 -75,60339.28472222222,60366.989583333336 -76,60367.197916666664,60394.77777777778 -77,60394.98611111111,60423.055555555555 -78,60423.26388888889,60451.82986111111 -79,60452.038194444445,60479.180555555555 -80,60479.38888888889,60505.84375 -81,60506.052083333336,60532.68402777778 -82,60532.89236111111,60558.75347222222 -83,60558.927083333336,60583.881944444445 -84,60584.09027777778,60609.84722222222 -85,60610.055555555555,60635.552083333336 -86,60635.760416666664,60662.333333333336 -87,60662.541666666664,60689.444444444445 -88,60689.65277777778,60717.430555555555 -89,60717.63888888889,60746.45138888889 -90,60746.65972222222,60774.583333333336 -91,60774.791666666664,60802.26388888889 -92,60802.47222222222,60829.149305555555 -93,60829.35763888889,60855.555555555555 -94,60855.76388888889,60881.625 -95,60881.833333333336,60907.06597222222 -96,60907.274305555555,60932.95138888889 -97,60933.15972222222,60987.791666666664 -98,60988.0,61045.40625 -99,61045.614583333336,61073.28472222222 -100,61073.493055555555,61099.97222222222 -101,61100.180555555555,61126.3125 -102,61126.520833333336,61164.729166666664 -103,61164.9375,61177.87847222222 -104,61178.086805555555,61204.708333333336 -105,61204.916666666664,61232.72222222222 -106,61232.930555555555,61261.57638888889 -107,61261.78472222222,61290.28125 diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 967fc99..4471309 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -22,6 +22,8 @@ smooth_zp, Smooth_bkg, regional_stats_mask, + _tess_pointing_table, + _target_sectors, ) @@ -315,5 +317,43 @@ def test_outlier_masked(self): self.assertTrue(mask[15, 15]) +class TestTessPointingTable(unittest.TestCase): + + def test_indexed_by_sector_with_expected_columns(self): + table = _tess_pointing_table() + self.assertEqual(table.index.name, 'Sector') + self.assertIn('mjd_start', table.columns) + self.assertIn('mjd_end', table.columns) + + def test_end_after_start(self): + table = _tess_pointing_table() + self.assertTrue((table['mjd_end'] > table['mjd_start']).all()) + + def test_known_sector_one_start_time(self): + # Sector 1 start is 2018-07-25 19:00 UT, JD 2458324.5 -> MJD 58324.0 + table = _tess_pointing_table() + self.assertAlmostEqual(table.loc[1, 'mjd_start'], 58324.0, places=3) + + +class TestTargetSectors(unittest.TestCase): + + def test_known_target_returns_expected_sectors(self): + # Reference target cross-checked against tess_stars2px_function_entry + # (tess-point) output: sectors 2, 29, 69, 96, 103, 104, 105, 106. + outSecs, outCam, outCcd, outColPix, outRowPix = _target_sectors(10.127, -50.687) + expected = {2, 29, 69, 96, 103, 104, 105, 106} + self.assertTrue(expected.issubset(set(outSecs.tolist()))) + + def test_arrays_aligned_and_sorted(self): + outSecs, outCam, outCcd, outColPix, outRowPix = _target_sectors(10.127, -50.687) + lengths = {len(outSecs), len(outCam), len(outCcd), len(outColPix), len(outRowPix)} + self.assertEqual(len(lengths), 1) + assert_array_equal(outSecs, np.sort(outSecs)) + + def test_pixel_coordinates_within_ccd_bounds(self): + outSecs, outCam, outCcd, outColPix, outRowPix = _target_sectors(10.127, -50.687) + self.assertTrue(np.all((outColPix >= 0) & (outColPix <= 2136))) + self.assertTrue(np.all((outRowPix >= 0) & (outRowPix <= 2078))) + if __name__ == '__main__': unittest.main() From 5a67756fd238373be019e113817fc7324e5fe555 Mon Sep 17 00:00:00 2001 From: Ryan Ridden Date: Thu, 6 Aug 2026 11:20:05 +1200 Subject: [PATCH 3/3] Fix stale trove classifiers, pin tesswcs, and use absolute image URLs Classifiers listed Python 3.6 despite python_requires>=3.9 and no newer versions; replaced with 3.9-3.12 and dropped the untested PyPy classifier. Pinned tesswcs>=1.8 since the sector lookup relies on WCS.from_sector/footprint_contains, both confirmed present at 1.8.15. README image links were relative paths that render broken on PyPI's project page (which doesn't rewrite them like GitHub does); switched to raw.githubusercontent.com URLs. --- README.md | 6 +++--- setup.py | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 30c547f..17c6e4b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![DOI](https://img.shields.io/badge/DOI-10.3847%2F1538--3881%2Fac2c2e-blue.svg)](https://doi.org/10.3847/1538-3881/ac2c2e) -![plot](./figs/header.png) +![plot](https://raw.githubusercontent.com/CheerfulUser/TESSreduce/main/figs/header.png) With this package that builds on lightkurve, you can reduce TESS data while preserving transient signals. You can supply a TPF or give coordinates and sector to construct a TPF with TESScut. The background subtraction accounts for the smooth background and detector straps. Alongisde background subtraction TESSreduce also aligns images, performs difference imaging, and can even detect transient events! @@ -29,7 +29,7 @@ obs = tr.sn_lookup('sn2018fub') ```python tess = tr.tessreduce(obs_list=obs) ``` -![plot](./figs/fub.png) +![plot](https://raw.githubusercontent.com/CheerfulUser/TESSreduce/main/figs/fub.png) **OR** ```python @@ -75,7 +75,7 @@ Several options are available for flux and are interchangeable, however, mag is ```python tess.plotter() ``` -![plot](./figs/fub_cal.png) +![plot](https://raw.githubusercontent.com/CheerfulUser/TESSreduce/main/figs/fub_cal.png) # Extracting key variables diff --git a/setup.py b/setup.py index 43327b7..56ab907 100755 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ 'sep', 'tqdm', 'alerce', - 'tesswcs', + 'tesswcs>=1.8', 'tabulate', 'TESS_PRF'] @@ -137,9 +137,11 @@ def run(self): 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: Implementation :: CPython', - 'Programming Language :: Python :: Implementation :: PyPy' ], # $ setup.py publish support. cmdclass={