提交 64779434 编写于 作者: 三月三net's avatar 三月三net

Python超人-宇宙模拟器

上级 ace3b911
...@@ -26,7 +26,7 @@ class Body(metaclass=ABCMeta): ...@@ -26,7 +26,7 @@ class Body(metaclass=ABCMeta):
text_color=None, text_color=None,
texture=None, size_scale=1.0, distance_scale=1.0, texture=None, size_scale=1.0, distance_scale=1.0,
rotation_speed=None, parent=None, ignore_mass=False, 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: 天体名称 @param name: 天体名称
...@@ -44,6 +44,7 @@ class Body(metaclass=ABCMeta): ...@@ -44,6 +44,7 @@ class Body(metaclass=ABCMeta):
@param ignore_mass: 是否忽略质量(如果为True,则不计算引力) @param ignore_mass: 是否忽略质量(如果为True,则不计算引力)
TODO: 注意:这里的算法是基于牛顿的万有引力(质量为0不受引力的影响在天体物理学中是不严谨) TODO: 注意:这里的算法是基于牛顿的万有引力(质量为0不受引力的影响在天体物理学中是不严谨)
@param is_fixed_star: 是否为恒星 @param is_fixed_star: 是否为恒星
@param show_trail: 天体拖尾是否显示
@param trail_color: 天体拖尾颜色(默认天体颜色) @param trail_color: 天体拖尾颜色(默认天体颜色)
@param show_name: 是否显示天体名称 @param show_name: 是否显示天体名称
""" """
...@@ -81,6 +82,7 @@ class Body(metaclass=ABCMeta): ...@@ -81,6 +82,7 @@ class Body(metaclass=ABCMeta):
self.__rotation_speed = rotation_speed self.__rotation_speed = rotation_speed
self.color = color self.color = color
self.show_trail = show_trail
self.trail_color = color if trail_color is None else trail_color self.trail_color = color if trail_color is None else trail_color
self.texture = texture self.texture = texture
......
...@@ -29,7 +29,7 @@ class Earth(Body): ...@@ -29,7 +29,7 @@ class Earth(Body):
color=(7, 0, 162), texture="earth1.jpg", text_color=None, color=(7, 0, 162), texture="earth1.jpg", text_color=None,
size_scale=1.0, distance_scale=1.0, size_scale=1.0, distance_scale=1.0,
rotation_speed=15, ignore_mass=False, rotation_speed=15, ignore_mass=False,
trail_color=None, show_name=False, show_trail=True, trail_color=None, show_name=False,
parent=None): parent=None):
params = { params = {
"name": name, "name": name,
...@@ -45,6 +45,7 @@ class Earth(Body): ...@@ -45,6 +45,7 @@ class Earth(Body):
"distance_scale": distance_scale, "distance_scale": distance_scale,
"rotation_speed": rotation_speed, "rotation_speed": rotation_speed,
"ignore_mass": ignore_mass, "ignore_mass": ignore_mass,
"show_trail": show_trail,
"trail_color": trail_color, "trail_color": trail_color,
"show_name": show_name, "show_name": show_name,
"parent": parent "parent": parent
......
...@@ -6,11 +6,31 @@ ...@@ -6,11 +6,31 @@
# link :https://gitcode.net/pythoncr/ # link :https://gitcode.net/pythoncr/
# python_version :3.8 # 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): def get_body_posvel(body, time=None):
""" """
获取太阳系天体指定时间的位置和矢量速度 获取太阳系天体指定时间的位置和矢量速度
pip install -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com de423
@param body: 天体(天体名称) @param body: 天体(天体名称)
@param time: 时间 @param time: 时间
@return: @return:
...@@ -59,6 +79,7 @@ def recalc_moon_position(moon_posvel, earth_pos): ...@@ -59,6 +79,7 @@ def recalc_moon_position(moon_posvel, earth_pos):
def get_celestial_body_data(body_name): def get_celestial_body_data(body_name):
# pip install ephem
import ephem import ephem
# 创建一个Observer对象,用于指定观测者的位置 # 创建一个Observer对象,用于指定观测者的位置
observer = ephem.Observer() observer = ephem.Observer()
...@@ -75,8 +96,19 @@ def get_celestial_body_data(body_name): ...@@ -75,8 +96,19 @@ def get_celestial_body_data(body_name):
velocity = (body.ra_velocity, body.dec_velocity) # 天体的赤经速度和赤纬速度 velocity = (body.ra_velocity, body.dec_velocity) # 天体的赤经速度和赤纬速度
return position, velocity return position, velocity
# # 示例用法
# body_name = 'Mars' # 天体名称,这里以火星为例 # pip install Astropysics
# position, velocity = get_celestial_body_data(body_name)
# print(f"The current position of {body_name} is: {position}") if __name__ == '__main__':
# print(f"The current velocity of {body_name} is: {velocity}") # 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)
...@@ -6,13 +6,11 @@ ...@@ -6,13 +6,11 @@
# link :https://gitcode.net/pythoncr/ # link :https://gitcode.net/pythoncr/
# python_version :3.8 # python_version :3.8
# ============================================================================== # ==============================================================================
# pip install -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com de423
import numpy as np import numpy as np
from bodies import Sun, Mercury, Venus, Earth, Mars, Asteroids, Jupiter, Saturn, Uranus, Neptune, Moon 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 common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, AU
from sim_scenes.func import ursina_run from sim_scenes.func import ursina_run
from simulators.ursina.entities.body_timer import TimeData from simulators.ursina.entities.body_timer import TimeData
...@@ -32,22 +30,37 @@ class SolarSystemRealitySim: ...@@ -32,22 +30,37 @@ class SolarSystemRealitySim:
self.clock_position_center = False self.clock_position_center = False
self.show_earth_clouds = False self.show_earth_clouds = False
self.debug_mode = False self.debug_mode = False
self.recalc_moon_pos = True
def create_bodies(self): def create_bodies(self):
""" """
创建太阳系的天体 创建太阳系的天体
@return: @return:
""" """
# 由于宇宙空间尺度非常大,如果按照实际的天体大小,则无法看到天体,因此需要对天体的尺寸进行放大
# 太阳缩放比例
self.sun_size_scale = 0.04e2 if self.debug_mode else 0.4e2 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.sun = Sun(name="太阳", size_scale=self.sun_size_scale) # 太阳
self.mercury = Mercury(name="水星", size_scale=1.5e3) # 水星 self.mercury = Mercury(name="水星", size_scale=1.5e3) # 水星
self.venus = Venus(name="金星", size_scale=1e3) # 金星 self.venus = Venus(name="金星", size_scale=1e3) # 金星
self.earth = Earth(name="地球", texture="earth_hd.jpg", size_scale=self.earth_size_scale) # 地球 self.earth = Earth(name="地球", texture="earth_hd.jpg",
self.earth_clouds = Earth(name="地球云层", texture="transparent_clouds.png", 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) # 地球云层 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.mars = Mars(name="火星", size_scale=1.2e3) # 火星
self.asteroids = Asteroids(size_scale=1e2, parent=self.sun, rotate_angle=-20) # 模拟的小行星带 self.asteroids = Asteroids(size_scale=1e2, parent=self.sun, rotate_angle=-20) # 模拟的小行星带
self.jupiter = Jupiter(name="木星", size_scale=4e2) # 木星 self.jupiter = Jupiter(name="木星", size_scale=4e2) # 木星
...@@ -90,19 +103,32 @@ class SolarSystemRealitySim: ...@@ -90,19 +103,32 @@ class SolarSystemRealitySim:
# 设置后,可以调整鼠标键盘的控制速度 # 设置后,可以调整鼠标键盘的控制速度
application.time_scale = 2 application.time_scale = 2
def show_timer_text(self, time_data): def set_earth_rotation(self, dt):
dt = time_data.get_datetime(str(self.start_time)) """
根据指定的时间控制地球的旋转角度(保证地球的自转和北京时间同步)
@param dt: 时间 datetime
@return:
"""
# 需要按照时间和日期控制地球的自转,不能随意转动 # timetuple 可以获取当天的小时数、分数钟、秒数
# 日期是当年的第几天
timetuple = dt.timetuple() timetuple = dt.timetuple()
# 当年的第几天
# 计算出:日期当天的偏转角度
day_of_year = timetuple.tm_yday day_of_year = timetuple.tm_yday
# 根据当年的第几天计算出该日期当天的偏转角度:360度 / 365天 = 当天的偏转角度
angle_of_day = day_of_year * (360 / 365) angle_of_day = day_of_year * (360 / 365)
# 计算出精确的小时数
total_hours = timetuple.tm_hour + timetuple.tm_min / 60 + timetuple.tm_sec / 60 / 60 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 # -total_hours: 负号控制地球的旋转方向、1天24小时,360度/24=15
# print(time_data.get_datetime(str(self.start_time))) # 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: if self.clock_position_center:
position, origin = (0, .25), (0, 0), position, origin = (0, .25), (0, 0),
else: else:
...@@ -114,9 +140,9 @@ class SolarSystemRealitySim: ...@@ -114,9 +140,9 @@ class SolarSystemRealitySim:
font="verdana.ttf", font="verdana.ttf",
close_time=-1) close_time=-1)
def on_timer_changed(self, time_data: TimeData): def set_bodies_position(self, time_data: TimeData):
""" """
时时刻刻运行 设置天体的位置(包含速度和加速度的信息)
@param time_data: @param time_data:
@return: @return:
""" """
...@@ -133,7 +159,10 @@ class SolarSystemRealitySim: ...@@ -133,7 +159,10 @@ class SolarSystemRealitySim:
posvel = get_body_posvel(body, t) posvel = get_body_posvel(body, t)
if isinstance(body, Moon): # 如果是月球,为了好的展示效果,需要对月球的位置重新计算 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: if posvel is None:
# posvel 为空,则使用太阳的坐标 # posvel 为空,则使用太阳的坐标
...@@ -151,14 +180,37 @@ class SolarSystemRealitySim: ...@@ -151,14 +180,37 @@ class SolarSystemRealitySim:
body.position = np.array(position) body.position = np.array(position)
body.velocity = np.array(velocity) body.velocity = np.array(velocity)
if isinstance(body, Earth): if isinstance(body, Asteroids):
# 记录地球的位置 pass
earth_pos = posvel[0]
elif isinstance(body, Sun): elif isinstance(body, Sun):
# 记录太阳的位置 # 记录太阳的位置
sun_pos = posvel[0] 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): def bind_events(self):
# 运行中,每时每刻都会触发 on_timer_changed # 运行中,每时每刻都会触发 on_timer_changed
...@@ -171,22 +223,38 @@ class SolarSystemRealitySim: ...@@ -171,22 +223,38 @@ class SolarSystemRealitySim:
start_time=None, start_time=None,
show_asteroids=False, show_asteroids=False,
show_earth_clouds=False, show_earth_clouds=False,
recalc_moon_pos=True,
clock_position_center=False): 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: @return:
""" """
self.recalc_moon_pos = recalc_moon_pos
self.debug_mode = debug_mode self.debug_mode = debug_mode
self.clock_position_center = clock_position_center self.clock_position_center = clock_position_center
self.show_asteroids = show_asteroids self.show_asteroids = show_asteroids
self.show_earth_clouds = show_earth_clouds self.show_earth_clouds = show_earth_clouds
self.create_bodies() # 创建太阳系天体 # 创建太阳系天体
self.init_earth() # 初始化地球 self.create_bodies()
self.bind_events() # 绑定事件 # 对地球进行初始化
self.init_earth()
# 绑定事件
self.bind_events()
from astropy.time import Time
from datetime import datetime
# 开始时间为空,则默认为当前时间
if start_time is None: if start_time is None:
from astropy.time import Time
self.start_time = Time.now() # 获取默认开始时间为当前时间 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 = SECONDS_PER_DAY # 1秒=1天
dt = 1 # 1秒=1秒 dt = 1 # 1秒=1秒
...@@ -203,10 +271,12 @@ class SolarSystemRealitySim: ...@@ -203,10 +271,12 @@ class SolarSystemRealitySim:
if __name__ == '__main__': if __name__ == '__main__':
# 以下展示的效果为太阳系真实的时间和位置 # 以下展示的效果为太阳系真实的时间和位置
# 由于宇宙空间尺度非常大,如果按照实际的天体大小,则无法看到天体,因此需要对天体的尺寸进行放大
sim = SolarSystemRealitySim() sim = SolarSystemRealitySim()
sim.run( sim.run(
debug_mode=True, # debug_mode=True, # 是否调试模式
clock_position_center=True # start_time='2023-08-05 19:20:00', # 指定运行的开始时间,不指定为当前时间
# show_asteroids=True, # 是否显示小行星带(图片模拟)
# show_earth_clouds=True, # 地球是否显示云层(图片效果,不是真实的云层)
# recalc_moon_pos=False, # 为了好的展示效果,需要对月球的位置重新计算(地月距离放大,月球相对地球方向不变)
# clock_position_center=True # 时钟是否显示在中间
) )
...@@ -63,6 +63,8 @@ class TimeData: ...@@ -63,6 +63,8 @@ class TimeData:
def get_datetime(self, init_datetime): def get_datetime(self, init_datetime):
import datetime import datetime
# UTC_format = "%Y-%m-%dT%H:%M:%S.%fZ" # 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") UTC = datetime.datetime.strptime(init_datetime + "Z", "%Y-%m-%d %H:%M:%S.%fZ")
# BJS_format = "%Y-%m-%d %H:%M:%S" # BJS_format = "%Y-%m-%d %H:%M:%S"
BJS = UTC + datetime.timedelta(hours=8+self.total_hours) BJS = UTC + datetime.timedelta(hours=8+self.total_hours)
......
...@@ -261,7 +261,7 @@ def create_rings(parent): ...@@ -261,7 +261,7 @@ def create_rings(parent):
# 创建行星环 # 创建行星环
torus = create_torus(0.7, 1.2, 64) torus = create_torus(0.7, 1.2, 64)
parent.ring = Entity(parent=parent, model=torus, texture=rings_texture, scale=1, 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() parent.ring.set_light_off()
...@@ -327,7 +327,9 @@ def create_fixed_star_lights(fixed_star): ...@@ -327,7 +327,9 @@ def create_fixed_star_lights(fixed_star):
if fixed_star.body_view.body.light_on: if fixed_star.body_view.body.light_on:
for i in range(2): for i in range(2):
# 创建 PointLight 对象,作为恒星的灯光源 # 创建 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): def get_value_direction_vectors(vectors):
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册