From 64779434ad00746f904988c9416006b8b4765bfe Mon Sep 17 00:00:00 2001 From: march3 Date: Sat, 5 Aug 2023 18:58:02 +0800 Subject: [PATCH] =?UTF-8?q?Python=E8=B6=85=E4=BA=BA-=E5=AE=87=E5=AE=99?= =?UTF-8?q?=E6=A8=A1=E6=8B=9F=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bodies/body.py | 4 +- bodies/earth.py | 3 +- common/celestial_data_service.py | 42 +++++- .../solar_system/solar_system_reality.py | 132 ++++++++++++++---- simulators/ursina/entities/body_timer.py | 2 + simulators/ursina/entities/entity_utils.py | 6 +- 6 files changed, 149 insertions(+), 40 deletions(-) diff --git a/bodies/body.py b/bodies/body.py index 037dbe8..6a634e9 100644 --- a/bodies/body.py +++ b/bodies/body.py @@ -26,7 +26,7 @@ class Body(metaclass=ABCMeta): text_color=None, texture=None, size_scale=1.0, distance_scale=1.0, rotation_speed=None, parent=None, ignore_mass=False, - is_fixed_star=False, trail_color=None, show_name=False): + is_fixed_star=False, show_trail=True,trail_color=None, show_name=False): """ 天体类 @param name: 天体名称 @@ -44,6 +44,7 @@ class Body(metaclass=ABCMeta): @param ignore_mass: 是否忽略质量(如果为True,则不计算引力) TODO: 注意:这里的算法是基于牛顿的万有引力(质量为0不受引力的影响在天体物理学中是不严谨) @param is_fixed_star: 是否为恒星 + @param show_trail: 天体拖尾是否显示 @param trail_color: 天体拖尾颜色(默认天体颜色) @param show_name: 是否显示天体名称 """ @@ -81,6 +82,7 @@ class Body(metaclass=ABCMeta): self.__rotation_speed = rotation_speed self.color = color + self.show_trail = show_trail self.trail_color = color if trail_color is None else trail_color self.texture = texture diff --git a/bodies/earth.py b/bodies/earth.py index 00f18ea..e1ea7dd 100644 --- a/bodies/earth.py +++ b/bodies/earth.py @@ -29,7 +29,7 @@ class Earth(Body): color=(7, 0, 162), texture="earth1.jpg", text_color=None, size_scale=1.0, distance_scale=1.0, rotation_speed=15, ignore_mass=False, - trail_color=None, show_name=False, + show_trail=True, trail_color=None, show_name=False, parent=None): params = { "name": name, @@ -45,6 +45,7 @@ class Earth(Body): "distance_scale": distance_scale, "rotation_speed": rotation_speed, "ignore_mass": ignore_mass, + "show_trail": show_trail, "trail_color": trail_color, "show_name": show_name, "parent": parent diff --git a/common/celestial_data_service.py b/common/celestial_data_service.py index 0db9a05..a60ee79 100644 --- a/common/celestial_data_service.py +++ b/common/celestial_data_service.py @@ -6,11 +6,31 @@ # link :https://gitcode.net/pythoncr/ # python_version :3.8 # ============================================================================== +import math +from common.consts import G +from bodies import Body + + +def calc_solar_acceleration(body_or_pos, big_body): + if isinstance(body_or_pos, Body): + body_pos = body_or_pos.position + else: + body_pos = body_or_pos + x, y, z = body_pos[0] * 1000 - big_body.position[0] * 1000, \ + body_pos[1] * 1000 - big_body.position[1] * 1000, \ + body_pos[2] * 1000 - big_body.position[2] * 1000 + r = math.sqrt(x ** 2 + y ** 2 + z ** 2) + a = G * big_body.mass / r ** 2 + ax = -a * x / r + ay = -a * y / r + az = -a * z / r + return [ax / 1000, ay / 1000, az / 1000] # 设置天体的加速度(单位:km/s²) def get_body_posvel(body, time=None): """ 获取太阳系天体指定时间的位置和矢量速度 + pip install -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com de423 @param body: 天体(天体名称) @param time: 时间 @return: @@ -59,6 +79,7 @@ def recalc_moon_position(moon_posvel, earth_pos): def get_celestial_body_data(body_name): + # pip install ephem import ephem # 创建一个Observer对象,用于指定观测者的位置 observer = ephem.Observer() @@ -75,8 +96,19 @@ def get_celestial_body_data(body_name): velocity = (body.ra_velocity, body.dec_velocity) # 天体的赤经速度和赤纬速度 return position, velocity - # # 示例用法 - # body_name = 'Mars' # 天体名称,这里以火星为例 - # position, velocity = get_celestial_body_data(body_name) - # print(f"The current position of {body_name} is: {position}") - # print(f"The current velocity of {body_name} is: {velocity}") + +# pip install Astropysics + +if __name__ == '__main__': + # pip install astropy + from astropy.coordinates import get_body_barycentric_posvel + from astropy.time import Time + # from astropy.units. import Unit + + from common.consts import AU, SECONDS_PER_DAY + + t = Time.now() + print("日期时间:", t) + posvel = get_body_barycentric_posvel('earth', t) + print("坐标(公里):", posvel[0] * AU) + print("速度(公里/秒):", posvel[1] * AU / SECONDS_PER_DAY) diff --git a/sim_scenes/solar_system/solar_system_reality.py b/sim_scenes/solar_system/solar_system_reality.py index 28b501e..294a8b2 100644 --- a/sim_scenes/solar_system/solar_system_reality.py +++ b/sim_scenes/solar_system/solar_system_reality.py @@ -6,13 +6,11 @@ # link :https://gitcode.net/pythoncr/ # python_version :3.8 # ============================================================================== -# pip install -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com de423 - import numpy as np from bodies import Sun, Mercury, Venus, Earth, Mars, Asteroids, Jupiter, Saturn, Uranus, Neptune, Moon -from common.celestial_data_service import get_body_posvel, recalc_moon_position +from common.celestial_data_service import get_body_posvel, recalc_moon_position, calc_solar_acceleration from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, AU from sim_scenes.func import ursina_run from simulators.ursina.entities.body_timer import TimeData @@ -32,22 +30,37 @@ class SolarSystemRealitySim: self.clock_position_center = False self.show_earth_clouds = False self.debug_mode = False + self.recalc_moon_pos = True def create_bodies(self): """ 创建太阳系的天体 @return: """ + # 由于宇宙空间尺度非常大,如果按照实际的天体大小,则无法看到天体,因此需要对天体的尺寸进行放大 + # 太阳缩放比例 self.sun_size_scale = 0.04e2 if self.debug_mode else 0.4e2 - self.earth_size_scale = 10e3 if self.debug_mode else 1e3 + + # 地月缩放比例 + # 为了好的展示效果,需要对月球的位置重新计算(地月距离放大,月球相对地球方向不变),重新计算位置后,地球和月球可以放大1000倍以上 + if self.recalc_moon_pos: # 重新计算月球位置 + self.earth_size_scale = 10e3 if self.debug_mode else 1e3 + self.moon_size_scale = 2e3 + else: + # 不重新计算,则地月的距离相对整个太阳系会非常近,因此,月球只放大了10倍 + self.earth_size_scale = 1e1 + self.moon_size_scale = 1e1 self.sun = Sun(name="太阳", size_scale=self.sun_size_scale) # 太阳 self.mercury = Mercury(name="水星", size_scale=1.5e3) # 水星 self.venus = Venus(name="金星", size_scale=1e3) # 金星 - self.earth = Earth(name="地球", texture="earth_hd.jpg", size_scale=self.earth_size_scale) # 地球 - self.earth_clouds = Earth(name="地球云层", texture="transparent_clouds.png", + self.earth = Earth(name="地球", texture="earth_hd.jpg", + rotate_angle=3.44, + size_scale=self.earth_size_scale) # 地球 + self.earth_clouds = Earth(name="地球云层", texture="transparent_clouds.png", show_trail=False, + rotate_angle=3.44, size_scale=self.earth_size_scale * 1.01) # 地球云层 - self.moon = Moon(name="月球", size_scale=2e3) # 月球 + self.moon = Moon(name="月球", size_scale=self.moon_size_scale) # 月球 self.mars = Mars(name="火星", size_scale=1.2e3) # 火星 self.asteroids = Asteroids(size_scale=1e2, parent=self.sun, rotate_angle=-20) # 模拟的小行星带 self.jupiter = Jupiter(name="木星", size_scale=4e2) # 木星 @@ -90,19 +103,32 @@ class SolarSystemRealitySim: # 设置后,可以调整鼠标键盘的控制速度 application.time_scale = 2 - def show_timer_text(self, time_data): - dt = time_data.get_datetime(str(self.start_time)) + def set_earth_rotation(self, dt): + """ + 根据指定的时间控制地球的旋转角度(保证地球的自转和北京时间同步) + @param dt: 时间 datetime + @return: + """ - # 需要按照时间和日期控制地球的自转,不能随意转动 - # 日期是当年的第几天 + # timetuple 可以获取当天的小时数、分数钟、秒数 timetuple = dt.timetuple() - - # 计算出:日期当天的偏转角度 + # 当年的第几天 day_of_year = timetuple.tm_yday + # 根据当年的第几天计算出该日期当天的偏转角度:360度 / 365天 = 当天的偏转角度 angle_of_day = day_of_year * (360 / 365) + # 计算出精确的小时数 total_hours = timetuple.tm_hour + timetuple.tm_min / 60 + timetuple.tm_sec / 60 / 60 - self.earth.planet.rotation_y = -total_hours * 15 - angle_of_day - # print(time_data.get_datetime(str(self.start_time))) + # -total_hours: 负号控制地球的旋转方向、1天24小时,360度/24=15 + # total_hours * 15:1天24小时,360度/24小时=1小时15度 + # angle_of_day: 1年第几天的角度 + self.earth.planet.rotation_y = -total_hours * 15 - angle_of_day + 15 # 精确调整 + + def show_clock(self, dt): + """ + 显示时钟 + @param dt: 时间 datetime + @return: + """ if self.clock_position_center: position, origin = (0, .25), (0, 0), else: @@ -114,9 +140,9 @@ class SolarSystemRealitySim: font="verdana.ttf", close_time=-1) - def on_timer_changed(self, time_data: TimeData): + def set_bodies_position(self, time_data: TimeData): """ - 时时刻刻运行 + 设置天体的位置(包含速度和加速度的信息) @param time_data: @return: """ @@ -133,7 +159,10 @@ class SolarSystemRealitySim: posvel = get_body_posvel(body, t) if isinstance(body, Moon): # 如果是月球,为了好的展示效果,需要对月球的位置重新计算 - posvel = recalc_moon_position(posvel, earth_pos) + moon_real_pos = [posvel[0].x.value * AU, posvel[0].z.value * AU, posvel[0].y.value * AU] + # TODO:注释下行,月球就会在真实的位置 + if self.recalc_moon_pos: + posvel = recalc_moon_position(posvel, earth_pos) if posvel is None: # posvel 为空,则使用太阳的坐标 @@ -151,14 +180,37 @@ class SolarSystemRealitySim: body.position = np.array(position) body.velocity = np.array(velocity) - if isinstance(body, Earth): - # 记录地球的位置 - earth_pos = posvel[0] + if isinstance(body, Asteroids): + pass elif isinstance(body, Sun): # 记录太阳的位置 sun_pos = posvel[0] + elif isinstance(body, Moon): + # 月球受到2个影响比较大的天体引力(地球和太阳),计算引力引起的加速度和 + acc_earth = calc_solar_acceleration(moon_real_pos, self.earth) + acc_sun = calc_solar_acceleration(moon_real_pos, self.sun) + body.acceleration = [acc_earth[0] + acc_sun[0], + acc_earth[1] + acc_sun[1], + acc_earth[2] + acc_sun[2]] + else: + # 其他天体受到太阳引力 + body.acceleration = calc_solar_acceleration(body, self.sun) - self.show_timer_text(time_data) + if isinstance(body, Earth): + # 记录地球的位置 + earth_pos = posvel[0] + + def on_timer_changed(self, time_data: TimeData): + """ + 时时刻刻运行 + @param time_data: + @return: + """ + dt = time_data.get_datetime(str(self.start_time)) + # 设置天体的位置(包含速度和加速度的信息) + self.set_bodies_position(time_data) + self.set_earth_rotation(dt) + self.show_clock(dt) def bind_events(self): # 运行中,每时每刻都会触发 on_timer_changed @@ -171,22 +223,38 @@ class SolarSystemRealitySim: start_time=None, show_asteroids=False, show_earth_clouds=False, + recalc_moon_pos=True, clock_position_center=False): """ 模拟运行 + @param debug_mode: 是否调试模式 + @param start_time: 运行的开始时间 + @param show_asteroids: 是否显示小行星带 + @param show_earth_clouds: 地球是否显示云层(图片效果,不是真实的云层) + @param recalc_moon_pos: 为了好的展示效果,需要对月球的位置重新计算(地月距离放大,月球相对地球方向不变) + @param clock_position_center: 时钟是否显示在中间 @return: """ + self.recalc_moon_pos = recalc_moon_pos self.debug_mode = debug_mode self.clock_position_center = clock_position_center self.show_asteroids = show_asteroids self.show_earth_clouds = show_earth_clouds - self.create_bodies() # 创建太阳系天体 - self.init_earth() # 初始化地球 - self.bind_events() # 绑定事件 - + # 创建太阳系天体 + self.create_bodies() + # 对地球进行初始化 + self.init_earth() + # 绑定事件 + self.bind_events() + + from astropy.time import Time + from datetime import datetime + # 开始时间为空,则默认为当前时间 if start_time is None: - from astropy.time import Time self.start_time = Time.now() # 获取默认开始时间为当前时间 + elif isinstance(start_time, str): + self.start_time = Time(datetime.strptime(start_time + '+0800', '%Y-%m-%d %H:%M:%S%z'), + format='datetime') dt = SECONDS_PER_DAY # 1秒=1天 dt = 1 # 1秒=1秒 @@ -203,10 +271,12 @@ class SolarSystemRealitySim: if __name__ == '__main__': # 以下展示的效果为太阳系真实的时间和位置 - # 由于宇宙空间尺度非常大,如果按照实际的天体大小,则无法看到天体,因此需要对天体的尺寸进行放大 - sim = SolarSystemRealitySim() sim.run( - debug_mode=True, - clock_position_center=True + # debug_mode=True, # 是否调试模式 + # start_time='2023-08-05 19:20:00', # 指定运行的开始时间,不指定为当前时间 + # show_asteroids=True, # 是否显示小行星带(图片模拟) + # show_earth_clouds=True, # 地球是否显示云层(图片效果,不是真实的云层) + # recalc_moon_pos=False, # 为了好的展示效果,需要对月球的位置重新计算(地月距离放大,月球相对地球方向不变) + # clock_position_center=True # 时钟是否显示在中间 ) diff --git a/simulators/ursina/entities/body_timer.py b/simulators/ursina/entities/body_timer.py index 24e965f..9340237 100644 --- a/simulators/ursina/entities/body_timer.py +++ b/simulators/ursina/entities/body_timer.py @@ -63,6 +63,8 @@ class TimeData: def get_datetime(self, init_datetime): import datetime # UTC_format = "%Y-%m-%dT%H:%M:%S.%fZ" + if len(init_datetime) == 19: + init_datetime = init_datetime + ".000" UTC = datetime.datetime.strptime(init_datetime + "Z", "%Y-%m-%d %H:%M:%S.%fZ") # BJS_format = "%Y-%m-%d %H:%M:%S" BJS = UTC + datetime.timedelta(hours=8+self.total_hours) diff --git a/simulators/ursina/entities/entity_utils.py b/simulators/ursina/entities/entity_utils.py index 785ff7d..aea3d49 100644 --- a/simulators/ursina/entities/entity_utils.py +++ b/simulators/ursina/entities/entity_utils.py @@ -261,7 +261,7 @@ def create_rings(parent): # 创建行星环 torus = create_torus(0.7, 1.2, 64) parent.ring = Entity(parent=parent, model=torus, texture=rings_texture, scale=1, - rotation=(parent.ring_rotation_x, 0, 0), double_sided=True) + rotation=(parent.ring_rotation_x, 0, 0), double_sided=True) # 设置行星环不受灯光影响,否则看不清行星环 parent.ring.set_light_off() @@ -327,7 +327,9 @@ def create_fixed_star_lights(fixed_star): if fixed_star.body_view.body.light_on: for i in range(2): # 创建 PointLight 对象,作为恒星的灯光源 - light = PointLight(parent=fixed_star, intensity=10, range=10, color=color.white) + light = PointLight(parent=fixed_star, # model="sphere", + scale=10, + intensity=10, range=10, color=color.white) def get_value_direction_vectors(vectors): -- GitLab