1.生成CZML
根据卫星显示的起始时间,终止时间,tle轨道两行数得出czml文件
import moment from "moment";
import julian from "julian";
import * as satellite from "satellite.js";
/*
根据卫星显示的起始时间,终止时间,tle轨道两行数得出czml文件,时间为js的Date对象,tles为对象数组,对象格式为
{
name:xx,
tle1:xx,
tle2:xx
}
*/
export default function tles2czml(startTime, endTime, tles) {
console.log(endTime);
console.log(startTime);
// 计算起始时间和终止时间相隔的分钟数
let minsInDuration = (endTime.getTime() - startTime.getTime()) / 6000; //mins
//设置为开始时间
let initialTime = moment(startTime.toISOString()).toISOString();
//设置为结束时间
endTime = moment(endTime.toISOString()).toISOString();
// 初始化czml数据,创建场景信息
let tempCZML = [];
tempCZML.push({
"id": "document",
"name": "CZML Point - Time Dynamic",
"version": "1.0",
"clock": {
"interval": `${initialTime}/${endTime}`,
"multiplier": 1,
"range": "LOOP_STOP",
"step": "SYSTEM_CLOCK"
}
},
)
// 处理每一个sat
for (let no = 0; no < tles.length; no++) {
if (!tles[no].name) {
console.log("请输入第" + no + 1 + "个卫星的名称");
return
};
if (!tles[no].tle1) {
console.log("请输入第" + no + 1 + "个卫星的第一个两行数");
return
};
if (!tles[no].tle2) {
console.log("请输入第" + no + 1 + "个卫星的第二个两行数");
return
};
let sat_name = tles[no].name;
// 保存位置信息
let res = [];
let satrec
satrec = satellite.twoline2satrec(tles[no].tle1, tles[no].tle2);
//satrec.no:以弧度/分钟为单位的平均运动,一天有1440分钟,一弧度是0.159155圈
// to go from RAD/DAY -> REV/DAY: rad * 1440 * 0.159155
//to go from REV/PER DAY to MINS/REV -> 1440/RevPerDay
let totalIntervalsInDay = satrec.no * 1440 * 0.159155; //1440 = min && 0.159155 = 1turn
// 获得运行一圈的分钟数
let minsPerInterval = 1440 / totalIntervalsInDay; // mins for 1 revolution around earth
// intervalTime 取起始时间 格式为2008-09-20T12:25:40.104Z
let intervalTime = moment(startTime.toISOString()).toISOString()
let leadIntervalArray = [];
let trailIntervalArray = [];
console.log("Setting intervals...");
// 注意:这里之所以要倒过来求leadInterval和trailInterval是因为如果正着求,很有可能在终止时刻卫星并没有运行完一圈,导致轨道只显示一半
for (let i = minsInDuration; i >= 0; i -= minsPerInterval) {
if (i <= minsPerInterval) { // intial interval
let currentOrbitalInterval = {
"interval": `${startTime.toISOString()}/${intervalTime}`,
"epoch": `${startTime.toISOString()}`,
"number": [
0, minsPerInterval * 60,
minsPerInterval * 60, 0
]
}
let currTrail = {
"interval": `${startTime.toISOString()}/${intervalTime}`,
"epoch": `${startTime.toISOString()}`,
"number": [
0, 0,
minsPerInterval * 60, minsPerInterval * 60
]
}
leadIntervalArray.push(currentOrbitalInterval);
trailIntervalArray.push(currTrail);
}
else { //not initial so make intervals
let previousIntervalTime = moment(intervalTime).add(-minsPerInterval, 'm').toISOString();
let currentOrbitalInterval = {
"interval": `${previousIntervalTime}/${intervalTime}`,
"epoch": `${previousIntervalTime}`,
"number": [
0, minsPerInterval * 60,
minsPerInterval * 60, 0
]
}
let currTrail = {
"interval": `${previousIntervalTime}/${intervalTime}`,
"epoch": `${previousIntervalTime}`,
"number": [
0, 0,
minsPerInterval * 60, minsPerInterval * 60
]
}
intervalTime = moment(intervalTime).add(-minsPerInterval, 'm').toISOString();
leadIntervalArray.push(currentOrbitalInterval);
trailIntervalArray.push(currTrail);
}
}
// Seconds between current time and epoch time
let sec = (startTime - julian.toDate(satrec.jdsatepoch)) / 1000;
console.log(startTime, julian.toDate(satrec.jdsatepoch), sec);
for (let i = sec; i <= sec + minsInDuration * 60; i++) { //每60秒计算一个位置信息,最后采用拉格朗日插值法处理数据
// 根据当前时间距tle两行数历元时刻的分钟数,计算当前卫星位置和速度
let positionAndVelocity = satellite.sgp4(satrec, i * 0.0166667); // 0.0166667min = 1sec
// 地惯坐标系
let positionEci = positionAndVelocity.position;
positionEci.x = positionEci.x * 1000;
positionEci.y = positionEci.y * 1000;
positionEci.z = positionEci.z * 1000;
// let velocityEci = positionAndVelocity.velocity;
// velocityEci.x = velocityEci.x * 1000;
// velocityEci.y = velocityEci.y * 1000;
// velocityEci.z = velocityEci.z * 1000;
res.push(i - sec, positionEci.x, positionEci.y, positionEci.z);
}
let initialCZMLProps =
{
"id": `${sat_name}`,
"name": `${sat_name}`,
"availability": `${initialTime}/${endTime}`,
"label": {
"fillColor": {
"rgba": [
255, 0, 255, 255
]
},
"font": "11pt Lucida Console",
"horizontalOrigin": "LEFT",
"outlineColor": {
"rgba": [
0, 0, 0, 255
]
},
"outlineWidth": 2,
"pixelOffset": {
"cartesian2": [
12, 0
]
},
"show": true,
"style": "FILL_AND_OUTLINE",
"text": `${sat_name}`,
"verticalOrigin": "CENTER"
},
"path": {
"show": [
{
"interval": `${initialTime}/${endTime}`,
"boolean": true
}
],
"width": 3,
"material": {
"solidColor": {
"color": {
"rgba": [
// 随机生成轨道颜色
Math.floor(255 * Math.random(0, 1)), Math.floor(255 * Math.random(0, 1)), Math.floor(255 * Math.random(0, 1)), 255
]
}
}
},
"resolution": 120,
// The time ahead of the animation time, in seconds, to show the path.
"leadTime": leadIntervalArray,
// The time behind the animation time, in seconds, to show the
"trailTime": trailIntervalArray
},
"model": {
"show": true,
"gltf": "./data/simple_satellite_low_poly_free.glb",
"minimumPixelSize": 55,
},
"position": {
// 采用拉格朗日插值法
"interpolationAlgorithm": "LAGRANGE",
// 1为线性插值,2为平方插值
"interpolationDegree": 2,
// 参考坐标系,地惯坐标系
"referenceFrame": "INERTIAL",
"epoch": `${initialTime}`,
"cartesian": res
}
}
tempCZML.push(initialCZMLProps);
}
return tempCZML;
}
入参(此示例为两个参数)
const tles = [
{
name: "QingHe1",
tle1: "1 25544U 98067A 20199.03008672 -.00000576 00000-0 -22221-5 0 9991",
tle2: "2 25544 51.6440 200.7619 0001412 122.6206 338.3473 15.49512746236640",
},
{
name: "NAVSTAR 76 (USA 266)",
tle1: "41328U 16007A 19049.49277110 .00000017 00000-0 00000+0 0 9993",
tle2: "2 25544 51.6440 200.7619 0001412 122.6206 338.3473 15.49512742 41328 54.8247 195.6929 0027216 214.4209 145.3559 2.00553553 22229",
},
];
const startTime = new Date("2023-02-14T08:00:00");
const endTime = new Date("2023-02-14T13:10:00");
const czml = tles2czml(startTime, endTime, tles);
2.根据生成的CZML获取卫星的时间和位置,生成Entity实体
import * as Cesium from "cesium";
export default function scanning(viewer,czml){
const cylinderEntity = viewer.entities.add({
name: "Red cone",
position: Cesium.Cartesian3.fromDegrees(-105.0, 40.0, 200000.0),
cylinder: {
length: 400000.0,
topRadius: 0.0,
bottomRadius: 200000.0,
material: Cesium.Color.RED.withAlpha(0.5),
},
});
var property;
viewer.dataSources
.add(Cesium.CzmlDataSource.load(czml))
.then(function (dataSource) {
var satellite = dataSource.entities.getById("QingHe1");
property = new Cesium.SampledPositionProperty();
for (var ind = 0; ind < 292; ind++) {
var time = Cesium.JulianDate.addSeconds(
viewer.clock.currentTime,
300 * ind,
new Cesium.JulianDate()
);
var position = satellite.position.getValue(time);
console.log(position);
if (position) {
var cartographic =
Cesium.Ellipsoid.WGS84.cartesianToCartographic(position);
var lat = Cesium.Math.toDegrees(cartographic.latitude),
lng = Cesium.Math.toDegrees(cartographic.longitude),
hei = cartographic.height / 1.9;
property.addSample(
time,
Cesium.Cartesian3.fromDegrees(lng, lat, hei)
);
}
}
cylinderEntity.position = property;
cylinderEntity.position.setInterpolationOptions({
//设定位置的插值算法
interpolationDegree: 5,
interpolationAlgorithm: Cesium.LagrangePolynomialApproximation,
});
// viewer.clock.onTick.addEventListener(function (clock) {
// if (property) {
// var time = clock.currentTime;
// var val = property.getValue(clock.currentTime);
// // console.log(val);
// }
// });
});
}
3.无两行数情况下输入开始时间和结束时间生成模拟轨道(非真实卫星轨迹)
import * as Cesium from "cesium";
var arrStates = [];
var start, stop, viewer;
export default function simulation(v, startTime, endTime) {
viewer = v
// start = new Cesium.JulianDate.fromDate(new Date()); // 获取当前时间 这不是国内的时间
start = new Cesium.JulianDate.fromDate(startTime); // 获取当前时间 这不是国内的时间
stop = new Cesium.JulianDate.fromDate(endTime); // 获取当前时间 这不是国内的时间
console.log(start);
const sec = stop.secondsOfDay - start.secondsOfDay
// start = Cesium.JulianDate.addHours(start, 8, new Cesium.JulianDate()); // 添加八小时,得到我们东八区的北京时间
// stop = Cesium.JulianDate.addHours(stop, 8, new Cesium.JulianDate()); // 设置一个结束时间,意思是360秒之后时间结束
viewer.clock.startTime = start.clone(); // 给cesium时间轴设置开始的时间,也就是上边的东八区时间
viewer.clock.stopTime = stop.clone(); // 设置cesium时间轴设置结束的时间
viewer.clock.currentTime = start.clone(); // 设置cesium时间轴设置当前的时间
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; // 时间结束了,再继续重复来一遍
//时间变化来控制速度 // 时间速率,数字越大时间过的越快
viewer.clock.multiplier = 2;
//给时间线设置边界
viewer.timeline.zoomTo(start, stop);
arrStates = [];
getRandState(arrStates, 1, sec);
startFunc();
}
function mySatePosition() {
this.lon = 0;
this.lat = 0;
this.hei = 700000; //卫星高度
this.phei = 700000 / 2; //轨道高度
this.time = 0;
}
function computeCirclularFlight(source, panduan) {
var property = new Cesium.SampledPositionProperty();
if (panduan == 1) {
//卫星位置
for (var i = 0; i < source.length; i++) {
var time = Cesium.JulianDate.addSeconds(
start,
source[i].time,
new Cesium.JulianDate()
);
var position = Cesium.Cartesian3.fromDegrees(
source[i].lon,
source[i].lat,
source[i].hei
);
// 添加位置,和时间对应
property.addSample(time, position);
}
} else if (panduan == 2) {
//轨道位置
for (var i = 0; i < source.length; i++) {
var time = Cesium.JulianDate.addSeconds(
start,
source[i].time,
new Cesium.JulianDate()
);
var position = Cesium.Cartesian3.fromDegrees(
source[i].lon,
source[i].lat,
source[i].phei
);
// 添加位置,和时间对应
property.addSample(time, position);
}
}
return property;
}
function getRandState(brr, count, sec) {
for (var m = 0; m < count; m++) {
var arr = [];
var t1 = Math.floor(Math.random() * sec);
var t2 = Math.floor(Math.random() * sec);
for (var i = t1; i <= sec + t1; i += 30) {
var aaa = new mySatePosition();
aaa.lon = t2;
aaa.lat = i;
aaa.time = i - t1;
arr.push(aaa);
}
brr.push(arr);
}
console.log(brr);
}
function getStatePath(aaa) {
console.log(aaa);
console.log(viewer);
var entity_ty1p = computeCirclularFlight(aaa, 2);
var entity_ty1 = viewer.entities.add({
availability: new Cesium.TimeIntervalCollection([
new Cesium.TimeInterval({
start: start,
stop: stop,
}),
]),
position: entity_ty1p, //轨道高度
orientation: new Cesium.VelocityOrientationProperty(entity_ty1p),
cylinder: {
HeightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
length: 700000,
topRadius: 0,
bottomRadius: 900000 / 2,
// material: Cesium.Color.RED.withAlpha(.4),
// outline: !0,
numberOfVerticalLines: 0,
// outlineColor: Cesium.Color.RED.withAlpha(.8),
material: Cesium.Color.fromBytes(35, 170, 242, 80),
},
});
entity_ty1.position.setInterpolationOptions({
interpolationDegree: 5,
interpolationAlgorithm: Cesium.LagrangePolynomialApproximation,
});
var entity1p = computeCirclularFlight(aaa, 1);
//创建实体
var entity1 = viewer.entities.add({
// 将实体availability设置为与模拟时间相同的时间间隔。
availability: new Cesium.TimeIntervalCollection([
new Cesium.TimeInterval({
start: start,
stop: stop,
}),
]),
position: entity1p, //计算实体位置属性
//基于位置移动自动计算方向.
orientation: new Cesium.VelocityOrientationProperty(entity1p),
//加载飞机模型
model: {
uri: "./data/simple_satellite_low_poly_free.glb",
scale: 30000,
},
//路径
path: {
resolution: 1,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.1,
color: Cesium.Color.PINK,
}),
width: 5,
},
});
//差值器
entity1.position.setInterpolationOptions({
interpolationDegree: 5,
interpolationAlgorithm: Cesium.LagrangePolynomialApproximation,
});
}
function startFunc() {
for (var i = 0; i < arrStates.length; i++) {
getStatePath(arrStates[i]);
}
}
入参
/* 模拟卫星,非真实卫星轨迹 */
const startTime = new Date("2023-02-14T13:00:00");
const endTime = new Date("2023-02-14T14:20:00");
simulation(viewer, startTime, endTime);