TLE2ToCZML/tle2czml.js

304 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

const satellite = require('satellite.js')
const moment = require('moment')
const julian = require('julian')
const { loadData } = require('./xlsx')
/*
根据卫星显示的起始时间终止时间tle轨道两行数得出czml文件时间为js的Date对象tles为对象数组对象格式为
{
namexx
tle1xx
tle2xx
}
*/
const tles2czml = (startTime, endTime, tles, fileName) => {
// 计算起始时间和终止时间相隔的分钟数
let minsInDuration = (endTime.getTime() - startTime.getTime()) / 60000; //mins
console.log(minsInDuration)
//设置为开始时间
let initialTime = moment(startTime.toISOString()).toISOString();
//设置为结束时间
endTime = moment(endTime.toISOString()).toISOString();
// 初始化czml数据创建场景信息
let tempCZML = [];
tempCZML.push({
"id": "document",
"version": "1.0",
"name": `${fileName} CZML Model`,
"clock": {
"currentTime": `${initialTime}`,
"multiplier": 555,
"interval": `${initialTime}/${endTime}`,
"range": "LOOP_STOP",
"step": "SYSTEM_CLOCK_MULTIPLIER"
}
},
)
// 处理每一个sat
for (let no = 0; no < tles.length; no++) {
if(!tles[no].name){
return
};
if(!tles[no].tle1){
return
};
if(!tles[no].tle2){
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 = endTime
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": `${initialTime}/${intervalTime}`,
"epoch": `${initialTime}`,
"number": [
0, minsPerInterval * 120,
minsPerInterval * 120, 0
]
}
let currTrail = {
"interval": `${initialTime}/${intervalTime}`,
"epoch": `${initialTime}`,
"number": [
0, 0,
minsPerInterval * 120, minsPerInterval * 120
]
}
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 * 120,
minsPerInterval * 120, 0
]
}
let currTrail = {
"interval": `${previousIntervalTime}/${intervalTime}`,
"epoch": `${previousIntervalTime}`,
"number": [
0, 0,
minsPerInterval * 120, minsPerInterval * 120
]
}
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 = i + 300) { //sec + minsInDuration * 60 每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 dataArr = {
rawname: '',
showname: '',
airDefenseNumber: '',
PRN: '',
type: '',
launchDate: '',
runningCycle: '',
orbitalInclination: '',
apogeeAltitude: '',
perigeeAltitude: '',
atomicClockType: '',
beidouType: ''
}
loadData()[fileName].forEach(element => {
if(element.rawname == sat_name){
dataArr.rawname = element.rawname
dataArr.showname = element.showname
dataArr.airDefenseNumber = element.airDefenseNumber
dataArr.PRN = element.PRN
dataArr.type = element.type
dataArr.launchDate = element.launchDate
dataArr.runningCycle = element.runningCycle
dataArr.orbitalInclination = element.orbitalInclination
dataArr.apogeeAltitude = element.apogeeAltitude
dataArr.perigeeAltitude = element.perigeeAltitude
if(element.atomicClockType !== undefined && element.beidouType !== undefined){
dataArr.atomicClockType = element.atomicClockType
dataArr.beidouType = element.beidouType
}
}
})
let satelliteType
let description
let fillColor
let solidColor
let model
if(fileName == "BEIDOU"){
satelliteType = 'Beidou'
model = 'Beidou' + dataArr.type
fillColor = [ 0, 204, 255, 500 ]
solidColor = [ 0, 204, 255, 100 ]
description = `<img style=\"float: right;\" src=\"./image/logo/Beidou.png\" /><img style=\"float: right;width:190px;clear:both;\" src=\"./image/gif/${ 'Beidou' + dataArr.type }.gif\" /><p>NORAD ID:${ dataArr.airDefenseNumber }</p><p>PRN:${ dataArr.PRN }</p><p>\u7c7b\u578b:${ dataArr.type }</p><p>\u53d1\u5c04\u65e5\u671f:${ dataArr.launchDate }</p><p>\u8fd0\u884c\u5468\u671f\uff08\u5206\u949f\uff09:${ dataArr.runningCycle }</p><p>\u8f68\u9053\u9762\u503e\u89d2\uff08\u5ea6\uff09:${ dataArr.orbitalInclination }</p><p>\u8fdc\u5730\u70b9\u9ad8\u5ea6\uff08\u516c\u91cc\uff09:${ dataArr.apogeeAltitude }</p><p>\u8fd1\u5730\u70b9\u9ad8\u5ea6\uff08\u516c\u91cc\uff09:${ dataArr.perigeeAltitude }</p><p>\u539f\u5b50\u949f\u7c7b\u578b\uff08\u5317\u6597\uff09:${ dataArr.atomicClockType }</p><p>\u5317\u6597\u7c7b\u578b\uff082/3\uff09:${ dataArr.beidouType }</p>`
}
if(fileName == "GPS"){
satelliteType = 'GPS'
model = 'GPS' + dataArr.type
fillColor = [ 255, 255, 0, 500 ]
solidColor = [ 255, 255, 0, 100 ]
description = `<img style=\"float: right;\" src=\"./image/logo/GPS.png\" /><img style=\"float: right;width:190px;clear:both;\" src=\"./image/gif/${ 'GPS' + dataArr.type }.gif\" /><p>NORAD ID:${ dataArr.airDefenseNumber }</p><p>PRN:${ dataArr.PRN }</p><p>\u7c7b\u578b:${ dataArr.type }</p><p>\u53d1\u5c04\u65e5\u671f:${ dataArr.launchDate }</p><p>\u8fd0\u884c\u5468\u671f\uff08\u5206\u949f\uff09:${ dataArr.runningCycle }</p><p>\u8f68\u9053\u9762\u503e\u89d2\uff08\u5ea6\uff09:${ dataArr.orbitalInclination }</p><p>\u8fdc\u5730\u70b9\u9ad8\u5ea6\uff08\u516c\u91cc\uff09:${ dataArr.apogeeAltitude }</p><p>\u8fd1\u5730\u70b9\u9ad8\u5ea6\uff08\u516c\u91cc\uff09:${ dataArr.perigeeAltitude }</p>`
}
if(fileName == "GLONASS"){
satelliteType = 'GLONASS'
model = 'GLONASS' + dataArr.type
fillColor = [ 102, 0, 255, 500 ]
solidColor = [ 102, 0, 255, 100 ]
description = `<img style=\"float: right;\" src=\"./image/logo/GLONASS.png\" /><img style=\"float: right;width:190px;clear:both;\" src=\"./image/gif/${ 'GLONASS' + dataArr.type }.gif\" /><p>NORAD ID:${ dataArr.airDefenseNumber }</p><p>PRN:${ dataArr.PRN }</p><p>\u7c7b\u578b:${ dataArr.type }</p><p>\u53d1\u5c04\u65e5\u671f:${ dataArr.launchDate }</p><p>\u8fd0\u884c\u5468\u671f\uff08\u5206\u949f\uff09:${ dataArr.runningCycle }</p><p>\u8f68\u9053\u9762\u503e\u89d2\uff08\u5ea6\uff09:${ dataArr.orbitalInclination }</p><p>\u8fdc\u5730\u70b9\u9ad8\u5ea6\uff08\u516c\u91cc\uff09:${ dataArr.apogeeAltitude }</p><p>\u8fd1\u5730\u70b9\u9ad8\u5ea6\uff08\u516c\u91cc\uff09:${ dataArr.perigeeAltitude }</p>`
}
if(fileName == "GALILEO"){
satelliteType = 'Galileo'
model = 'Galileo' + dataArr.type
fillColor = [ 0, 204, 102, 500 ]
solidColor = [ 0, 204, 102, 100 ]
description = `<img style=\"float: right;\" src=\"./image/logo/Galileo.png\" /><img style=\"float: right;width:190px;clear:both;\" src=\"./image/gif/${ 'Galileo' + dataArr.type }.gif\" /><p>NORAD ID:${ dataArr.airDefenseNumber }</p><p>PRN:${ dataArr.PRN }</p><p>\u7c7b\u578b:${ dataArr.type }</p><p>\u53d1\u5c04\u65e5\u671f:${ dataArr.launchDate }</p><p>\u8fd0\u884c\u5468\u671f\uff08\u5206\u949f\uff09:${ dataArr.runningCycle }</p><p>\u8f68\u9053\u9762\u503e\u89d2\uff08\u5ea6\uff09:${ dataArr.orbitalInclination }</p><p>\u8fdc\u5730\u70b9\u9ad8\u5ea6\uff08\u516c\u91cc\uff09:${ dataArr.apogeeAltitude }</p><p>\u8fd1\u5730\u70b9\u9ad8\u5ea6\uff08\u516c\u91cc\uff09:${ dataArr.perigeeAltitude }</p>`
}
let initialCZMLProps =
{
"id": `Satellite/${sat_name}`,
"name": `${sat_name}`,
"description": description,
"availability": `${initialTime}/${endTime}`,
"label": {
"fillColor": {
"rgba": fillColor
},
"font": "11pt Lucida Console",
"horizontalOrigin": "TOP",
"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": 1,
"material": {
"solidColor": {
"color": {
"rgba": solidColor
// [
// // 随机生成轨道颜色
// // 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": `./models/geo/${model}.glb`,
"minimumPixelSize": 60,
"maximumPixelSize": 60,
"scale": 24000.0
},
"properties":{
"satelliteType": satelliteType
},
"position": {
// 采用拉格朗日插值法
"interpolationAlgorithm": "LAGRANGE",
// 1为线性插值2为平方插值
"interpolationDegree": 5,
// 参考坐标系,地惯坐标系
"referenceFrame": "INERTIAL",
"epoch": `${initialTime}`,
"cartesian": res
},
"orientation": {
"velocityReference": "#position"
}
}
tempCZML.push(initialCZMLProps);
}
if(fileName == "BEIDOU"){
tempCZML.push({
"id": "Constellation/beidou",
"name": "\u5317\u6597\u536b\u661f\u5bfc\u822a\u7cfb\u7edf",
"description": "<p style=\"text-align: center;\"><img src=\"./image/logo/Beidou.png\" style=\"width: 190px;height: 190px;\" /></p><p>\u5317\u6597\u536b\u661f\u5bfc\u822a\u7cfb\u7edf\u662f\u4e2d\u56fd\u7740\u773c\u4e8e\u56fd\u5bb6\u5b89\u5168\u548c\u7ecf\u6d4e\u793e\u4f1a\u53d1\u5c55\u9700\u8981\uff0c\u81ea\u4e3b\u5efa\u8bbe\u8fd0\u884c\u7684\u5168\u7403\u536b\u661f\u5bfc\u822a\u7cfb\u7edf\uff0c\u662f\u4e3a\u5168\u7403\u7528\u6237\u63d0\u4f9b\u5168\u5929\u5019\u3001\u5168\u5929\u65f6\u3001\u9ad8\u7cbe\u5ea6\u7684\u5b9a\u4f4d\u3001\u5bfc\u822a\u548c\u6388\u65f6\u670d\u52a1\u7684\u56fd\u5bb6\u91cd\u8981\u65f6\u7a7a\u57fa\u7840\u8bbe\u65bd\u30022000\u5e74\u5e74\u5e95\uff0c\u5efa\u6210\u5317\u6597\u4e00\u53f7\u7cfb\u7edf\uff0c\u5411\u4e2d\u56fd\u63d0\u4f9b\u670d\u52a1\uff1b2012\u5e74\u5e74\u5e95\uff0c\u5efa\u6210\u5317\u6597\u4e8c\u53f7\u7cfb\u7edf\uff0c\u5411\u4e9a\u592a\u5730\u533a\u63d0\u4f9b\u670d\u52a1\uff1b2020\u5e74\uff0c\u5efa\u6210\u5317\u6597\u4e09\u53f7\u7cfb\u7edf\uff0c\u5411\u5168\u7403\u63d0\u4f9b\u670d\u52a1\u30022035\u5e74\u524d\u5c06\u5efa\u8bbe\u5b8c\u5584\u66f4\u52a0\u6cdb\u5728\u3001\u66f4\u52a0\u878d\u5408\u3001\u66f4\u52a0\u667a\u80fd\u7684\u7efc\u5408\u65f6\u7a7a\u4f53\u7cfb\u3002</p>"
})
}
if(fileName == "GALILEO"){
tempCZML.push({
"id": "Constellation/galileo",
"name": "\u6b27\u76df\u7814\u5236\u4f3d\u5229\u7565\u536b\u661f\u5bfc\u822a\u7cfb\u7edf",
"description": "<p style=\"text-align: center;\"><img src=\"./image/logo/Galileo.png\" style=\"width: 190px;height: 190px;\" /></p><p>\u4f3d\u5229\u7565\u5b9a\u4f4d\u7cfb\u7edf\uff08\u610f\u5927\u5229\u8bed\uff1aGalileo\uff09\uff0c\u662f\u4e00\u4e2a\u6b63\u5728\u5efa\u9020\u4e2d\u7684\u536b\u661f\u5b9a\u4f4d\u7cfb\u7edf\uff0c\u8be5\u7cfb\u7edf\u7531\u6b27\u76df\u901a\u8fc7\u6b27\u6d32\u7a7a\u95f4\u5c40\u548c\u6b27\u6d32\u5bfc\u822a\u536b\u661f\u7cfb\u7edf\u7ba1\u7406\u5c40\u5efa\u9020\uff0c\u603b\u90e8\u8bbe\u5728\u6377\u514b\u5171\u548c\u56fd\u7684\u5e03\u62c9\u683c\u3002\u4f3d\u5229\u7565\u7cfb\u7edf\u7684\u76ee\u6807\u662f\u5728\u6c34\u5e73\u548c\u5782\u76f4\u65b9\u5411\u63d0\u4f9b\u7cbe\u5ea61\u7c73\u4ee5\u5185\u7684\u5b9a\u4f4d\u670d\u52a1\uff0c\u5e76\u4e14\u5728\u9ad8\u7eac\u5ea6\u5730\u533a\u63d0\u4f9b\u6bd4\u5176\u4ed6\u7cfb\u7edf\u66f4\u597d\u7684\u5b9a\u4f4d\u670d\u52a1\u3002</p>"
})
}
if(fileName == "GLONASS"){
tempCZML.push({
"id": "Constellation/glo-ops",
"name": "\u4fc4\u7f57\u65af\u683c\u6d1b\u7eb3\u65af\u536b\u661f\u5bfc\u822a\u7cfb\u7edf",
"description": "<p style=\"text-align: center;\"><img src=\"./image/logo/GLONASS.png\" style=\"width: 190px;height: 190px;\" /></p><p>\u683c\u6d1b\u7eb3\u65af\u7cfb\u7edf\u6216\u5168\u7403\u5bfc\u822a\u536b\u661f\u7cfb\u7edf\uff0c\u7b80\u79f0GLONASS\uff08\u4fc4\u8bed\uff1a\u0413\u041b\u041e\u041d\u0410\u0421\u0421\uff0c\u7f57\u9a6c\u5316\uff1aGLONASS\uff0c\u4fc4\u8bed\uff1a\u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u0430\u044f \u043da\u0432\u0438\u0433\u0430\u0446\u0438\u043e\u043d\u043d\u0430\u044f \u0441\u043f\u0443\u0442\u043d\u0438\u043a\u043e\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430\uff0c\u7f57\u9a6c\u5316\uff1aGlobalnaya navigatsionnaya sputnikovaya sistema\uff09\uff0c\u5b83\u662f\u7531\u82cf\u8054\u4e8e1982\u5e74\u7814\u53d1\u7684\u536b\u661f\u5bfc\u822a\u7cfb\u7edf\uff0c\u82cf\u8054\u89e3\u4f53\u540e\u4e00\u5ea6\u4e27\u5931\u5927\u591a\u6570\u536b\u661f\u4e0e\u529f\u80fd\uff0c\u4eca\u65e5\u7531\u4fc4\u7f57\u65af\u7ef4\u62a4\u8fd0\u4f5c\u3002\u7c7b\u4f3c\u4e8e\u7f8e\u56fd\u7684\u5168\u7403\u5b9a\u4f4d\u7cfb\u7edf\u3001\u6b27\u76df\u7684\u4f3d\u5229\u7565\u5b9a\u4f4d\u7cfb\u7edf\u548c\u4e2d\u56fd\u7684\u5317\u6597\u536b\u661f\u5bfc\u822a\u7cfb\u7edf\uff0c\u4e0d\u8fc7\u6280\u672f\u7565\u6709\u5dee\u5f02\u3002</p>"
})
}
if(fileName == "GPS"){
tempCZML.push({
"id": "Constellation/gps-ops",
"name": "\u7f8e\u56fd\u5168\u7403\u5b9a\u4f4d\u7cfb\u7edf",
"description": "<p style=\"text-align: center;\"><img src=\"./image/logo/GPS.png\" style=\"width: 190px;height: 190px;\" /></p><p>GPS\uff0c\u53c8\u79f0\u5168\u7403\u536b\u661f\u5b9a\u4f4d\u7cfb\u7edf\uff0c\u662f\u7f8e\u56fd\u56fd\u9632\u90e8\u7814\u5236\u548c\u7ef4\u62a4\u7684\u4e2d\u8ddd\u79bb\u5706\u578b\u8f68\u9053\u536b\u661f\u5bfc\u822a\u7cfb\u7edf\u3002\u5b83\u53ef\u4ee5\u4e3a\u5730\u7403\u8868\u9762\u7edd\u5927\u90e8\u5206\u5730\u533a\uff0898%\uff09\u63d0\u4f9b\u51c6\u786e\u7684\u5b9a\u4f4d\u3001\u6d4b\u901f\u548c\u9ad8\u7cbe\u5ea6\u7684\u6807\u51c6\u65f6\u95f4\u3002\u5168\u7403\u5b9a\u4f4d\u7cfb\u7edf\u53ef\u6ee1\u8db3\u4f4d\u4e8e\u5168\u7403\u5730\u9762\u4efb\u4f55\u4e00\u5904\u6216\u8fd1\u5730\u7a7a\u95f4\u7684\u519b\u4e8b\u7528\u6237\u8fde\u7eed\u4e14\u7cbe\u786e\u5730\u786e\u5b9a\u4e09\u7ef4\u4f4d\u7f6e\u3001\u4e09\u7ef4\u8fd0\u52a8\u548c\u65f6\u95f4\u7684\u9700\u6c42\u3002</p>"
})
}
return tempCZML;
}
module.exports = { tles2czml }