102 lines
3.8 KiB
TypeScript
102 lines
3.8 KiB
TypeScript
import * as Cesium from 'cesium';
|
|
import { eciToEcf, gstime, propagate, type SatRec, twoline2satrec } from 'satellite.js';
|
|
|
|
import type { I卫星 } from '../managers/HCesiumManager.types'; // 保持类型定义位置
|
|
|
|
const 默认轨道时长秒 = 2 * 60 * 60; // 2小时
|
|
const 默认时间步长秒 = 30;
|
|
|
|
export interface OrbitCalculationResult {
|
|
orbitPositions: Cesium.Cartesian3[]; // 用于绘制完整轨道线
|
|
sampledPositionProperty: Cesium.SampledPositionProperty;
|
|
}
|
|
|
|
export interface OrbitSample {
|
|
position: Cesium.Cartesian3;
|
|
time: Cesium.JulianDate;
|
|
}
|
|
|
|
export class SatelliteCalculator {
|
|
/**
|
|
* 解析 TLE 字符串获取卫星记录对象。
|
|
* @param tle - 包含名称和两行数据的 TLE 字符串。
|
|
* @param satelliteId - 用于错误日志的卫星 ID。
|
|
* @returns 解析成功返回 SatRec 对象,否则返回 null。
|
|
*/
|
|
parseTle(tle: string, satelliteId: string): null | SatRec {
|
|
const tleLines = tle.trim().split('\n') as [string, string, string];
|
|
if (tleLines.length < 3) {
|
|
console.error(`无效的 TLE 格式 (ID: ${satelliteId}): TLE 字符串至少需要三行`);
|
|
return null;
|
|
}
|
|
const tle1 = tleLines[1].trim();
|
|
const tle2 = tleLines[2].trim();
|
|
|
|
try {
|
|
return twoline2satrec(tle1, tle2);
|
|
} catch (error) {
|
|
console.error(`解析 TLE 失败 (ID: ${satelliteId}):`, error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 计算卫星在给定时间段内的轨道位置。
|
|
* @param satrec - 卫星记录对象。
|
|
* @param startTime - 计算轨道的开始时间。
|
|
* @param options - 包含轨道时长、步长等选项。
|
|
* @param satelliteId - 用于日志记录的卫星 ID。
|
|
* @returns 包含 SampledPositionProperty 和轨道点数组的对象,如果计算失败则返回 null。
|
|
*/
|
|
calculateOrbit(
|
|
satrec: SatRec,
|
|
startTime: Cesium.JulianDate,
|
|
options: Pick<I卫星, 'orbitDurationSeconds' | 'showOrbit' | 'timeStepSeconds'>,
|
|
satelliteId: string,
|
|
): null | OrbitCalculationResult {
|
|
const { orbitDurationSeconds = 默认轨道时长秒, timeStepSeconds = 默认时间步长秒, showOrbit } = options;
|
|
|
|
const sampledPositionProperty = new Cesium.SampledPositionProperty();
|
|
const orbitPositions: Cesium.Cartesian3[] = [];
|
|
let hasSamples = false; // 标记是否成功添加了至少一个样本点
|
|
|
|
for (let i = 0; i <= orbitDurationSeconds; i += timeStepSeconds) {
|
|
const time = Cesium.JulianDate.addSeconds(startTime, i, new Cesium.JulianDate());
|
|
const jsDate = Cesium.JulianDate.toDate(time);
|
|
|
|
try {
|
|
const positionAndVelocity = propagate(satrec, jsDate);
|
|
if (typeof positionAndVelocity.position === 'boolean') {
|
|
console.warn(`卫星 ${satelliteId} 在时间 ${jsDate} 位置计算失败或已衰减。`);
|
|
continue; // 跳过这个时间点
|
|
}
|
|
|
|
const gmst = gstime(jsDate);
|
|
const positionEcf = eciToEcf(positionAndVelocity.position, gmst);
|
|
|
|
// 转换为 Cesium 坐标(单位:米)
|
|
const cesiumPosition = new Cesium.Cartesian3(positionEcf.x * 1000, positionEcf.y * 1000, positionEcf.z * 1000);
|
|
|
|
// 添加位置样本
|
|
sampledPositionProperty.addSample(time, cesiumPosition);
|
|
hasSamples = true; // 标记已添加样本
|
|
if (showOrbit) {
|
|
orbitPositions.push(cesiumPosition);
|
|
}
|
|
} catch (error) {
|
|
console.error(`计算卫星 ${satelliteId} 在时间 ${jsDate} 的位置时出错:`, error);
|
|
// 可以在这里决定是跳过还是中断循环
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// 确保至少有一个样本点,否则 SampledPositionProperty 会有问题
|
|
if (!hasSamples) {
|
|
console.warn(`卫星 ${satelliteId} 未能计算出任何有效轨道点。`);
|
|
return null;
|
|
}
|
|
|
|
return { sampledPositionProperty, orbitPositions };
|
|
}
|
|
}
|