diff --git a/.gitignore b/.gitignore deleted file mode 100644 index d168342..0000000 --- a/.gitignore +++ /dev/null @@ -1,19 +0,0 @@ -# Local cache folders -ExoTransitEngine_Cache/ -.exotransit_cache/ - -# Python environment artifacts -__pycache__/ -*.pyc -*.pyo -*.pyd -.pytest_cache/ -.venv/ -venv/ -ENV/ - -# Data and logs -*.fits -*.csv -*.log -Export_*/ diff --git a/AU Mic b 2.png b/AU Mic b 2.png deleted file mode 100644 index b323c3b..0000000 Binary files a/AU Mic b 2.png and /dev/null differ diff --git a/ExoTransit Engine.py b/ExoTransit Engine.py deleted file mode 100644 index c3020e8..0000000 --- a/ExoTransit Engine.py +++ /dev/null @@ -1,683 +0,0 @@ -import sys -import os -import subprocess -import warnings -import csv -import time -import socket -import ctypes -import threading - -if sys.platform == "win32": - appdata_base = os.getenv('APPDATA', os.path.expanduser("~")) - cache_dir = os.path.join(appdata_base, "ExoTransitEngine_Cache") -else: - cache_dir = os.path.join(os.path.expanduser("~"), ".exotransit_cache") - -os.makedirs(os.path.join(cache_dir, 'astropy_config'), exist_ok=True) -os.makedirs(os.path.join(cache_dir, 'astropy_cache'), exist_ok=True) - -os.environ['ASTROPY_CONFIG_DIR'] = os.path.join(cache_dir, 'astropy_config') -os.environ['ASTROPY_CACHE_DIR'] = os.path.join(cache_dir, 'astropy_cache') -os.environ['ASTROPY_USE_DOWNLOAD_CACHE'] = 'True' - -try: - myappid = 'mycompany.exotransit.engine.v5.3.3' - ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid) -except Exception: - pass - -if getattr(sys, 'frozen', False): - base_path = sys._MEIPASS -else: - try: - base_path = os.path.dirname(os.path.abspath(__file__)) - except NameError: - base_path = os.getcwd() - -icon_full_path = os.path.join(base_path, "app_icon.ico") -if not os.path.exists(icon_full_path): - icon_full_path = "app_icon.ico" - -REQUIRED_PACKAGES = { - "customtkinter": "customtkinter", - "lightkurve": "lightkurve", - "astroquery": "astroquery", - "matplotlib": "matplotlib", - "astropy": "astropy", - "numpy": "numpy" -} - -def auto_deploy_environment(): - for module_name, pip_name in REQUIRED_PACKAGES.items(): - try: - __import__(module_name) - except ImportError: - print(f"Deploying system dependency: {pip_name}...") - try: - subprocess.check_call([sys.executable, "-m", "pip", "install", pip_name], timeout=30) - except Exception as e: - print(f"Deployment restriction encountered for {pip_name}: {e}") - -if not getattr(sys, 'frozen', False): - auto_deploy_environment() -try: - from astropy.utils import data as astropy_data - original_download_file = astropy_data.download_file - def smart_download_file(remote_url, *args, **kwargs): - if "astropy_icon" in str(remote_url): - raise IOError("Offline mode forced for local cache asset.") - return original_download_file(remote_url, *args, **kwargs) - astropy_data.download_file = smart_download_file - from astropy.utils.iers import conf as iers_conf - iers_conf.auto_download = False -except Exception: - pass - -import customtkinter as ctk -import matplotlib -matplotlib.use('TkAgg') -import matplotlib.pyplot as plt -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk -import numpy as np - -HAS_ASTROPHYSICS_SUITE = True -try: - import lightkurve as lk - from astropy.units import Quantity -except (ImportError, ModuleNotFoundError): - HAS_ASTROPHYSICS_SUITE = False - -HAS_ASTROQUERY = False -try: - import astroquery - from astroquery.ipac.nexsci.nasaexoplanetarchive import NasaExoplanetArchive - HAS_ASTROQUERY = True -except Exception: - HAS_ASTROQUERY = False - -from tkinter import messagebox, filedialog - -warnings.filterwarnings("ignore") - -ctk.set_appearance_mode("Light") -ctk.set_default_color_theme("blue") - -canvas_p1, canvas_p2 = None, None -toolbar_p1, toolbar_p2 = None, None -tf1, tf2 = None, None -is_running_pipeline = False - -current_lc_clean = None -current_metrics_text = "" -fig1_global, fig2_global = None, None - -class MockLightCurve: - def __init__(self, time, flux): - self.time = time - self.flux = flux - -def clean_to_flat_array(data_input): - if data_input is None: - return np.array([], dtype=float) - if hasattr(data_input, 'value'): - arr = data_input.value - elif hasattr(data_input, 'filled'): - arr = data_input.filled(np.nan) - else: - arr = data_input - return np.asarray(arr, dtype=float).flatten() - -def parse_target_input(raw_input): - tokens = raw_input.strip().split() - if not tokens: - return "", "" - last_token = tokens[-1].lower() - if len(last_token) == 1 and last_token.isalpha() and last_token != 'a': - host_star = " ".join(tokens[:-1]) - planet_letter = last_token - else: - raw_str = "".join(tokens) - if raw_str[-1].isalpha() and raw_str[-1].lower() != 'a' and raw_str[-2].isdigit(): - planet_letter = raw_str[-1].lower() - host_star = raw_input.strip()[:-1].strip() - else: - host_star = raw_input.strip() - planet_letter = "" - return host_star, planet_letter - -def fetch_nasa_planetary_intel(target_name): - host_star, planet_letter = parse_target_input(target_name) - defaults = { - "AU MIC b": {"period": 8.4622, "radius": 4.20, "depth": 1.2, "teff": 3700}, - "AU MIC c": {"period": 18.8590, "radius": 3.24, "depth": 0.7, "teff": 3700}, - "KEPLER-10 b": {"period": 0.8375, "radius": 1.47, "depth": 0.3, "teff": 5627}, - "KEPLER-10 c": {"period": 45.2940, "radius": 2.35, "depth": 0.4, "teff": 5627} - } - lookup_key = f"{host_star.upper()} {planet_letter}".strip() - - if HAS_ASTROQUERY: - try: - status_label.configure(text="[STEP 1/5] CROSS-REFERENCING NASA ARCHIVE...", text_color="#2980b9") - if planet_letter: - query_table = NasaExoplanetArchive.query_criteria( - table="pscompmars", - select="st_teff, pl_orbper, pl_rade, pl_trandep, sy_dist, sy_pnum", - where=f"hostname llike '{host_star}' and pl_letter = '{planet_letter}'" - ) - else: - query_table = NasaExoplanetArchive.query_criteria( - table="pscompmars", - select="st_teff, pl_orbper, pl_rade, pl_trandep, sy_dist, sy_pnum", - where=f"hostname llike '{host_star}'" - ) - if query_table is not None and len(query_table) > 0: - row = query_table[0] - teff = row['st_teff'] if 'st_teff' in query_table.colnames and not np.isnan(row['st_teff']) else 5778 - period = row['pl_orbper'] if 'pl_orbper' in query_table.colnames and not np.isnan(row['pl_orbper']) else None - radius = row['pl_rade'] if 'pl_rade' in query_table.colnames and not np.isnan(row['pl_rade']) else None - depth = row['pl_trandep'] if 'pl_trandep' in query_table.colnames and not np.isnan(row['pl_trandep']) else None - dist = f"{row['sy_dist']:.1f}" if 'sy_dist' in query_table.colnames and not np.isnan(row['sy_dist']) else 'Unknown' - pnum = int(row['sy_pnum']) if 'sy_pnum' in query_table.colnames and not np.isnan(row['sy_pnum']) else 'Unknown' - - label_text = ( - f"Host Star Temp : {int(teff)} K\n" - f"System Distance: {dist} parsecs\n" - f"Total Planets : {pnum} confirmed\n" - f"Target Planet : Variant '{planet_letter.upper() if planet_letter else 'b discoverer'}'" - ) - return {"text": label_text, "teff": teff, "period": period, "radius": radius, "depth": depth, "host": host_star} - except Exception: - pass - - match = defaults.get(lookup_key, {"period": None, "radius": None, "depth": None, "teff": 5778}) - label_text = ( - f"Host Star Temp : {match['teff']} K\n" - f"System Distance: Local Registry Map\n" - f"Total Planets : Automated Extract\n" - f"Target Planet : Catalog Match '{planet_letter.upper() if planet_letter else 'B'}'" - ) - return {"text": label_text, "teff": match['teff'], "period": match['period'], "radius": match['radius'], "depth": match['depth'], "host": host_star} - -def toggle_theme(): - if ctk.get_appearance_mode() == "Dark": - ctk.set_appearance_mode("Light") - theme_btn.configure(text="Dark Mode", fg_color="#2c3e50", text_color="#ffffff") - else: - ctk.set_appearance_mode("Dark") - theme_btn.configure(text="Light Mode", fg_color="#e0e0e0", text_color="#000000") - -def import_local_csv_pipeline(): - global is_running_pipeline, current_lc_clean, current_metrics_text - if is_running_pipeline: - return - - csv_path = filedialog.askopenfilename( - title="Select Exoplanet Dataset (CSV/TXT)", - filetypes=[("Data Files", "*.csv *.txt"), ("All Files", "*.*")] - ) - if not csv_path: - return - - is_running_pipeline = True - status_label.configure(text="PROCESSING LOCAL CSV MATRIX...", text_color="#e67e22") - progress.start() - - def csv_worker(): - global current_lc_clean, current_metrics_text - try: - start_time = time.time() - time_arr, flux_arr = [], [] - - with open(csv_path, mode='r') as file: - sample = file.read(2048) - file.seek(0) - has_header = csv.Sniffer().has_header(sample) - reader = csv.reader(file) - if has_header: - next(reader) - - for row in reader: - if len(row) >= 2: - try: - t_val = float(row[0].strip()) - f_val = float(row[1].strip()) - time_arr.append(t_val) - flux_arr.append(f_val) - except ValueError: - continue - - t_data = np.array(time_arr) - f_data = np.array(flux_arr) - - if len(t_data) == 0: - raise ValueError("No parsing metrics extracted from file columns.") - - f_data /= np.median(f_data) - current_lc_clean = MockLightCurve(t_data, f_data) - - total_days = t_data[-1] - t_data[0] - periods = np.linspace(0.4, min(25.0, total_days/2.0), 4000) - - best_p, best_power, best_t0 = 1.0, -1.0, t_data[0] - for p in periods: - phases = (t_data - t_data[0]) / p - phases = phases - np.floor(phases) - hist, bin_edges = np.histogram(phases, bins=40, weights=f_data) - counts, _ = np.histogram(phases, bins=40) - binned_profile = np.divide(hist, counts, out=np.ones_like(hist), where=counts!=0) - p_metric = np.max(binned_profile) - np.min(binned_profile) - if p_metric > best_power: - best_power = p_metric - best_p = p - best_t0 = t_data[0] + (bin_edges[np.argmin(binned_profile)] * p) - - phases_final = ((t_data - best_t0) / best_p) - phases_final = (phases_final + 0.5) % 1.0 - 0.5 - sort_idx = np.argsort(phases_final) - - calc_depth = 1.0 - np.percentile(f_data, 1) - calc_radius = np.sqrt(calc_depth) * 10.0 - planet_class = "Super-Earth Profile" if calc_radius < 2.5 else "Gas Giant Segment" - - current_metrics_text = ( - f"Orbit Period : {best_p:.4f} Days\n" - f"Transit Depth : {calc_depth*1000.0:.2f} ppt\n" - f"Planet Radius : {calc_radius:.2f}x Earth Rad\n" - f"Discovery Mode : Local CSV Import\n" - f"Classification : {planet_class}" - ) - - if ctk.get_appearance_mode() == "Dark": - plt.style.use('dark_background') - fig_bg, axes_bg, text_color, grid_color = '#1e1e24', '#121216', '#ffffff', '#2c2c35' - else: - plt.style.use('default') - fig_bg, axes_bg, text_color, grid_color = '#ffffff', '#f8f9fa', '#2c3e50', '#e2e8f0' - - plt.rcParams.update({ - 'figure.facecolor': fig_bg, 'axes.facecolor': axes_bg, - 'text.color': text_color, 'axes.labelcolor': text_color, - 'xtick.color': text_color, 'ytick.color': text_color, 'grid.color': grid_color - }) - - fig1, (ax1, ax2) = plt.subplots(2, 1, figsize=(7, 5.8)) - fig1.suptitle(f"Imported CSV Profile Matrix: {os.path.basename(csv_path)}", fontsize=10, fontweight='bold', color='#e67e22') - ax1.scatter(t_data, f_data, s=1, color='#2c3e50', alpha=0.5) - ax1.set_title("1. Local Dataset Timeline Chronology Matrix", fontsize=8, loc='left', fontweight='bold') - ax1.grid(True, linestyle=':') - - ax2.plot(periods, np.random.rand(len(periods)) * 0.1, color='#7f8c8d', alpha=0.3) - ax2.axvline(best_p, color='#e74c3c', linestyle='--', label=f"Detected Base: {best_p:.4f} d") - ax2.set_title("2. Frequency Target Lock Diagnostic Sweep", fontsize=8, loc='left', fontweight='bold') - ax2.legend(fontsize=7) - ax2.grid(True, linestyle=':') - fig1.tight_layout() - - fig2, (ax3, ax4) = plt.subplots(2, 1, figsize=(7, 5.8)) - fig2.suptitle("Statistical Phase Geometry Alignment", fontsize=10, fontweight='bold', color='#27ae60') - ax3.scatter(phases_final, f_data, s=1, color='#bdc3c7', alpha=0.4) - ax3.plot(phases_final[sort_idx], np.convolve(f_data[sort_idx], np.ones(20)/20, mode='same'), color='#2980b9', lw=2) - ax3.set_title("3. Continuous Wrapped Phase Folded Transit Map", fontsize=8, loc='left', fontweight='bold') - ax3.grid(True, linestyle=':') - - ax4.hist(f_data, bins=30, density=True, color='#27ae60', alpha=0.6) - ax4.set_title("4. Amplitude Deviation Probability Density Curve", fontsize=8, loc='left', fontweight='bold') - ax4.grid(True, linestyle=':') - fig2.tight_layout() - - elapsed = time.time() - start_time - root.after(0, render_plots_on_main_thread, "CSV Import System", "Source: Local Storage Registry", current_metrics_text, fig1, fig2, elapsed) - except Exception as e: - progress.stop() - status_label.configure(text="CSV IMPORT ERROR", text_color="#c0392b") - messagebox.showerror("Parsing Fault", f"Failed to extract array parameters:\n{e}") - global is_running_pipeline - is_running_pipeline = False - - threading.Thread(target=csv_worker, daemon=True).start() - -def export_pipeline_results(): - global current_lc_clean, current_metrics_text, fig1_global, fig2_global - user_input = star_search_entry.get().strip() or "imported_system" - if current_lc_clean is None: - messagebox.showwarning("Export Failed", "No processed telemetry found. Please run an analysis first.") - return - selected_directory = filedialog.askdirectory(title="Select Destination Folder for Export") - if not selected_directory: - return - try: - clean_name = user_input.replace(" ", "_").replace("-", "_") - export_folder = os.path.join(selected_directory, f"Export_{clean_name}") - os.makedirs(export_folder, exist_ok=True) - csv_path = os.path.join(export_folder, f"{clean_name}_lightcurve.csv") - times = clean_to_flat_array(current_lc_clean.time) - fluxes = clean_to_flat_array(current_lc_clean.flux) - with open(csv_path, mode='w', newline='') as file: - writer = csv.writer(file) - writer.writerow(["Time_BJD", "Normalized_Flux"]) - for t, f in zip(times, fluxes): - writer.writerow([t, f]) - txt_path = os.path.join(export_folder, f"{clean_name}_summary.txt") - with open(txt_path, mode='w') as file: - file.write(f"EXOTRANSIT ENGINE EXPORT DATA FOR: {user_input.upper()}\n") - file.write("="*45 + "\n") - file.write(current_metrics_text + "\n") - if fig1_global: - fig1_global.savefig(os.path.join(export_folder, f"{clean_name}_telemetry_matrix.png"), dpi=200) - if fig2_global: - fig2_global.savefig(os.path.join(export_folder, f"{clean_name}_morphology_models.png"), dpi=200) - messagebox.showinfo("Export Success", f"All data packets safely archived inside:\n'{export_folder}'") - except Exception as e: - messagebox.showerror("Export Error", f"Failed to save system data archives:\n{e}") - -def render_plots_on_main_thread(user_input, intel_text, metrics_text, fig1, fig2, elapsed): - global canvas_p1, canvas_p2, toolbar_p1, toolbar_p2, tf1, tf2, is_running_pipeline - global fig1_global, fig2_global - info_label.configure(text=intel_text) - metrics_label.configure(text=metrics_text) - if canvas_p1 is not None: - canvas_p1.get_tk_widget().destroy() - if canvas_p2 is not None: - canvas_p2.get_tk_widget().destroy() - if tf1 is not None: - tf1.destroy() - if tf2 is not None: - tf2.destroy() - fig1_global = fig1 - fig2_global = fig2 - canvas_p1 = FigureCanvasTkAgg(fig1, master=page1) - canvas_p1.draw() - canvas_p1.get_tk_widget().pack(fill="both", expand=True, padx=5, pady=2) - tf1 = ctk.CTkFrame(page1, height=35, fg_color="transparent") - tf1.pack(fill="x", side="bottom", padx=5, pady=2) - toolbar_p1 = NavigationToolbar2Tk(canvas_p1, tf1, pack_toolbar=False) - toolbar_p1.update() - toolbar_p1.pack(side="left", padx=5) - canvas_p2 = FigureCanvasTkAgg(fig2, master=page2) - canvas_p2.draw() - canvas_p2.get_tk_widget().pack(fill="both", expand=True, padx=5, pady=2) - tf2 = ctk.CTkFrame(page2, height=35, fg_color="transparent") - tf2.pack(fill="x", side="bottom", padx=5, pady=2) - toolbar_p2 = NavigationToolbar2Tk(canvas_p2, tf2, pack_toolbar=False) - toolbar_p2.update() - toolbar_p2.pack(side="left", padx=5) - progress.stop() - status_label.configure(text=f"COMPLETED IN {elapsed:.1f}s | READY", text_color="#27ae60") - is_running_pipeline = False - -def _async_pipeline_worker(user_input): - global current_lc_clean, current_metrics_text, is_running_pipeline - if not HAS_ASTROPHYSICS_SUITE: - progress.stop() - status_label.configure(text="ASTROPY NOT LOADED", text_color="#c0392b") - messagebox.showerror("Module Error", "Online operations require 'lightkurve' and 'astropy'.\nPlease use the 'Import Local Dataset' feature on this computer instead!") - is_running_pipeline = False - return - try: - start_time = time.time() - intel = fetch_nasa_planetary_intel(user_input) - host_star = intel["host"] - clean_name = host_star.replace(" ", "").replace("-", "").lower() - local_filename = os.path.join(cache_dir, f"cache_{clean_name}_data.fits") - lc = None - - if os.path.exists(local_filename): - try: - status_label.configure(text="[STEP 2/5] LOADING CACHED FITS BLOCKS...", text_color="#27ae60") - lc = lk.read(local_filename).remove_nans() - except Exception: - if os.path.exists(local_filename): - os.remove(local_filename) - - if lc is None: - try: - status_label.configure(text="[STEP 2/5] PINGING SPACE TELESCOPE SERVERS...", text_color="#d35400") - search_result = lk.search_lightcurve(host_star, mission="TESS") - if len(search_result) == 0: - search_result = lk.search_lightcurve(host_star, mission="Kepler") - if len(search_result) == 0: - raise ValueError("Target system light curve data not found on MAST.") - - status_label.configure(text="[STEP 2/5] STREAMING STELLAR LIGHT CURVE...", text_color="#d35400") - lc_downloaded = search_result[0].download() - lc_downloaded.to_fits(local_filename, overwrite=True) - lc = lc_downloaded.remove_nans() - except Exception as e: - progress.stop() - status_label.configure(text="DOWNLOAD EXCEPTION RECOVERED", text_color="#c0392b") - messagebox.showerror("Pipeline Network Fault", f"Could not safely mount remote arrays.\n\nDetails: {e}") - is_running_pipeline = False - return - - status_label.configure(text="[STEP 3/5] RUNNING ADAPTIVE TREND FILTER...", text_color="#16a085") - raw_time = clean_to_flat_array(lc.time) - raw_flux = clean_to_flat_array(lc.flux) - total_days = raw_time[-1] - raw_time[0] if len(raw_time) > 1 else 20.0 - adaptive_window = 21 if "AU" in host_star.upper() else (25 if total_days > 10 else 11) - - flatten_res = lc.flatten(window_length=adaptive_window, return_trend=True) - lc_clean = flatten_res[0].remove_outliers(sigma=3.5) - current_lc_clean = lc_clean - lc_trend_obj = flatten_res[1] - - status_label.configure(text="[STEP 4/5] COMPUTING BLS SIGNAL INTEGRATION...", text_color="#8e44ad") - max_period_trial = min(25.0, max(5.0, total_days / 2.0)) - periods = np.linspace(0.4, max_period_trial, 6000) - - try: - bls = lc_clean.to_periodogram(method='bls', period=periods, objective='snr') - t0 = bls.transit_time_at_max_power - duration = bls.duration_at_max_power - bls_periods_out = clean_to_flat_array(bls.period) - bls_power_out = clean_to_flat_array(bls.power) - calculated_p = float(bls.period_at_max_power.value if hasattr(bls.period_at_max_power, 'value') else bls.period_at_max_power) - except Exception: - from astropy.timeseries import BoxLeastSquares - clean_t = clean_to_flat_array(lc_clean.time) - clean_f = clean_to_flat_array(lc_clean.flux) - bls_model = BoxLeastSquares(clean_t, clean_f) - bls_results = bls_model.power(periods, 0.1) - calculated_p = float(periods[np.argmax(bls_results.power)]) - t0 = bls_results.transit_time[np.argmax(bls_results.power)] - duration = bls_results.duration[np.argmax(bls_results.power)] - bls_periods_out = periods - bls_power_out = bls_results.power - - if intel["period"] is not None: - best_p_val = float(intel["period"]) - status_mode = "NASA Telemetry Locked" - else: - best_p_val = calculated_p - status_mode = "BLS Algorithm Computed" - - status_label.configure(text="[STEP 5/5] PLOTTING MORPHOLOGY SUB-MODELS...", text_color="#d35400") - folded = lc_clean.fold(period=best_p_val, epoch_time=t0) - dynamic_bin = best_p_val / 400.0 - binned = folded.bin(time_bin_size=dynamic_bin).remove_nans() - - binned_flux = clean_to_flat_array(binned.flux) - calc_depth = 1.0 - np.nanmin(binned_flux) if len(binned_flux) > 0 else 0.001 - final_depth = intel["depth"] / 1000.0 if intel["depth"] else calc_depth - star_radius = lc.meta.get('RADIUS', 0.698) or 0.698 - final_radius = intel["radius"] if intel["radius"] else (star_radius * np.sqrt(final_depth)) * 109.2 - planet_class = "Super-Earth" if final_radius < 2.0 else ("Neptune-Like" if final_radius < 6.0 else "Gas Giant Class") - - current_metrics_text = ( - f"Orbit Period : {best_p_val:.4f} Days\n" - f"Transit Depth : {final_depth*1000.0:.2f} ppt\n" - f"Planet Radius : {final_radius:.2f}x Earth Rad\n" - f"Discovery Mode : {status_mode}\n" - f"Classification : {planet_class}" - ) - - if ctk.get_appearance_mode() == "Dark": - plt.style.use('dark_background') - fig_bg, axes_bg, text_color, grid_color = '#1e1e24', '#121216', '#ffffff', '#2c2c35' - else: - plt.style.use('default') - fig_bg, axes_bg, text_color, grid_color = '#ffffff', '#f8f9fa', '#2c3e50', '#e2e8f0' - - plt.rcParams.update({ - 'figure.facecolor': fig_bg, 'axes.facecolor': axes_bg, - 'text.color': text_color, 'axes.labelcolor': text_color, - 'xtick.color': text_color, 'ytick.color': text_color, 'grid.color': grid_color - }) - - fig1, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(7, 5.8)) - fig1.suptitle(f"Pipeline Telemetry Matrix: {user_input.upper()}", fontsize=11, fontweight='bold', color='#2980b9') - ax1.scatter(clean_to_flat_array(lc.time), raw_flux, s=1, color='#95a5a6', alpha=0.4, label='Raw Flux') - ax1.plot(clean_to_flat_array(lc_trend_obj.time), clean_to_flat_array(lc_trend_obj.flux), color='#e74c3c', lw=1.5, label='Trend Filter') - ax1.set_title("1. Raw Sap Flux Timeline & Stellar Variability Filter", fontsize=8, loc='left', fontweight='bold') - ax1.legend(fontsize=7, loc='upper right') - ax1.grid(True, linestyle=':') - - ax2.scatter(clean_to_flat_array(lc_clean.time), clean_to_flat_array(lc_clean.flux), s=1, color='#2c3e50', alpha=0.4) - ax2.set_title("2. Detrended & Outlier-Clipped Science Light Curve", fontsize=8, loc='left', fontweight='bold') - ax2.set_ylabel("Normalized Flux") - ax2.grid(True, linestyle=':') - - ax3.plot(bls_periods_out, bls_power_out, color='#9b59b6', lw=1) - ax3.axvline(best_p_val, color='#e74c3c', linestyle='--', lw=1.2, label=f"Target Lock Vector: {best_p_val:.4f} d") - ax3.legend(fontsize=7, loc='upper right') - ax3.set_title("3. Box Least Squares (BLS) Periodogram Spectral Power Sweep", fontsize=8, loc='left', fontweight='bold') - ax3.set_xlabel("Trial Period (Days)") - ax3.grid(True, linestyle=':') - fig1.tight_layout() - - fig2, (ax4, ax5, ax6) = plt.subplots(3, 1, figsize=(7, 5.8)) - fig2.suptitle(f"Statistical Morphology Models: {user_input.upper()}", fontsize=11, fontweight='bold', color='#27ae60') - f_time = clean_to_flat_array(folded.time) - f_flux = clean_to_flat_array(folded.flux) - ax4.scatter(clean_to_flat_array(folded.phase), f_flux, s=1, color='#bdc3c7', alpha=0.3) - ax4.set_title("4. Continuous Orbit Full-Phase Folded Alignment Map (Phase -0.5 to 0.5)", fontsize=8, loc='left', fontweight='bold') - ax4.grid(True, linestyle=':') - - b_time = clean_to_flat_array(binned.time) - b_flux = clean_to_flat_array(binned.flux) - ax5.scatter(f_time, f_flux, alpha=0.15, s=1, color='#7f8c8d') - ax5.plot(b_time, b_flux, color='#2980b9', lw=2.5, label='Binned Telemetry') - duration_val = float(duration.value if hasattr(duration, 'value') else duration) - box_model = np.ones_like(f_time) - box_mask = (f_time >= -duration_val/2) & (f_time <= duration_val/2) - box_model[box_mask] = 1.0 - calc_depth - sort_idx = np.argsort(f_time) - ax5.plot(f_time[sort_idx], box_model[sort_idx], color='#e74c3c', lw=1.5, linestyle=':', label='Box Fit') - ax5.set_xlim(-2.5 * duration_val, 2.5 * duration_val) - ax5.set_title("5. High-Resolution Micro-Scale Transit Profile Geometry Fit", fontsize=8, loc='left', fontweight='bold') - ax5.legend(fontsize=7, loc='lower left') - ax5.grid(True, linestyle=':') - - t_vals = clean_to_flat_array(lc_clean.time) - float(t0.value if hasattr(t0, 'value') else t0) - orbits = np.round(t_vals / best_p_val) - even = (orbits % 2 == 0) - try: - ax6.remove() - ax6_left = plt.subplot(3, 2, 5) - ax6_right = plt.subplot(3, 2, 6) - le = lc_clean[even].fold(period=best_p_val, epoch_time=t0).bin(time_bin_size=dynamic_bin) - lo = lc_clean[~even].fold(period=best_p_val, epoch_time=t0).bin(time_bin_size=dynamic_bin) - ax6_left.plot(clean_to_flat_array(le.time), clean_to_flat_array(le.flux), color='#3498db', lw=1.5, label='Even Orbits') - ax6_left.plot(clean_to_flat_array(lo.time), clean_to_flat_array(lo.flux), color='#e67e22', lw=1.5, label='Odd Orbits') - ax6_left.set_xlim(-2.5 * duration_val, 2.5 * duration_val) - ax6_left.set_title("6a. Alternating Cycle Contrast (EB Vetting)", fontsize=7.5, fontweight='bold', loc='left') - ax6_left.set_xlabel("Time from Mid-Transit (Days)", fontsize=7) - ax6_left.legend(fontsize=6, loc='lower left') - ax6_left.grid(True, linestyle=':') - - in_transit_points = f_flux[box_mask & ~np.isnan(f_flux)] - out_transit_points = f_flux[~box_mask & ~np.isnan(f_flux)] - ax6_right.hist(out_transit_points, bins=35, density=True, color='#7f8c8d', alpha=0.5, label='Baseline') - ax6_right.hist(in_transit_points, bins=35, density=True, color='#2980b9', alpha=0.5, label='In-Transit') - ax6_right.set_title("6b. Flux Probability Density Distribution", fontsize=7.5, fontweight='bold', loc='left') - ax6_right.set_xlabel("Normalized Magnitude Spectrum", fontsize=7) - ax6_right.legend(fontsize=6, loc='upper left') - ax6_right.grid(True, linestyle=':') - except Exception: - pass - - fig2.tight_layout() - elapsed_time = time.time() - start_time - root.after(0, render_plots_on_main_thread, user_input, intel["text"], current_metrics_text, fig1, fig2, elapsed_time) - except Exception as e: - progress.stop() - status_label.configure(text="PROCESSING EXCEPTION ABORT", text_color="#c0392b") - messagebox.showerror("Error", f"Processing matrix fault: {e}") - is_running_pipeline = False - -def run_pipeline(): - global is_running_pipeline - if is_running_pipeline: - messagebox.showwarning("Pipeline Running", "Please wait until the active extraction completes.") - return - user_input = star_search_entry.get().strip() - if not user_input or user_input == "Enter Planet": - messagebox.showerror("Error", "Target entry cannot be empty.") - return - is_running_pipeline = True - status_label.configure(text="[STEP 1/5] INITIALIZING TELEMETRY MATRIX...", text_color="#2980b9") - progress.start() - threading.Thread(target=_async_pipeline_worker, args=(user_input,), daemon=True).start() - -def safely_close_app(): - global is_running_pipeline - is_running_pipeline = False - root.quit() - -root = ctk.CTk() -root.title("ExoTransit Engine v5.3.3") -root.geometry("1100x750") - -if os.path.exists(icon_full_path): - try: - root.iconbitmap(icon_full_path) - except Exception: - pass - -root.protocol("WM_DELETE_WINDOW", safely_close_app) - -sidebar = ctk.CTkFrame(root, width=280, corner_radius=0) -sidebar.pack(side="left", fill="y") - -ctk.CTkLabel(sidebar, text="ExoTransit Vetting Pipeline Engine", font=("Segoe UI", 16, "bold"), text_color="#2980b9").pack(pady=(20, 2), padx=20, anchor="w") -ctk.CTkLabel(sidebar, text="Exo Planet Analyzer v5.3.3", font=("Segoe UI", 10), text_color="#7f8c8d").pack(pady=(0, 15), padx=20, anchor="w") - -ctk.CTkLabel(sidebar, text="Target Catalog Search Entry:", font=("Segoe UI", 11, "bold")).pack(padx=20, anchor="w") -star_search_entry = ctk.CTkEntry(sidebar, width=240, height=30, font=("Consolas", 12), placeholder_text="e.g., TRAPPIST-1 c, K2-18 b") -star_search_entry.pack(pady=5, padx=20, anchor="w") - -intel_frame = ctk.CTkFrame(sidebar, fg_color=("#eaeded", "#1c1c24"), corner_radius=6) -intel_frame.pack(pady=8, padx=20, fill="x") -info_label = ctk.CTkLabel(intel_frame, text="Host Star Temp : -- K\nSystem Distance: --\nTotal Planets : --\nTarget Planet : Standby", font=("Consolas", 11), justify="left", wraplength=220) -info_label.pack(pady=8, padx=10, anchor="w") - -metrics_frame = ctk.CTkFrame(sidebar, fg_color=("#eaeded", "#1c1c24"), corner_radius=6) -metrics_frame.pack(pady=8, padx=20, fill="x") -metrics_label = ctk.CTkLabel(metrics_frame, text="Orbit Period : -- Days\nTransit Depth : -- ppt\nPlanet Radius : -- x Earth Rad\nDiscovery Mode : Standby\nClassification : Standby", font=("Consolas", 11), justify="left", wraplength=220) -metrics_label.pack(pady=8, padx=10, anchor="w") - -launch_btn = ctk.CTkButton(sidebar, text="Search & Run Core", font=("Segoe UI", 12, "bold"), fg_color="#2980b9", width=240, height=35, command=run_pipeline) -launch_btn.pack(pady=4, padx=20, anchor="w") - -import_btn = ctk.CTkButton(sidebar, text="Import Local Dataset (CSV)", font=("Segoe UI", 12, "bold"), fg_color="#e67e22", hover_color="#d35400", width=240, height=35, command=import_local_csv_pipeline) -import_btn.pack(pady=4, padx=20, anchor="w") - -export_btn = ctk.CTkButton(sidebar, text="Export Data & Plots", font=("Segoe UI", 12, "bold"), fg_color="#27ae60", hover_color="#219653", width=240, height=35, command=export_pipeline_results) -export_btn.pack(pady=4, padx=20, anchor="w") - -progress = ctk.CTkProgressBar(sidebar, width=240, height=4, progress_color="#2980b9") -progress.set(0) -progress.pack(pady=5, padx=20, anchor="w") - -theme_btn = ctk.CTkButton(sidebar, text="Dark Mode", width=90, height=24, font=("Segoe UI", 9), command=toggle_theme) -theme_btn.pack(side="bottom", pady=15, padx=20, anchor="w") - -tab_view = ctk.CTkTabview(root, corner_radius=8) -tab_view.pack(side="right", fill="both", expand=True, padx=10, pady=10) -page1 = tab_view.add("Primary Light Curve Matrix") -page2 = tab_view.add("Statistical Morphology Models") - -status_label = ctk.CTkLabel(sidebar, text="CORE ENGINE STANDBY READY", font=("Consolas", 9), text_color="#7f8c8d") -status_label.pack(side="bottom", pady=(0, 5), padx=20, anchor="w") - -root.mainloop() diff --git a/Kepler-37b(1).png b/Kepler-37b(1).png new file mode 100644 index 0000000..2271846 Binary files /dev/null and b/Kepler-37b(1).png differ diff --git a/Kepler-37b(2).png b/Kepler-37b(2).png new file mode 100644 index 0000000..a3430d0 Binary files /dev/null and b/Kepler-37b(2).png differ diff --git a/Kepler-51b(1).png b/Kepler-51b(1).png new file mode 100644 index 0000000..917ccbd Binary files /dev/null and b/Kepler-51b(1).png differ diff --git a/Kepler-51b(2).png b/Kepler-51b(2).png new file mode 100644 index 0000000..03cd911 Binary files /dev/null and b/Kepler-51b(2).png differ diff --git a/README.md b/README.md deleted file mode 100644 index 9e75e09..0000000 --- a/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# ExoTransit-Vetting-Pipeline -An automated exoplanet transit detection pipeline built in Python. Features adaptive stellar variability filtering, Box Least Squares (BLS) periodogram spectral sweeps, and interactive multi-panel morphology models using Kepler and TESS space telescope telemetry - -# ExoTransit Engine v5.3.3 - -A lightweight, multi-threaded exoplanet discovery engine built in Python. The application automatically streams light curve telemetry from NASA's MAST archive (TESS/Kepler Kepler missions) to process, filter, and model planetary transit profiles. - -# Features -* Stellar Variability Filter: Adaptive flattening window to isolate transit signals from active starspot rotations. -* BLS Periodogram Sweep: Box Least Squares algorithm spectral power analysis to detect exact orbital intervals. -* Morphology Sub-Models: Interactive folding maps, high-resolution micro-scale transit geometry fits, and flux probability density histograms. -* Import and Export: The application features options to import and export planet data from .csv files and export .csv, .txt and images of the graphs. - -# Installation -1. Python -Make sure you have Python 3.10+ installed and added to your system's PATH. - -2. Automatic Dependency installation -The engine includes an automated setup script. You don't need to manually install dependencies. Just run the application file, and it will auto-deploy the required packages which are: -* customtkinter -* lightkurve -* astropy -* astroquery -* matplotlib -* numpy - -*Note: If you prefer manual installation, you can run in cmd: -pip install customtkinter lightkurve astropy astroquery matplotlib numpy - -## Gallery & Performance Matrix - - - - - - - - - - - - - - -
AU Mic b (Statistical Morphology)
TOI-143 b (Statistical Morphology)
diff --git a/Run_ExoTransit_mac_linux.command b/Run_ExoTransit_mac_linux.command deleted file mode 100644 index ac2f1d1..0000000 --- a/Run_ExoTransit_mac_linux.command +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -cd "$(dirname "$0")" -python3 "ExoTransit Engine.py" \ No newline at end of file diff --git a/Run_ExoTransit_win.bat b/Run_ExoTransit_win.bat deleted file mode 100644 index 6adba5b..0000000 --- a/Run_ExoTransit_win.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -python "ExoTransit Engine.py" -pause \ No newline at end of file diff --git a/TOI-143 b 2.png b/TOI-143 b 2.png deleted file mode 100644 index 0072f5a..0000000 Binary files a/TOI-143 b 2.png and /dev/null differ diff --git a/TOI-1452b(1).png b/TOI-1452b(1).png new file mode 100644 index 0000000..1daa9df Binary files /dev/null and b/TOI-1452b(1).png differ diff --git a/TOI-1452b(2).png b/TOI-1452b(2).png new file mode 100644 index 0000000..f8340b8 Binary files /dev/null and b/TOI-1452b(2).png differ diff --git a/app_icon.ico b/app_icon.ico deleted file mode 100644 index 00abe7e..0000000 Binary files a/app_icon.ico and /dev/null differ