Advertisement
phy_bunny

Satellite orbit change model

Feb 24th, 2025
188
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 12.46 KB | Science | 0 0
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from matplotlib.animation import FuncAnimation
  4. from matplotlib.patches import Circle as mpl_Circle
  5.  
  6. class RK4:
  7.     def __init__(self, ode, step_size):
  8.         self.ode = ode
  9.         self.step_size = step_size
  10.         self.k1 = np.zeros_like(ode.state)
  11.         self.k2 = np.zeros_like(ode.state)
  12.         self.k3 = np.zeros_like(ode.state)
  13.         self.k4 = np.zeros_like(ode.state)
  14.         self.temp_state = np.zeros_like(ode.state)
  15.        
  16.     def step(self):
  17.         h = self.step_size
  18.         state = self.ode.state
  19.        
  20.         self.ode.get_rate(state, self.k1)
  21.        
  22.         self.temp_state[:] = state + h/2 * self.k1
  23.         self.ode.get_rate(self.temp_state, self.k2)
  24.        
  25.         self.temp_state[:] = state + h/2 * self.k2
  26.         self.ode.get_rate(self.temp_state, self.k3)
  27.        
  28.         self.temp_state[:] = state + h * self.k3
  29.         self.ode.get_rate(self.temp_state, self.k4)
  30.        
  31.         self.ode.state += h/6 * (self.k1 + 2*self.k2 + 2*self.k3 + self.k4)
  32.  
  33. class Planet:
  34.     GM = 4 * np.pi**2
  35.  
  36.     def __init__(self):
  37.         self.state = np.zeros(5)
  38.         self.trail_x = []
  39.         self.trail_y = []
  40.         self.circle = mpl_Circle((0, 0), 0.1, color='blue', alpha=0.5)
  41.         self.ode_solver = None
  42.         self.energy_data = []
  43.         self.last_y = None
  44.         self.last_period_time = None
  45.         self.periods = []
  46.         self.area_rate = []
  47.         self.impulse_strength = 0.1  # 默认冲量强度
  48.         self.speed_multiplier = 1.1  # 速度变化倍率
  49.  
  50.        
  51.         # 新增极值检测相关变量
  52.         self.max_speed_points = []  # 存储速度最大点 (x, y, t, speed)
  53.         self.min_speed_points = []  # 存储速度最小点
  54.         self.prev_state = None      # 前一步状态
  55.         self.prev_r_dot = None      # 前一步的r_dot值
  56.  
  57.     def initialize(self, init_state, dt):
  58.         self.state = np.array(init_state)
  59.         self.trail_x = [self.state[0]]
  60.         self.trail_y = [self.state[2]]
  61.         self.circle.center = (self.state[0], self.state[2])
  62.         self.ode_solver = RK4(self, dt)
  63.         self.last_y = self.state[2]
  64.         self.last_period_time = None
  65.         self.periods = []
  66.         self.max_speed_points = []
  67.         self.min_speed_points = []
  68.         self.prev_state = None
  69.         self.prev_r_dot = None
  70.         self.area_rate = []
  71.  
  72.  
  73.     def get_rate(self, state, rate):
  74.         x, vx, y, vy, t = state
  75.         r = np.sqrt(x**2 + y**2)
  76.         r3 = r ** 3
  77.         ax = (-self.GM * x) / r3
  78.         ay = (-self.GM * y) / r3
  79.         rate[0] = vx
  80.         rate[1] = ax
  81.         rate[2] = vy
  82.         rate[3] = ay
  83.         rate[4] = 1
  84.  
  85.     def do_step(self):
  86.         if self.ode_solver:
  87.             self.ode_solver.step()
  88.             self.energy_data.append((self.state[4], self.compute_energy()))
  89.  
  90.         self.trail_x.append(self.state[0])
  91.         self.trail_y.append(self.state[2])
  92.         self.circle.center = (self.state[0], self.state[2])
  93.         self.period()
  94.         self.detect_speed_extremes()
  95.         self.compute_area()
  96.  
  97.     def apply_tangential_boost(self, multiplier):
  98.         """应用切向速度冲量"""
  99.         x, vx, y, vy, t = self.state
  100.         r = np.sqrt(x**2 + y**2)
  101.         if r < 1e-3:
  102.             return
  103.        
  104.         # 计算切向方向单位向量
  105.         tangential_dir = np.array([-y/r, x/r])
  106.        
  107.         # 计算当前切向速度分量
  108.         current_tangent_speed = vx * tangential_dir[0] + vy * tangential_dir[1]
  109.        
  110.         # 施加冲量后的新速度
  111.         new_vx = vx + (multiplier-1)*current_tangent_speed*tangential_dir[0]
  112.         new_vy = vy + (multiplier-1)*current_tangent_speed*tangential_dir[1]
  113.        
  114.         self.state[1] = new_vx
  115.         self.state[3] = new_vy
  116.         self.last_period_time = None
  117.         self.periods = []
  118.         self.last_y = y
  119.    
  120.     def detect_speed_extremes(self):
  121.         x, vx, y, vy, t = self.state
  122.         current_r_dot = x * vx + y * vy  # 计算r_dot(位置矢量与速度矢量的点积)
  123.         if self.prev_r_dot is not None:
  124.             # 检测r_dot符号变化,判断是否经过极值点
  125.             if self.prev_r_dot * current_r_dot < 0:
  126.                 # 比较绝对值确定更接近极值点的状态
  127.                 if abs(self.prev_r_dot) < abs(current_r_dot):
  128.                     x_prev, vx_prev, y_prev, vy_prev, t_prev = self.prev_state
  129.                     speed_prev = np.sqrt(vx_prev**2 + vy_prev**2)
  130.                     # 根据r_dot的正负判断极大或极小
  131.                     if self.prev_r_dot > 0:
  132.                         self.min_speed_points.append((x_prev, y_prev, t_prev, speed_prev))
  133.                     else:
  134.                         self.max_speed_points.append((x_prev, y_prev, t_prev, speed_prev))
  135.                 else:
  136.                     speed = np.sqrt(vx**2 + vy**2)
  137.                     if current_r_dot < 0:
  138.                         self.min_speed_points.append((x, y, t, speed))
  139.                     else:
  140.                         self.max_speed_points.append((x, y, t, speed))
  141.         # 保存当前状态用于下一步比较
  142.         self.prev_state = self.state.copy()
  143.         self.prev_r_dot = current_r_dot
  144.        
  145.         # 保存当前状态用于下一步比较
  146.         self.prev_state = self.state.copy()
  147.         self.prev_r_dot = current_r_dot
  148.  
  149.     def compute_energy(self):
  150.         x, vx, y, vy, t = self.state
  151.         r = np.sqrt(x**2 + y**2)
  152.         return 0.5*(vx**2 + vy**2) - self.GM/r
  153.    
  154.     def compute_area(self):
  155.         # 计算面积
  156.         x, vx, y, vy, t = self.state
  157.         current_area_dAdt = 0.5 * abs(x * vy - y * vx)
  158.         self.area_rate.append((t, current_area_dAdt))
  159.         return self.area_rate
  160.  
  161.     def period(self):
  162.         # 周期检测
  163.         x, _, y, _, t = self.state
  164.         if self.last_y is not None and y * self.last_y < 0:
  165.             if x > 0:
  166.                 if self.last_period_time is not None:
  167.                     period = t - self.last_period_time
  168.                     self.periods.append(period)
  169.                     print(f"检测到周期: {period:.5f} y")  # 立即输出周期
  170.                 self.last_period_time = t
  171.         self.last_y = y
  172.         return self.period
  173.    
  174.     def compute_semi_major_axis(self):
  175.         if len(self.max_speed_points) > 0 and len(self.min_speed_points) > 0:
  176.             # 获取最近的速度极值点
  177.             max_point = self.max_speed_points[-1]
  178.             min_point = self.min_speed_points[-1]
  179.            
  180.             # 计算距离
  181.             r_max = np.sqrt(max_point[0]**2 + max_point[1]**2)
  182.             r_min = np.sqrt(min_point[0]**2 + min_point[1]**2)
  183.            
  184.             # 计算半长轴
  185.             semi_major_axis = (r_max + r_min) / 2
  186.             return semi_major_axis
  187.         return None
  188.  
  189. class PlanetSimulation:
  190.     def __init__(self):
  191.         self.fig, (self.ax1, self.ax2, self.ax3) = plt.subplots(1, 3, figsize=(24, 12))
  192.         self.ax1.set(xlim=(-3, 10), ylim=(-5, 5), aspect='equal',
  193.                     xlabel="x (AU)", ylabel="y (AU)", title="Planet Simulation")
  194.         self.ax2.set(xlabel="Time (yr)", ylabel="Energy (AU²/yr²)",
  195.                     title="Energy vs Time", xlim=(0, 10), ylim=(-40, 0))
  196.         self.sun = mpl_Circle((0, 0), 0.2, color='yellow', alpha=1.0)
  197.         self.ax1.add_patch(self.sun)
  198.         self.planet = Planet()
  199.         self.ax1.add_patch(self.planet.circle)
  200.         self.trail, = self.ax1.plot([], [], 'b-', lw=0.5)
  201.         self.energy_line, = self.ax2.plot([], [], 'r-', lw=1)
  202.        
  203.         self.ax3.set(xlabel="Time (yr)", ylabel="dA/dt (AU²/yr)",
  204.         title="Area Rate Verification",
  205.         xlim=(0, 10), ylim=(0, 5))
  206.         self.area_line, = self.ax3.plot([], [], 'b-', lw=1)
  207.         self.connect_events()
  208.        
  209.         # 添加半长轴显示文本
  210.         self.semi_major_axis_text = self.ax1.text(0.95, 0.85, '',
  211.                                                  transform=self.ax1.transAxes,
  212.                                                  ha='right', va='top',
  213.                                                  fontsize=12,
  214.                                                  bbox=dict(facecolor='white', alpha=0.5))
  215.        
  216.         # 添加周期显示文本
  217.         self.period_text = self.ax1.text(0.95, 0.95, '',
  218.                                         transform=self.ax1.transAxes,
  219.                                         ha='right', va='top',
  220.                                         fontsize=12,
  221.                                         bbox=dict(facecolor='white', alpha=0.5))
  222.         self.last_period_count = 0  # 跟踪已显示的周期数量
  223.  
  224.         self.max_scatter = self.ax1.scatter([], [], color='red', marker='*',
  225.                                            s=100, label='Max Speed')
  226.         self.min_scatter = self.ax1.scatter([], [], color='green', marker='*',
  227.                                            s=100, label='Min Speed')
  228.         self.ax1.legend()
  229.         # 在AX1中添加文本显示
  230.         self.area_text = self.ax1.text(0.95, 0.65, '',
  231.                                     transform=self.ax1.transAxes,
  232.                                     ha='right', va='top',
  233.                                     fontsize=10,
  234.                                     bbox=dict(facecolor='white', alpha=0.5))
  235.  
  236.         self.dt = 0.01
  237.         self.steps_per_frame = 5
  238.  
  239.     def initialize(self, x, vx, y, vy, dt):
  240.         self.planet.initialize([x, vx, y, vy, 0], dt)
  241.         self.trail.set_data(self.planet.trail_x, self.planet.trail_y)
  242.         self.energy_line.set_data([], [])
  243.  
  244.     def connect_events(self):
  245.         """连接所有交互事件"""
  246.         self.fig.canvas.mpl_connect('button_press_event', self.on_mouse_click)
  247.    
  248.     def on_mouse_click(self, event):
  249.         """鼠标点击事件处理"""
  250.         click_x, click_y = event.xdata, event.ydata
  251.         self.planet.apply_tangential_boost(self.planet.speed_multiplier)
  252.            
  253.         # 更新冲量显示
  254.         current_speed = np.sqrt(self.planet.state[1]**2 + self.planet.state[3]**2)
  255.         self.impulse_text.set_text(f'Speed: {current_speed:.2f} AU/yr\n'+
  256.                                       f'Last boost: {self.planet.speed_multiplier}x')
  257.            
  258.         # 显示视觉反馈
  259.         self.ax1.plot(click_x, click_y, 'r*', markersize=15, alpha=0.5)
  260.         self.fig.canvas.draw_idle()
  261.  
  262.     def update(self, frame):
  263.         for _ in range(self.steps_per_frame):
  264.             self.planet.do_step()
  265.        
  266.         self.trail.set_data(self.planet.trail_x, self.planet.trail_y)
  267.        
  268.         # 更新能量曲线
  269.         if self.planet.energy_data:
  270.             times, energies = zip(*self.planet.energy_data)
  271.             self.energy_line.set_data(times, energies)
  272.             self.ax2.set_xlim(0, max(times))
  273.             self.ax2.set_ylim(min(energies)-1, max(energies)+1)
  274.        
  275.         # 更新极值点标记
  276.         max_points = np.array([[p[0], p[1]] for p in self.planet.max_speed_points])
  277.         min_points = np.array([[p[0], p[1]] for p in self.planet.min_speed_points])
  278.         if len(max_points) > 0:
  279.             self.max_scatter.set_offsets(max_points)
  280.         if len(min_points) > 0:
  281.             self.min_scatter.set_offsets(min_points)
  282.        
  283.         # 更新周期显示
  284.         current_count = len(self.planet.periods)
  285.         if current_count > self.last_period_count:
  286.             latest_period = self.planet.periods[-1]
  287.             self.period_text.set_text(f'Period: {latest_period:.2f} yr')
  288.             self.last_period_count = current_count
  289.        
  290.         # 更新半长轴显示
  291.         semi_major_axis = self.planet.compute_semi_major_axis()
  292.         if semi_major_axis is not None:
  293.             self.semi_major_axis_text.set_text(f'Semi-major Axis: {semi_major_axis:.2f} AU')
  294.        
  295.         # 更新面积率图
  296.         if self.planet.area_rate:
  297.             times, dAdt = zip(*self.planet.area_rate)
  298.             self.area_line.set_data(times, dAdt)
  299.             self.ax3.set_xlim(0, max(times))
  300.  
  301.         return [self.trail, self.planet.circle, self.energy_line,
  302.                 self.max_scatter, self.min_scatter, self.period_text,
  303.                 self.semi_major_axis_text, self.area_line]
  304.  
  305.  
  306.     def run(self):
  307.         ani = FuncAnimation(self.fig, self.update,frames=200, interval=50, blit=True)
  308.         plt.show()
  309.  
  310.  
  311. if __name__ == "__main__":
  312.     sim = PlanetSimulation()
  313.     sim.initialize(x=1.0, vx=0.0, y=0.0, vy=6.28, dt=0.01)
  314.     sim.run()
  315.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement