#!/usr/bin/env python """广寒计划发射窗口计算 广寒基地往返标准 LLO,每月有 2 个标准窗口。 地月转移耗时 5 天。给定任意时间,计算最近的地球出发发射窗口 及对应的月球着陆窗口。 """ import math import sys from datetime import datetime, timedelta, timezone MOON_PERIOD = timedelta(days=27.32166) # 月球自转周期 TRANSIT_DAYS = timedelta(days=5) # 地月转移耗时 # 参考着陆窗口(UTC)—— 发射窗口 = 着陆窗口 − 5 天地月转移 # A窗: 2025-05-14 04:09 着陆 校准 (k=8) # B窗: 2025-03-15 02:22 着陆 校准 (k=5) REF_LANDING_A = datetime(2024, 10, 7, 14, 23, tzinfo=timezone.utc) REF_LANDING_B = datetime(2024, 10, 29, 11, 46, tzinfo=timezone.utc) REF_LAUNCH_A = REF_LANDING_A - TRANSIT_DAYS REF_LAUNCH_B = REF_LANDING_B - TRANSIT_DAYS def parse_dt(s: str) -> datetime: """解析用户输入的日期时间字符串,自动补全缺失部分。""" s = s.strip() formats = [ "%Y-%m-%d %H:%M", "%Y-%m-%d %H", "%Y-%m-%d", "%Y/%m/%d %H:%M", "%Y/%m/%d %H", "%Y/%m/%d", ] for fmt in formats: try: return datetime.strptime(s, fmt).replace(tzinfo=timezone.utc) except ValueError: continue raise ValueError(f"无法解析日期: {s}(支持格式: YYYY-MM-DD HH:MM 或 YYYY/MM/DD)") def next_occurrences(ref: datetime, period: timedelta, after: datetime, count: int = 3): """计算从 after 之后最近的 count 个窗口(以 ref 为相位零点)。""" n = math.floor((after - ref) / period) # 已完成周期数(floor确保负数时正确) results = [] for i in range(1, count + 1): results.append(ref + (n + i) * period) return results def guanghan_windows(target: datetime, count: int = 4): """返回最近 count 个广寒发射/着陆窗口。 以着陆窗口为相位零点,发射窗口 = 着陆窗口 − 5 天地月转移。 """ landing_a = next_occurrences(REF_LANDING_A, MOON_PERIOD, target, count) landing_b = next_occurrences(REF_LANDING_B, MOON_PERIOD, target, count) # 合并着陆窗口,去重排序 all_landings = sorted(set(landing_a + landing_b)) lines = [] lines.append(f"查询时间: {target.strftime('%Y-%m-%d %H:%M UTC')}") lines.append(f"参考着陆窗 A: {REF_LANDING_A.strftime('%Y-%m-%d %H:%M UTC')}") lines.append(f"参考着陆窗 B: {REF_LANDING_B.strftime('%Y-%m-%d %H:%M UTC')}") lines.append(f"月球周期: {MOON_PERIOD.days}.{MOON_PERIOD.seconds/3600:.2f} 天") lines.append(f"地月转移: 5 天") lines.append("") header = f"{'#':<4} {'发射 (UTC)':<22} {'着陆 (UTC)':<22} {'距现在':>10}" lines.append(header) lines.append("-" * len(header)) for i, landing in enumerate(all_landings[:count], 1): launch = landing - TRANSIT_DAYS delta = launch - target if delta.days >= 0: delta_str = f"{delta.days}d {delta.seconds // 3600}h" else: delta_str = f"已过" lines.append(f"{i:<4} {launch.strftime('%Y-%m-%d %H:%M'):<22} {landing.strftime('%Y-%m-%d %H:%M'):<22} {delta_str:>10}") return "\n".join(lines) def main(): if len(sys.argv) < 2: print("用法: python guanghan_windows.py <日期时间>") print("示例: python guanghan_windows.py '2024-07-20'") print(" python guanghan_windows.py '2024-08-15 12:00'") sys.exit(1) target = parse_dt(sys.argv[1]) print(guanghan_windows(target)) if __name__ == "__main__": main()