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

Python超人-宇宙模拟器

上级 ace3b911
......@@ -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
......
......@@ -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
......
......@@ -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)
......@@ -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
# 地月缩放比例
# 为了好的展示效果,需要对月球的位置重新计算(地月距离放大,月球相对地球方向不变),重新计算位置后,地球和月球可以放大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,6 +159,9 @@ class SolarSystemRealitySim:
posvel = get_body_posvel(body, t)
if isinstance(body, Moon): # 如果是月球,为了好的展示效果,需要对月球的位置重新计算
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:
......@@ -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()
if start_time is None:
from astropy.time import Time
from datetime import datetime
# 开始时间为空,则默认为当前时间
if start_time is None:
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 # 时钟是否显示在中间
)
......@@ -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)
......
......@@ -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):
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册