setiastrosuitepro 1.6.4__py3-none-any.whl → 1.6.12__py3-none-any.whl

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.

Potentially problematic release.


This version of setiastrosuitepro might be problematic. Click here for more details.

Files changed (115) hide show
  1. setiastro/images/abeicon.svg +16 -0
  2. setiastro/images/acv_icon.png +0 -0
  3. setiastro/images/colorwheel.svg +97 -0
  4. setiastro/images/cosmic.svg +40 -0
  5. setiastro/images/cosmicsat.svg +24 -0
  6. setiastro/images/first_quarter.png +0 -0
  7. setiastro/images/full_moon.png +0 -0
  8. setiastro/images/graxpert.svg +19 -0
  9. setiastro/images/last_quarter.png +0 -0
  10. setiastro/images/linearfit.svg +32 -0
  11. setiastro/images/new_moon.png +0 -0
  12. setiastro/images/pixelmath.svg +42 -0
  13. setiastro/images/waning_crescent_1.png +0 -0
  14. setiastro/images/waning_crescent_2.png +0 -0
  15. setiastro/images/waning_crescent_3.png +0 -0
  16. setiastro/images/waning_crescent_4.png +0 -0
  17. setiastro/images/waning_crescent_5.png +0 -0
  18. setiastro/images/waning_gibbous_1.png +0 -0
  19. setiastro/images/waning_gibbous_2.png +0 -0
  20. setiastro/images/waning_gibbous_3.png +0 -0
  21. setiastro/images/waning_gibbous_4.png +0 -0
  22. setiastro/images/waning_gibbous_5.png +0 -0
  23. setiastro/images/waxing_crescent_1.png +0 -0
  24. setiastro/images/waxing_crescent_2.png +0 -0
  25. setiastro/images/waxing_crescent_3.png +0 -0
  26. setiastro/images/waxing_crescent_4.png +0 -0
  27. setiastro/images/waxing_crescent_5.png +0 -0
  28. setiastro/images/waxing_gibbous_1.png +0 -0
  29. setiastro/images/waxing_gibbous_2.png +0 -0
  30. setiastro/images/waxing_gibbous_3.png +0 -0
  31. setiastro/images/waxing_gibbous_4.png +0 -0
  32. setiastro/images/waxing_gibbous_5.png +0 -0
  33. setiastro/qml/ResourceMonitor.qml +84 -82
  34. setiastro/saspro/__main__.py +20 -1
  35. setiastro/saspro/_generated/build_info.py +2 -2
  36. setiastro/saspro/abe.py +37 -4
  37. setiastro/saspro/aberration_ai.py +237 -21
  38. setiastro/saspro/acv_exporter.py +379 -0
  39. setiastro/saspro/add_stars.py +33 -6
  40. setiastro/saspro/backgroundneutral.py +108 -40
  41. setiastro/saspro/blemish_blaster.py +4 -1
  42. setiastro/saspro/blink_comparator_pro.py +74 -24
  43. setiastro/saspro/clahe.py +4 -1
  44. setiastro/saspro/continuum_subtract.py +4 -1
  45. setiastro/saspro/convo.py +13 -7
  46. setiastro/saspro/cosmicclarity.py +129 -18
  47. setiastro/saspro/crop_dialog_pro.py +123 -7
  48. setiastro/saspro/curve_editor_pro.py +109 -42
  49. setiastro/saspro/doc_manager.py +245 -15
  50. setiastro/saspro/exoplanet_detector.py +120 -28
  51. setiastro/saspro/frequency_separation.py +1158 -204
  52. setiastro/saspro/ghs_dialog_pro.py +81 -16
  53. setiastro/saspro/graxpert.py +1 -0
  54. setiastro/saspro/gui/main_window.py +429 -228
  55. setiastro/saspro/gui/mixins/dock_mixin.py +245 -24
  56. setiastro/saspro/gui/mixins/menu_mixin.py +27 -1
  57. setiastro/saspro/gui/mixins/theme_mixin.py +160 -14
  58. setiastro/saspro/gui/mixins/toolbar_mixin.py +384 -18
  59. setiastro/saspro/gui/mixins/update_mixin.py +138 -36
  60. setiastro/saspro/gui/mixins/view_mixin.py +42 -0
  61. setiastro/saspro/halobgon.py +4 -0
  62. setiastro/saspro/histogram.py +5 -1
  63. setiastro/saspro/image_combine.py +4 -0
  64. setiastro/saspro/image_peeker_pro.py +4 -0
  65. setiastro/saspro/imageops/starbasedwhitebalance.py +23 -52
  66. setiastro/saspro/imageops/stretch.py +582 -62
  67. setiastro/saspro/isophote.py +4 -0
  68. setiastro/saspro/layers.py +13 -9
  69. setiastro/saspro/layers_dock.py +183 -3
  70. setiastro/saspro/legacy/image_manager.py +154 -20
  71. setiastro/saspro/legacy/numba_utils.py +67 -47
  72. setiastro/saspro/legacy/xisf.py +240 -98
  73. setiastro/saspro/live_stacking.py +180 -79
  74. setiastro/saspro/luminancerecombine.py +228 -27
  75. setiastro/saspro/mask_creation.py +174 -15
  76. setiastro/saspro/mfdeconv.py +113 -35
  77. setiastro/saspro/mfdeconvcudnn.py +119 -70
  78. setiastro/saspro/mfdeconvsport.py +112 -35
  79. setiastro/saspro/morphology.py +4 -0
  80. setiastro/saspro/multiscale_decomp.py +51 -12
  81. setiastro/saspro/numba_utils.py +72 -57
  82. setiastro/saspro/ops/commands.py +18 -18
  83. setiastro/saspro/ops/script_editor.py +10 -2
  84. setiastro/saspro/ops/scripts.py +122 -0
  85. setiastro/saspro/perfect_palette_picker.py +37 -3
  86. setiastro/saspro/plate_solver.py +84 -49
  87. setiastro/saspro/psf_viewer.py +119 -37
  88. setiastro/saspro/resources.py +67 -0
  89. setiastro/saspro/rgbalign.py +4 -0
  90. setiastro/saspro/selective_color.py +4 -1
  91. setiastro/saspro/sfcc.py +364 -152
  92. setiastro/saspro/shortcuts.py +160 -29
  93. setiastro/saspro/signature_insert.py +692 -33
  94. setiastro/saspro/stacking_suite.py +1331 -484
  95. setiastro/saspro/star_alignment.py +247 -123
  96. setiastro/saspro/star_spikes.py +4 -0
  97. setiastro/saspro/star_stretch.py +38 -3
  98. setiastro/saspro/stat_stretch.py +743 -128
  99. setiastro/saspro/subwindow.py +786 -360
  100. setiastro/saspro/supernovaasteroidhunter.py +1 -1
  101. setiastro/saspro/wavescale_hdr.py +4 -1
  102. setiastro/saspro/wavescalede.py +4 -1
  103. setiastro/saspro/whitebalance.py +84 -12
  104. setiastro/saspro/widgets/common_utilities.py +28 -21
  105. setiastro/saspro/widgets/resource_monitor.py +109 -59
  106. setiastro/saspro/widgets/spinboxes.py +10 -13
  107. setiastro/saspro/wimi.py +27 -656
  108. setiastro/saspro/wims.py +13 -3
  109. setiastro/saspro/xisf.py +101 -11
  110. {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/METADATA +2 -1
  111. {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/RECORD +115 -82
  112. {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/WHEEL +0 -0
  113. {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/entry_points.txt +0 -0
  114. {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/licenses/LICENSE +0 -0
  115. {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/licenses/license.txt +0 -0
@@ -27,6 +27,7 @@ from astropy.coordinates import SkyCoord
27
27
  import astropy.units as u
28
28
  from astropy.wcs import WCS
29
29
  from astropy.timeseries import LombScargle, BoxLeastSquares
30
+ import re
30
31
 
31
32
  from astroquery.simbad import Simbad
32
33
  from astroquery.vizier import Vizier
@@ -152,6 +153,41 @@ def _estimate_scale_arcsec_per_pix(h: fits.Header):
152
153
 
153
154
  return None
154
155
 
156
+ _TZ_RE = re.compile(r'([+-])(\d{2})(\d{2})$') # -0700 -> -07:00
157
+
158
+ def _fix_iso_tz(s: str) -> str:
159
+ s = s.strip()
160
+ m = _TZ_RE.search(s)
161
+ if m:
162
+ s = s[:m.start()] + f"{m.group(1)}{m.group(2)}:{m.group(3)}"
163
+ return s
164
+
165
+ def _parse_obs_time_from_header(hdr) -> Time | None:
166
+ # hdr can be fits.Header or your dict-ish header
167
+ def _get(key):
168
+ try:
169
+ return hdr.get(key)
170
+ except Exception:
171
+ return None
172
+
173
+ # 1) Prefer UT-OBS if present (already “UTC-ish”)
174
+ for key in ("UT-OBS", "DATE-OBS", "DATE-END"):
175
+ v = _get(key)
176
+ if isinstance(v, str) and v.strip():
177
+ try:
178
+ return Time(_fix_iso_tz(v), format="isot", scale="utc")
179
+ except Exception:
180
+ pass
181
+
182
+ # 2) MJD-OBS is super reliable
183
+ v = _get("MJD-OBS")
184
+ if v is not None:
185
+ try:
186
+ return Time(float(v), format="mjd", scale="utc")
187
+ except Exception:
188
+ pass
189
+
190
+ return None
155
191
 
156
192
  def _estimate_fov_deg(img_shape, scale_arcsec):
157
193
  """Rough FOV (deg) from image size and scale (max of X/Y)."""
@@ -646,9 +682,12 @@ class ExoPlanetWindow(QDialog):
646
682
  self.measure_btn.setVisible(is_raw)
647
683
 
648
684
  def load_and_measure_subs(self):
685
+ before = len(getattr(self, "image_paths", []))
649
686
  self.load_aligned_subs()
687
+ after = len(getattr(self, "image_paths", []))
688
+ if after == 0 or after == before and not self._cached_images:
689
+ return
650
690
  self.detect_stars()
651
-
652
691
  # --------------- I/O + Calibration ----------------
653
692
 
654
693
  def load_raw_subs(self):
@@ -682,13 +721,38 @@ class ExoPlanetWindow(QDialog):
682
721
  ds = hdr0.get('DATE-OBS')
683
722
  except:
684
723
  ds = None
724
+
725
+ # Use robust header time parsing (UT-OBS -> DATE-OBS -> MJD-OBS fallback)
685
726
  t = None
686
- if isinstance(ds, str):
687
- try:
688
- t = Time(ds, format='isot', scale='utc')
689
- except Exception as e:
690
- print(f"[DEBUG] Failed to parse DATE-OBS for {p}: {e}")
727
+ try:
728
+ if ext == ".xisf":
729
+ # Build a tiny dict-like header for the helper
730
+ hdr_like = {}
731
+ try:
732
+ xisf = XISF(p)
733
+ img_meta = xisf.get_images_metadata()[0]
734
+ kw = img_meta.get("FITSKeywords", {}) or {}
735
+ # XISF FITSKeywords layout: key -> [ {value: ...}, ... ]
736
+ for k in ("UT-OBS", "DATE-OBS", "DATE-END", "MJD-OBS"):
737
+ if k in kw and kw[k]:
738
+ hdr_like[k] = kw[k][0].get("value")
739
+ except Exception:
740
+ hdr_like = {}
741
+ t = _parse_obs_time_from_header(hdr_like)
742
+
743
+ elif ext in (".fit", ".fits", ".fz"):
744
+ hdr0, _ = get_valid_header(p)
745
+ t = _parse_obs_time_from_header(hdr0)
746
+
747
+ else:
748
+ # TIFF etc. may not have FITS-like time headers; leave None
749
+ t = None
750
+
751
+ except Exception as e:
752
+ print(f"[DEBUG] Failed to parse obs time for {p}: {e}")
753
+
691
754
  datelist.append((p, t))
755
+
692
756
  self.progress_bar.setValue(i)
693
757
  QApplication.processEvents()
694
758
 
@@ -768,24 +832,28 @@ class ExoPlanetWindow(QDialog):
768
832
  self.progress_bar.setValue(i)
769
833
  QApplication.processEvents()
770
834
 
771
- iso_strs, mask_arr = [], []
772
- for _, t in datelist:
773
- if t is not None:
774
- iso_strs.append(t.isot); mask_arr.append(False)
775
- else:
776
- iso_strs.append(''); mask_arr.append(True)
777
- ma_strs = np.ma.MaskedArray(iso_strs, mask=mask_arr)
778
- self.times = Time(ma_strs, format='isot', scale='utc', out_subfmt='date')
835
+ # Keep full timestamps (DO NOT truncate to date-only)
836
+ tlist = [t for _, t in datelist if t is not None]
837
+ if tlist:
838
+ self.times = Time(tlist) # already utc scale from helper
839
+ else:
840
+ self.times = None
779
841
 
780
842
  self.progress_bar.setVisible(False)
781
843
  loaded = sum(1 for im in self._cached_images if im is not None)
782
844
  self.status_label.setText(f"Loaded {loaded}/{len(sorted_paths)} raw frames")
783
845
 
784
- def load_aligned_subs(self):
846
+ def load_aligned_subs(self) -> bool:
785
847
  settings = QSettings()
786
848
  start_dir = settings.value("ExoPlanet/lastAlignedFolder", os.path.expanduser("~"), type=str)
787
- paths, _ = QFileDialog.getOpenFileNames(self, "Select Aligned Frames", start_dir, "FITS or TIFF (*.fit *.fits *.tif *.tiff *.xisf)")
788
- if not paths: return
849
+ paths, _ = QFileDialog.getOpenFileNames(
850
+ self, "Select Aligned Frames", start_dir,
851
+ "FITS or TIFF (*.fit *.fits *.tif *.tiff *.xisf)"
852
+ )
853
+ if not paths:
854
+ self.status_label.setText("Load canceled.")
855
+ return False
856
+
789
857
  settings.setValue("ExoPlanet/lastAlignedFolder", os.path.dirname(paths[0]))
790
858
 
791
859
  self.status_label.setText("Reading metadata from aligned frames…")
@@ -809,10 +877,35 @@ class ExoPlanetWindow(QDialog):
809
877
  hdr0, _ = get_valid_header(p)
810
878
  ds = hdr0.get('DATE-OBS')
811
879
  except: ds = None
880
+ # Use robust header time parsing (UT-OBS -> DATE-OBS -> MJD-OBS fallback)
812
881
  t = None
813
- if isinstance(ds, str):
814
- try: t = Time(ds, format='isot', scale='utc')
815
- except Exception as e: print(f"[DEBUG] Failed to parse DATE-OBS for {p}: {e}")
882
+ try:
883
+ if ext == ".xisf":
884
+ # Build a tiny dict-like header for the helper
885
+ hdr_like = {}
886
+ try:
887
+ xisf = XISF(p)
888
+ img_meta = xisf.get_images_metadata()[0]
889
+ kw = img_meta.get("FITSKeywords", {}) or {}
890
+ # XISF FITSKeywords layout: key -> [ {value: ...}, ... ]
891
+ for k in ("UT-OBS", "DATE-OBS", "DATE-END", "MJD-OBS"):
892
+ if k in kw and kw[k]:
893
+ hdr_like[k] = kw[k][0].get("value")
894
+ except Exception:
895
+ hdr_like = {}
896
+ t = _parse_obs_time_from_header(hdr_like)
897
+
898
+ elif ext in (".fit", ".fits", ".fz"):
899
+ hdr0, _ = get_valid_header(p)
900
+ t = _parse_obs_time_from_header(hdr0)
901
+
902
+ else:
903
+ # TIFF etc. may not have FITS-like time headers; leave None
904
+ t = None
905
+
906
+ except Exception as e:
907
+ print(f"[DEBUG] Failed to parse obs time for {p}: {e}")
908
+
816
909
  datelist.append((p, t))
817
910
  self.progress_bar.setValue(i)
818
911
  QApplication.processEvents()
@@ -889,18 +982,17 @@ class ExoPlanetWindow(QDialog):
889
982
  self.progress_bar.setValue(i)
890
983
  QApplication.processEvents()
891
984
 
892
- iso_strs, mask_arr = [], []
893
- for _, t in datelist:
894
- if t is not None:
895
- iso_strs.append(t.isot); mask_arr.append(False)
896
- else:
897
- iso_strs.append(''); mask_arr.append(True)
898
- ma_strs = np.ma.MaskedArray(iso_strs, mask=mask_arr)
899
- self.times = Time(ma_strs, format='isot', scale='utc', out_subfmt='date')
985
+ # Keep full timestamps (DO NOT truncate to date-only)
986
+ tlist = [t for _, t in datelist if t is not None]
987
+ if tlist:
988
+ self.times = Time(tlist) # already utc scale from helper
989
+ else:
990
+ self.times = None
900
991
 
901
992
  self.progress_bar.setVisible(False)
902
993
  loaded = sum(1 for im in self._cached_images if im is not None)
903
994
  self.status_label.setText(f"Loaded {loaded}/{len(sorted_paths)} aligned frames")
995
+ return loaded > 0
904
996
 
905
997
  def load_masters(self):
906
998
  settings = QSettings()