我们正在模拟地球的第一颗人造卫星
在这个项目中,我们将建造1957年10月4日发射的PS-1卫星的地面轨道。
筹备及推出
让我们准备工作:打开海岸等高线图并为其设置所需的投影。
In [ ]:
Pkg.add(["SatelliteToolbox", "Shapefile", "Proj"])
我们提前从[自然地球]存储库下载了地图(https://www.naturalearthdata.com /)。 你会发现更广泛的信息与地图工作这里。
In [ ]:
using Shapefile, Downloads, Proj
shp_path = joinpath(@__DIR__, "map", "ne_110m_coastline.shp");
table = Shapefile.Table(shp_path);
trans = Proj.Transformation("+proj=longlat", "+proj=robin +lon_0=0 +x_0=0 +y_0=0");
现在让我们运行模型。 我们将得到几个状态向量。
In [ ]:
engee.open("sputnik_model.engee") # 如果模型已经打开,请评论。
data = engee.run("sputnik_model")
X_icrf = collect(data["X_icrf"]).value;
V_icrf = collect(data["V_icrf"]).value;
t_utc_jd = collect(data["t_utc_jd"]).value;
df = DataFrame( time = data["X_icrf"].time, t_utc_jd = t_utc_jd, X_icrf = X_icrf, V_icrf = V_icrf);
我们已经将向量组装成一个DataFrame对象,现在我们将用其他参数填充它。
将坐标转换到其他系统
为了显示图形,将坐标转换为地心和大地测量系统通常很有趣。 请考虑以下注意事项:
-对于我们的项目,ICRF系统几乎与GCRF(ECI)系统相同,误差为一米
-观察员将位于莫斯科
-使用WGS84椭球绘制地心坐标
In [ ]:
using DataFrames
using SatelliteToolbox
using LinearAlgebra
# 观察者在哪里
OBS_LLA = [deg2rad(55.7558), deg2rad(37.6173), 0.2e3]
eop = fetch_iers_eop()
# 添加新列
df[!, :lon] = zeros(length(df[!, :time]))
df[!, :lat] = zeros(length(df[!, :time]))
df[!, :alt] = zeros(length(df[!, :time]))
df[!, :az] = zeros(length(df[!, :time]))
df[!, :el] = zeros(length(df[!, :time]))
df[!, :range] = zeros(length(df[!, :time]))
# 地球的参数,用于转换观察者的坐标
a = 6378137.0
lat_obs, lon_obs, alt_obs = OBS_LLA
x_obs = (a + alt_obs) * cos(lat_obs) * cos(lon_obs)
y_obs = (a + alt_obs) * cos(lat_obs) * sin(lon_obs)
z_obs = (a + alt_obs) * sin(lat_obs)
for i in 1:length(df[!, :time])
# ICRF→ITRF转换
sv_icrf = OrbitStateVector(df[i, :t_utc_jd], df[i, :X_icrf], df[i, :V_icrf])
sv_ecef = sv_eci_to_ecef(sv_icrf, GCRF(), ITRF(), df[i, :t_utc_jd], eop)
# 大地坐标
lat, lon, alt = ecef_to_geodetic(sv_ecef.r)
df[i, :lon] = rad2deg(lon)
df[i, :lat] = rad2deg(lat)
df[i, :alt] = alt
# 地心坐标
Δr = sv_ecef.r - [x_obs, y_obs, z_obs]
range_val = norm(Δr)
# ENU坐标(观察者的局部坐标系east-north-top)
e = -sin(lon_obs)*Δr[1] + cos(lon_obs)*Δr[2]
n = -sin(lat_obs)*cos(lon_obs)*Δr[1] - sin(lat_obs)*sin(lon_obs)*Δr[2] + cos(lat_obs)*Δr[3]
u = cos(lat_obs)*cos(lon_obs)*Δr[1] + cos(lat_obs)*sin(lon_obs)*Δr[2] + sin(lat_obs)*Δr[3]
az = atan(e, n) % (2π)
el = atan(u, sqrt(e^2 + n^2))
df[i, :az] = rad2deg(az)
df[i, :el] = rad2deg(el)
df[i, :range] = range_val
end
航天器的地球轨迹图
获得的大地坐标可以简单地绘制,但为了将它们绘制为线,您首先需要将行星周围的每个圆划分为单独的段,以免在图上观察到不必要的跳跃(经度180和-
In [ ]:
ground_track_df = select(df, [:lon, :lat, :time])
# 轨道的地面路线
ground_track_lons = df.lon
ground_track_lats = df.lat
# 我们把它分成几段
jumps = findall(i -> abs(ground_track_lons[i+1] - ground_track_lons[i]) > 180, 1:length(ground_track_lons)-1)
segments = []
start_idx = 1
for jump_idx in jumps
push!(segments, (ground_track_lons[start_idx:jump_idx], ground_track_lats[start_idx:jump_idx]))
start_idx = jump_idx + 1
end
# 添加最后一个段
push!(segments, (ground_track_lons[start_idx:end], ground_track_lats[start_idx:end]))
# 我们分别绘制每个段
plot(title="轨道的地面路线")
for (seg_lons, seg_lats) in segments
xy_transf = trans.(seg_lons, seg_lats)
xx,yy = getindex.(xy_transf, 1), getindex.(xy_transf, 2)
plot!(xx, yy, legend=false, linecolor=:blue)
end
plot!()
for shape in table
x = [point.x for point in shape.geometry.points]
y = [point.y for point in shape.geometry.points]
xy_transf = trans.(x, y)
xx,yy = getindex.(xy_transf, 1), getindex.(xy_transf, 2)
plot!(xx, yy, c=1, lw=0.5, fill=:lightblue)
end
plot!()
Out[0]:
地球表面卫星可观测性图
为了绘制卫星从观察者所在点的可观测性,我们计算了地心方位角和赤纬。 当卫星在地平线上方时,我们将只分离轨迹的那些部分。
In [ ]:
# 地平线以上的点
skyplot_mask = df.el .> 0
skyplot_az = df.az # df.az[skyplot_mask]#这个数据已经可以推导出来了
skyplot_el = df.el # df.el[skyplot_mask]#但是让我们把它们分成段
true_indices = findall(skyplot_mask) # 我们把它分成连续的段
gaps = findall(diff(true_indices) .> 1) # 我们发现索引中的空白
boundaries = vcat([1], gaps .+ 1, [length(true_indices) + 1]) # 段边界
segments_az = Vector{Vector{eltype(skyplot_az)}}()
segments_el = Vector{Vector{eltype(skyplot_el)}}()
for i in 1:length(boundaries)-1
start_idx = boundaries[i]
end_idx = boundaries[i+1] - 1
segment_indices = true_indices[start_idx:end_idx]
push!(segments_az, skyplot_az[segment_indices])
push!(segments_el, skyplot_el[segment_indices])
end
在一个单独的单元格中,我们将显示一个半球并在其上绘制所有线段,以便看到卫星从地球表面看到的轨迹。
In [ ]:
# 创造一个半球
n = 30
u = range(0, 2π; length=n)
v = range(0, π/2; length=n)
x_sphere = cos.(u) .* sin.(v)'
y_sphere = sin.(u) .* sin.(v)'
z_sphere = ones(n) .* cos.(v)'
# 我们画一个半球
surface(x_sphere, y_sphere, z_sphere, alpha=0.4, legend=false)
xlabel!("东"); ylabel!("北区"); zlabel!("顶部")
# 绘制轨迹段
for (azimuths,elevations) in zip(segments_az, segments_el)
# 转换为笛卡尔坐标
az_rad = deg2rad.(azimuths)
el_rad = deg2rad.(elevations)
x = cos.(el_rad) .* cos.(az_rad)
y = cos.(el_rad) .* sin.(az_rad)
z = sin.(el_rad)
plot!(x, y, z, lw=4)
end
plot!(legend=false, title="卫星能见度图(莫斯科)")
Out[0]:
我们可以看到卫星何时以及如何从莫斯科的一个点观察到。 由于卫星的状态矢量还包括速度,我们可以计算多普勒频移并准确指定其信号的声音,这使我们取得了新的成就。
结论
我们在60,000秒的观测中获得了PS-1卫星的坐标(真正的卫星在轨道上花费了大约800万秒),并构建了几个有用的图表来解释这个航天器的轨迹。