Feature/satellites (#2185)

* Remove planet geometry and simplespheregeometry
* Only use a single TLE loading implementation
* Add caching to the satellite loader;  Add Lua function to load kepler file
* Fix RenderablePlanetProjection specification
* Add OMM loading funtion;  Remove mean motion from Kepler parameters
* Replace TLETranslation class with GPTranslation and support OMM files
* Support loading SMDB files in kepler functions
* Merge RenderableSatellites and RenderableSmallBody with RenderableOrbitalKepler
* Update submodules
* Adapt existing satellites to new OMM file type
* Remove TLE helper
* Remove SSSB shared file and adapt sssb assets
This commit is contained in:
Alexander Bock
2022-08-02 13:11:50 +02:00
committed by GitHub
parent 351eb33d61
commit 7bc9e99b87
94 changed files with 3757 additions and 2723 deletions

View File

@@ -87,7 +87,7 @@ void FileSystemAccess::parseChildFile(std::string filename, bool& hasDirHeaderBe
else {
std::string extension = filename.substr(filename.length()
- _fileExtension.length());
if (extension.compare(_fileExtension) != 0) {
if (extension != _fileExtension) {
return;
}
}

View File

@@ -352,7 +352,7 @@ void PropertiesDialog::editBoxDisabled(bool disabled) {
void PropertiesDialog::parseSelections() {
// Handle case with only one remaining but empty line
if ((_propertyData.size() == 1) && (_propertyData.at(0).name.compare("") == 0)) {
if ((_propertyData.size() == 1) && _propertyData.at(0).name.empty()) {
_propertyData.clear();
}
*_properties = std::move(_propertyData);

View File

@@ -1,21 +1,50 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Amateur Radio",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Amateur Radio)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_amateur_radio",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=amateur&FORMAT=kvn",
Filename = "amateur.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=amateur&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "amateur",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "amateur.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Amateur Radio",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {
Name = "Satellites Communications - Amateur Radio",
Version = "1.0",
Description = [[Satellites asset for Communications - Amateur Radio. Data from
Celestrak]],
Description = [[Satellites asset for Communications - Amateur Radio. Data from
Celestrak]],
Author = "OpenSpace Team",
URL = "https://celestrak.com/NORAD/elements/",
License = "Celestrak"

View File

@@ -1,21 +1,49 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Experimental",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Experimental)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_x-comm",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=x-comm&FORMAT=kvn",
Filename = "x-comm.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=x-comm&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "x-comm",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "x-comm.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Experimental",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {
Name = "Satellites Communications - Experimental",
Version = "1.0",
Description = [[Satellites asset for Communications - Experimental. Data from
Celestrak]],
Description = "Satellites asset for Communications - Experimental. Data from Celestrak",
Author = "OpenSpace Team",
URL = "https://celestrak.com/NORAD/elements/",
License = "Celestrak"

View File

@@ -1,22 +1,51 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Geostationary",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Geostationary)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_geo",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=geo&FORMAT=kvn",
Filename = "geo.txt",
Url = "https://celestrak.com/NORAD/elements/gp.php?GROUP=geo&FORMAT=TLE",
TrailColor = { 0.9, 0.9, 0.0 },
Description = [[Satellites that are currently active and in a Geosynchronous orbit, meaning
their orbital period matches Earth's rotation]]
Override = true
})
local satellites = {
Identifier = "geo",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "geo.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.9, 0.9, 0.0 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Geostationary",
Path = "/Solar System/Planets/Earth/Satellites",
Description = [[Satellites that are currently active and in a Geosynchronous orbit,
meaning their orbital period matches Earth's rotation.]]
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {
Name = "Satellites Communications - Geostationary",
Version = "1.0",
Description = [[Satellites asset for Communications - Geostationary. Data from
Description = [[Satellites asset for Communications - Geostationary. Data from
Celestrak]],
Author = "OpenSpace Team",
URL = "https://celestrak.com/NORAD/elements/",

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "GlobalStar",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (GlobalStar)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_globalstar",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=globalstar&FORMAT=kvn",
Filename = "globalstar.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=globalstar&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "globalstar",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "globalstar.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "GlobalStar",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Gorizont",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Gorizont)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_gorizont",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=gorizont&FORMAT=kvn",
Filename = "gorizont.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=gorizont&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "gorizont",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "gorizont.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Gorizont",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Intelsat",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Intelsat)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_intelsat",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=intelsat&FORMAT=kvn",
Filename = "intelsat.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=intelsat&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "intelsat",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "intelsat.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Intelsat",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Iridium",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Iridium)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_iridium",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=iridium&FORMAT=kvn",
Filename = "iridium.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=iridium&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "iridium",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "iridium.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Iridium",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Iridium NEXT",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Iridium NEXT)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_iridium-NEXT",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=iridium-NEXT&FORMAT=kvn",
Filename = "iridium-NEXT.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=iridium-NEXT&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "iridium-NEXT",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "iridium-NEXT.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Iridium NEXT",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Molniya",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Molniya)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_molniya",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=molniya&FORMAT=kvn",
Filename = "molniya.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=molniya&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "molniya",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "molniya.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Molniya",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Orbcomm",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Orbcomm)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_orbcomm",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=orbcomm&FORMAT=kvn",
Filename = "orbcomm.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=orbcomm&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "orbcomm",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "orbcomm.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Orbcomm",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Other comm",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Other comm)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_other-comm",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=other-comm&FORMAT=kvn",
Filename = "other-comm.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=other-comm&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "other-comm",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "other-comm.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Other comm",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Raduga",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Raduga)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_raduga",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=raduga&FORMAT=kvn",
Filename = "raduga.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=raduga&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "raduga",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "raduga.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Raduga",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "SES",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (SES)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_ses",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=ses&FORMAT=kvn",
Filename = "ses.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=ses&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "ses",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "ses.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "SES",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,16 +1,45 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Starlink",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Starlink)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_starlink",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=starlink&FORMAT=kvn",
Filename = "starlink.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=starlink&FORMAT=TLE",
TrailColor = { 0.65, 0.55, 0.55 },
Description = [[LEO satellite constellation launched by SpaceX to provide
broadband internet access]]
Override = true
})
local satellites = {
Identifier = "starlink",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "starlink.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.65, 0.55, 0.55 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Starlink",
Path = "/Solar System/Planets/Earth/Satellites",
Description = [[LEO satellite constellation launched by SpaceX to provide broadband
internet access.]]
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Indian ASAT test Debris",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Indian ASAT test Debris)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_2019-006",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?INTDES=2019-006&FORMAT=kvn",
Filename = "2019-006.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?INTDES=2019-006&FORMAT=TLE",
TrailColor = { 0.25, 0.35, 0.45 }
Override = true
})
local satellites = {
Identifier = "2019-006",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "2019-006.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.25, 0.35, 0.45 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Indian ASAT test Debris",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Breeze-M Breakup",
Filename = "2021-044.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?INTDES=2012-044&FORMAT=TLE",
TrailColor = { 0.25, 0.35, 0.45 }
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Breeze-M Breakup)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_2012-044",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?INTDES=2012-044&FORMAT=kvn",
Filename = "2012-044.txt",
Override = true
})
local satellites = {
Identifier = "2012-044",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "2012-044.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.25, 0.35, 0.45 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Breeze-M Breakup",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Fengyun Debris",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Fengyun Debris)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_1999-025",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?INTDES=1999-025&FORMAT=kvn",
Filename = "1999-025.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?INTDES=1999-025&FORMAT=TLE",
TrailColor = { 0.784, 1.0, 0.737 }
Override = true
})
local satellites = {
Identifier = "1999-025",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "1999-025.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.784, 1.0, 0.737 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Fengyun Debris",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Iridium 33 Debris",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Iridium 33 Debris)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_iridium-33-debris",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=iridium-33-debris&FORMAT=kvn",
Filename = "iridium-33-debris.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=iridium-33-debris&FORMAT=TLE",
TrailColor = { 0.8, 0.0, 0.3 }
Override = true
})
local satellites = {
Identifier = "iridium-33-debris",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "iridium-33-debris.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.8, 0.0, 0.3 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Iridium 33 Debris",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Kosmos 2251 Debris",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Kosmos 2251 Debris)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_cosmos-2251-debris",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=cosmos-2251-debris&FORMAT=kvn",
Filename = "cosmos-2251-debris.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=cosmos-2251-debris&FORMAT=TLE",
TrailColor = { 0.66, 0.8, 0.5 }
Override = true
})
local satellites = {
Identifier = "cosmos-2251-debris",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "cosmos-2251-debris.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.66, 0.8, 0.5 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Kosmos 2251 Debris",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,15 +1,44 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Active",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Active)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_active",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=active&FORMAT=kvn",
Filename = "active.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=active&FORMAT=TLE",
TrailColor = { 0.45, 0.25, 0.45 },
Description = "Satellites that employ active communication"
Override = true
})
local satellites = {
Identifier = "active",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "active.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.45, 0.25, 0.45 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Active",
Path = "/Solar System/Planets/Earth/Satellites",
Description = [[Satellites that employ active communication.]]
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,16 +1,45 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "100 Brightest",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (100 Brightest)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_visual",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=visual&FORMAT=kvn",
Filename = "visual.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=visual&FORMAT=TLE",
TrailColor = { 0.55, 0.25, 0.65 },
Description = [[The 100 (or so) satellites that will appear brightest when viewed
from Earth]]
Override = true
})
local satellites = {
Identifier = "visual",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "visual.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.55, 0.25, 0.65 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "100 Brightest",
Path = "/Solar System/Planets/Earth/Satellites",
Description = [[The 100 (or so) satellites that will appear brightest when viewed
from Earth.]]
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "CubeSat",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (CubeSat)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_cubesat",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=cubesat&FORMAT=kvn",
Filename = "cubesat.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=cubesat&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "cubesat",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "cubesat.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "CubeSat",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,83 +1,78 @@
local transforms = asset.require('scene/solarsystem/planets/earth/transforms')
local satelliteHelper = asset.require('util/tle_helper')
local url = "https://celestrak.com/NORAD/elements/gp.php?CATNR=20580"
local identifier = "HST"
local filename = "HST.txt"
local nodes = {}
local tle = satelliteHelper.downloadTLEFile(asset, url, identifier, filename)
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Hubble)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_hst",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?CATNR=20580&FORMAT=kvn",
Filename = "hst.txt",
Override = true
})
local initializeAndAddNodes = function()
local lineElement = satelliteHelper.makeSingleLineElement(tle, filename)
local period = satelliteHelper.getPeriodFromElement(lineElement)
local path = tle .. "/" .. filename
local HubblePosition = {
Identifier = "HubblePosition",
Parent = transforms.EarthInertial.Identifier,
Transform = {
Translation = {
Type = "TLETranslation",
Body = identifier,
Observer = transforms.EarthInertial.Identifier,
File = path,
LineNumber = 1
},
Rotation = {
Type = "SpiceRotation",
SourceFrame = "GALACTIC",
DestinationFrame = "J2000",
}
local HubblePosition = {
Identifier = "HubblePosition",
Parent = transforms.EarthInertial.Identifier,
Transform = {
Translation = {
Type = "GPTranslation",
Observer = transforms.EarthInertial.Identifier,
File = omm .. "hst.txt",
Format = "OMM"
},
Tag = { "earth_satellite", "hubble" },
GUI = {
Name = "Hubble Position",
Path = "/Solar System/Planets/Earth/Satellites/Hubble",
Hidden = true
Rotation = {
Type = "SpiceRotation",
SourceFrame = "GALACTIC",
DestinationFrame = "J2000",
}
},
Tag = { "earth_satellite", "hubble" },
GUI = {
Name = "Hubble Position",
Path = "/Solar System/Planets/Earth/Satellites/Hubble",
Hidden = true
}
}
local HubbleTrail = {
Identifier = "HubbleTrail",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableTrailOrbit",
Translation = {
Type = "TLETranslation",
Body = identifier,
Observer = transforms.EarthInertial.Identifier,
File = path,
LineNumber = 1
},
RenderBinMode = "PostDeferredTransparent",
Color = { 0.0, 0.902, 0.6},
Fade = 1.5,
Period = period,
Resolution = 320
local HubbleTrail = {
Identifier = "HubbleTrail",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableTrailOrbit",
Translation = {
Type = "GPTranslation",
Observer = transforms.EarthInertial.Identifier,
File = omm .. "hst.txt",
Format = "OMM"
},
Tag = { "earth_satellite", "hubble" },
GUI = {
Name = "Hubble Trail",
Path = "/Solar System/Planets/Earth/Satellites/Hubble"
}
RenderBinMode = "PostDeferredTransparent",
Color = { 0.0, 0.902, 0.6},
Fade = 1.5,
Resolution = 320
},
Tag = { "earth_satellite", "hubble" },
GUI = {
Name = "Hubble Trail",
Path = "/Solar System/Planets/Earth/Satellites/Hubble"
}
return { HubblePosition, HubbleTrail }
end
}
asset.onInitialize(function ()
nodes = initializeAndAddNodes()
for _, node in ipairs(nodes) do
openspace.addSceneGraphNode(node)
end
local hubble = openspace.space.readKeplerFile(omm .. "hst.txt", "OMM")
HubbleTrail.Renderable.Period = hubble[0].Period / (60 * 60 * 24)
openspace.addSceneGraphNode(HubblePosition)
openspace.addSceneGraphNode(HubbleTrail)
end)
asset.onDeinitialize(function ()
for i=1, #nodes do
openspace.removeSceneGraphNode(nodes[#nodes + 1 - i].Identifier)
end
openspace.removeSceneGraphNode(HubbleTrail)
openspace.removeSceneGraphNode(HubblePosition)
end)
asset.export(HubblePosition)
asset.export(HubbleTrail)
asset.meta = {
Name = "Hubble Space Telescope Trail",
Version = "1.0",

View File

@@ -1,145 +1,142 @@
local satelliteHelper = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local sun = asset.require("scene/solarsystem/sun/sun")
local url = "http://celestrak.com/NORAD/elements/gp.php?CATNR=25544"
local identifier = "ISS"
local filename = "ISS.txt"
local nodes = {}
local tle = satelliteHelper.downloadTLEFile(asset, url, identifier, filename)
local models = asset.syncedResource({
Name = "ISS Models",
Type = "HttpSynchronization",
Identifier = "iss_model",
Version = 3
Name = "ISS Models",
Type = "HttpSynchronization",
Identifier = "iss_model",
Version = 3
})
local initializeAndAddNodes = function()
local lineElement = satelliteHelper.makeSingleLineElement(tle, filename)
local period = satelliteHelper.getPeriodFromElement(lineElement)
local path = tle .. filename
local omm = asset.syncedResource({
Name = "Satellite OMM Data (ISS)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_iss",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?CATNR=25544&FORMAT=kvn",
Filename = "ISS.txt",
Override = true
})
local iss = {
Identifier = "ISS",
Parent = transforms.EarthInertial.Identifier,
BoundingSphere = 54.5, -- half the width
InteractionSphere = 30,
Transform = {
Translation = {
Type = "TLETranslation",
Body = identifier,
Observer = transforms.EarthInertial.Identifier,
File = path,
LineNumber = 1
},
Rotation = {
Type = "SpiceRotation",
SourceFrame = "GALACTIC",
DestinationFrame = "J2000",
}
local iss = {
Identifier = "ISS",
Parent = transforms.EarthInertial.Identifier,
BoundingSphere = 54.5, -- half the width
InteractionSphere = 30,
Transform = {
Translation = {
Type = "GPTranslation",
Observer = transforms.EarthInertial.Identifier,
File = omm .. "ISS.txt",
Format = "OMM"
},
Tag = { "earth_satellite", "ISS" },
GUI = {
Path = "/Solar System/Planets/Earth/Satellites/ISS"
Rotation = {
Type = "SpiceRotation",
SourceFrame = "GALACTIC",
DestinationFrame = "J2000",
}
},
Tag = { "earth_satellite", "ISS" },
GUI = {
Name = "ISS",
Path = "/Solar System/Planets/Earth/Satellites/ISS"
}
}
local parentNode = {
Identifier = "ISSModel",
Parent = iss.Identifier,
Transform = {
Rotation = {
Type = "FixedRotation",
Attached = "ISSModel",
XAxis = { 0.01, -1.0, 0.56 },
XAxisOrthogonal = true,
YAxis = transforms.EarthInertial.Identifier
}
},
Renderable = {
Type = "RenderableModel",
GeometryFile = models .. "ISS.fbx",
ModelScale = "Centimeter",
LightSources = {
sun.LightSource
},
PerformShading = true,
DisableFaceCulling = true
},
GUI = {
Name = "ISS Model",
Path = "/Solar System/Planets/Earth/Satellites/ISS"
local parentNode = {
Identifier = "ISSModel",
Parent = iss.Identifier,
Transform = {
Rotation = {
Type = "FixedRotation",
Attached = "ISSModel",
XAxis = { 0.01, -1.0, 0.56 },
XAxisOrthogonal = true,
YAxis = transforms.EarthInertial.Identifier
}
}
local issTrail = {
Identifier = identifier .. "_trail",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableTrailOrbit",
Translation = {
Type = "TLETranslation",
Body = identifier,
Observer = transforms.EarthInertial.Identifier,
File = path,
LineNumber = 1,
},
RenderBinMode = "PostDeferredTransparent",
Color = { 0.9, 0.6715, 0.0 },
Fade = 1.5,
Period = period,
Resolution = 320
},
Renderable = {
Type = "RenderableModel",
GeometryFile = models .. "ISS.fbx",
ModelScale = "Centimeter",
LightSources = {
sun.LightSource
},
Tag = { "earth_satellite", "ISS" },
GUI = {
Name = "ISS Trail",
Path = "/Solar System/Planets/Earth/Satellites/ISS"
}
PerformShading = true,
DisableFaceCulling = true
},
GUI = {
Name = "ISS Model",
Path = "/Solar System/Planets/Earth/Satellites/ISS"
}
}
-- @TODO (emmbr, 2021-05-27) add to scene when label rendering issues have been fixed
local IssLabel = {
Identifier = "IssLabel",
Parent = iss.Identifier,
Renderable = {
Enabled = false,
Type = "RenderableLabels",
Text = "ISS",
FontSize = 70.0,
Size = 3.4,
MinMaxSize = { 1, 100 },
OrientationOption = "Camera View Direction",
BlendMode = "Additive",
EnableFading = true,
FadeDistances = { 0.15, 15.0 },
FadeWidths = { 1.0, 25.0 }
local issTrail = {
Identifier = "ISS_trail",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableTrailOrbit",
Translation = {
Type = "GPTranslation",
Observer = transforms.EarthInertial.Identifier,
File = omm .. "ISS.txt",
Format = "OMM"
},
Tag = { "solarsystem_labels" },
GUI = {
Name = "ISS Label",
Path = "/Solar System/Planets/Earth/Satellites"
}
RenderBinMode = "PostDeferredTransparent",
Color = { 0.9, 0.6715, 0.0 },
Fade = 1.5,
Resolution = 320
},
Tag = { "earth_satellite", "ISS" },
GUI = {
Name = "ISS Trail",
Path = "/Solar System/Planets/Earth/Satellites/ISS"
}
}
return { iss, parentNode, issTrail }
end
-- @TODO (emmbr, 2021-05-27) add to scene when label rendering issues have been fixed
local IssLabel = {
Identifier = "IssLabel",
Parent = iss.Identifier,
Renderable = {
Enabled = false,
Type = "RenderableLabels",
Text = "ISS",
FontSize = 70.0,
Size = 3.4,
MinMaxSize = { 1, 100 },
OrientationOption = "Camera View Direction",
BlendMode = "Additive",
EnableFading = true,
FadeDistances = { 0.15, 15.0 },
FadeWidths = { 1.0, 25.0 }
},
Tag = { "solarsystem_labels" },
GUI = {
Name = "ISS Label",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
asset.onInitialize(function ()
nodes = initializeAndAddNodes()
for _, node in ipairs(nodes) do
openspace.addSceneGraphNode(node)
end
local i = openspace.space.readKeplerFile(omm .. "ISS.txt", "OMM")
issTrail.Renderable.Period = i[0].Period / (60 * 60 * 24)
openspace.addSceneGraphNode(iss)
openspace.addSceneGraphNode(parentNode)
openspace.setPropertyValueSingle("Scene.ISSModel.Rotation.yAxisInvertObject", true)
openspace.addSceneGraphNode(issTrail)
end)
asset.onDeinitialize(function ()
for i=1, #nodes do
openspace.removeSceneGraphNode(nodes[#nodes + 1 - i].Identifier)
end
openspace.removeSceneGraphNode(issTrail)
openspace.removeSceneGraphNode(parentNode)
openspace.removeSceneGraphNode(iss)
end)
asset.export(issTrail)
asset.export(parentNode)
asset.export(iss)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Military",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Military)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_military",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=military&FORMAT=kvn",
Filename = "military.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=military&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "military",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "military.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Military",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Other",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Other)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_other",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=other&FORMAT=kvn",
Filename = "other.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=other&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "other",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "other.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Other",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Radar Calibration",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Radar Calibration)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_radar",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=radar&FORMAT=kvn",
Filename = "radar.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=radar&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "radar",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "radar.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Radar Calibration",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,17 +1,46 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "SpaceStations",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (SpaceStations)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_stations",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=stations&FORMAT=kvn",
Filename = "stations.txt",
Url = "https://celestrak.com/NORAD/elements/gp.php?GROUP=stations&FORMAT=TLE",
TrailColor = { 0.9, 0.1, 0.0 },
Description = [[A collection of space stations (including the ISS and China's
Tiangong), along with certain cubesats and satellite constellations from space
agencies]]
Override = true
})
local satellites = {
Identifier = "stations",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "stations.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.9, 0.1, 0.0 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "SpaceStations",
Path = "/Solar System/Planets/Earth/Satellites",
Description = [[A collection of space stations (including the ISS and China's
Tiangong), along with certain cubesats and satellite constellations from space
agencies.]]
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,15 +1,44 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Last 30 Days",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Last 30 Days)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_tle-new",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=last-30-days&FORMAT=kvn",
Filename = "tle-new.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=last-30-days&FORMAT=TLE",
TrailColor = { 0.65, 0.25, 0.45 },
Description = [[All the satellites that have been launched in the last 30 days]]
Override = true
})
local satellites = {
Identifier = "tle-new",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "tle-new.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.65, 0.25, 0.45 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Last 30 Days",
Path = "/Solar System/Planets/Earth/Satellites",
Description = [[All the satellites that have been launched in the last 30 days.]]
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Beidou",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Beidou)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_beidou",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=beidou&FORMAT=kvn",
Filename = "beidou.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=beidou&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "beidou",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "beidou.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Beidou",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Galileo",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Galileo)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_galileo",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=galileo&FORMAT=kvn",
Filename = "galileo.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=galileo&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local galileo = {
Identifier = "galileo",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "galileo.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Galileo",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(galileo)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(galileo)
end)
asset.export(galileo)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Glosnass",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Glosnass)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_glo-ops",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=glo-ops&FORMAT=kvn",
Filename = "glo-ops.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=glo-ops&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "glo-ops",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "glo-ops.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Glosnass",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,15 +1,44 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "GPS",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (GPS)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_gps-ops",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=gps-ops&FORMAT=kvn",
Filename = "gps-ops.txt",
Url = "https://celestrak.com/NORAD/elements/gp.php?GROUP=gps-ops&FORMAT=TLE",
TrailColor = { 0.9, 0.5, 0.0 },
Description = "The GPS satellites that give us our precise locations back here on Earth"
Override = true
})
local satellites = {
Identifier = "gps-ops",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "gps-ops.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.9, 0.5, 0.0 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "GPS",
Path = "/Solar System/Planets/Earth/Satellites",
Description = [[The GPS satellites that give us our precise locations back on Earth.]]
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Russian LEO Navigation",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Russian LEO Navigation)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_musson",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=musson&FORMAT=kvn",
Filename = "musson.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=musson&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "musson",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "musson.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Russian LEO Navigation",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,44 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Navy Navigation Satellite System",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Navy Navigation Satellite System)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_nnss",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=nnss&FORMAT=kvn",
Filename = "nnss.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=nnss&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "nnss",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "nnss.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Navy Navigation Satellite System",
Path = "/Solar System/Planets/Earth/Satellites",
Description = [[The first satellite navigation system to be used operationally.]]
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Satellite Based Augmentation System",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Satellite Based Augmentation System)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_sbas",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=sbas&FORMAT=kvn",
Filename = "sbas.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=sbas&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "sbas",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "sbas.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Satellite Based Augmentation System",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Education",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Education)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_education",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=education&FORMAT=kvn",
Filename = "education.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=education&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "education",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "education.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Education",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Engineering",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Engineering)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_engineering",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=engineering&FORMAT=kvn",
Filename = "engineering.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=engineering&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "engineering",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "engineering.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Engineering",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Geodect",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Geodetic)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_geodetic",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=geodetic&FORMAT=kvn",
Filename = "geodetic.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=geodetic&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "geodetic",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "geodetic.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Geodetic",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,21 +1,49 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Space & Earth Science",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Space & Earth Science)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_science",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=science&FORMAT=kvn",
Filename = "science.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=science&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "science",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "science.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Space & Earth Science",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {
Name = "Satellites Science Space & Earth Science",
Version = "1.0",
Description = [[Satellites asset for Science Space & Earth Science. Data from
Celestrak]],
Description = "Satellites asset for Science Space & Earth Science. Data from Celestrak",
Author = "OpenSpace Team",
URL = "https://celestrak.com/NORAD/elements/",
License = "Celestrak"

View File

@@ -1,111 +1,105 @@
local satelliteHelper = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local sunTransforms = asset.require("scene/solarsystem/sun/transforms")
local url = "http://celestrak.com/NORAD/elements/gp.php?CATNR=27424"
local identifier = "Aqua"
local filename = "Aqua.txt"
local nodes = {}
local tle = satelliteHelper.downloadTLEFile(asset, url, identifier, filename)
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Aqua)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_aqua",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?CATNR=27424&FORMAT=kvn",
Filename = "Aqua.txt",
Override = true
})
local Aqua = {
Identifier = "Aqua",
Parent = transforms.EarthInertial.Identifier,
InteractionSphere = 30,
Transform = {
Translation = {
Type = "GPTranslation",
Observer = transforms.EarthInertial.Identifier,
File = omm .. "Aqua.txt",
Format = "OMM"
},
Rotation = {
Type = "SpiceRotation",
SourceFrame = "GALACTIC",
DestinationFrame = "J2000",
}
},
Tag = { "earth_satellite", "Aqua" },
GUI = {
Name = "Aqua",
Path = "/Solar System/Planets/Earth/Satellites/Aqua"
}
}
local AquaTrail = {
Identifier = "Aqua_trail",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableTrailOrbit",
Translation = {
Type = "GPTranslation",
Observer = transforms.EarthInertial.Identifier,
File = omm .. "Aqua.txt",
Format = "OMM",
RenderBinMode = "PostDeferredTransparent"
},
Color = { 0.9, 0.6715, 0.0 },
Fade = 1.5,
Resolution = 320
},
Tag = { "earth_satellite", "Aqua" },
GUI = {
Name = "Aqua Trail",
Path = "/Solar System/Planets/Earth/Satellites/Aqua"
}
}
-- @TODO (emmbr, 2021-05-27) this label is not visible. Too large fade distances?
-- Might also be affected by the rendering issues for labels
local AquaLabel = {
Identifier = "AquaLabel",
Parent = Aqua.Identifier,
Renderable = {
Enabled = false,
Type = "RenderableLabels",
Text = "Aqua",
FontSize = 70.0,
Size = 4.0,
MinMaxSize = { 1, 100 },
OrientationOption = "Camera View Direction",
BlendMode = "Additive",
EnableFading = true,
FadeUnit = "au",
FadeDistances = { 1.5, 15.0 },
FadeWidths = { 1.0, 25.0 }
},
Tag = { "solarsystem_labels" },
GUI = {
Name = "Aqua Label",
Path = "/Solar System/Planets/Earth"
}
}
asset.onInitialize(function ()
local lineElement = satelliteHelper.makeSingleLineElement(tle, filename)
local period = satelliteHelper.getPeriodFromElement(lineElement)
local path = tle .. "/" .. filename
local a = openspace.space.readKeplerFile(omm .. "Aqua.txt", "OMM")
AquaTrail.Renderable.Period = a[0].Period / (60 * 60 * 24)
local Aqua = {
Identifier = "Aqua",
Parent = transforms.EarthInertial.Identifier,
InteractionSphere = 30,
Transform = {
Translation = {
Type = "TLETranslation",
Body = identifier,
Observer = transforms.EarthInertial.Identifier,
File = path,
LineNumber = 1
},
Rotation = {
Type = "SpiceRotation",
SourceFrame = "GALACTIC",
DestinationFrame = "J2000",
}
},
Tag = { "earth_satellite", "Aqua" },
GUI = {
Path = "/Solar System/Planets/Earth/Satellites/Aqua"
}
}
local AquaTrail = {
Identifier = identifier .. "_trail",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableTrailOrbit",
Translation = {
Type = "TLETranslation",
Body = identifier,
Observer = transforms.EarthInertial.Identifier,
File = path,
LineNumber = 1,
RenderBinMode = "PostDeferredTransparent"
},
Color = { 0.9, 0.6715, 0.0 },
Fade = 1.5,
Period = period,
Resolution = 320
},
Tag = { "earth_satellite", "Aqua" },
GUI = {
Name = "Aqua Trail",
Path = "/Solar System/Planets/Earth/Satellites/Aqua"
}
}
-- @TODO (emmbr, 2021-05-27) this label is not visible. Too large fade distances?
-- Might also be affected by the rendering issues for labels
local AquaLabel = {
Identifier = "AquaLabel",
Parent = Aqua.Identifier,
Renderable = {
Enabled = false,
Type = "RenderableLabels",
Text = "Aqua",
FontSize = 70.0,
Size = 4.0,
MinMaxSize = { 1, 100 },
OrientationOption = "Camera View Direction",
BlendMode = "Additive",
EnableFading = true,
FadeUnit = "au",
FadeDistances = { 1.5, 15.0 },
FadeWidths = { 1.0, 25.0 }
},
Tag = { "solarsystem_labels" },
GUI = {
Name = "Aqua Label",
Path = "/Solar System/Planets/Earth"
}
}
table.insert(nodes, Aqua)
table.insert(nodes, AquaTrail)
table.insert(nodes, AquaLabel)
for _, node in ipairs(nodes) do
openspace.addSceneGraphNode(node)
end
openspace.addSceneGraphNode(Aqua)
openspace.addSceneGraphNode(AquaTrail)
openspace.addSceneGraphNode(AquaLabel)
end)
asset.onDeinitialize(function ()
for _, node in ipairs(nodes) do
openspace.removeSceneGraphNode(node)
end
openspace.removeSceneGraphNode(AquaLabel)
openspace.removeSceneGraphNode(AquaTrail)
openspace.removeSceneGraphNode(Aqua)
end)
for _, node in ipairs(nodes) do
asset.export(node)
end
asset.export(Aqua)
asset.export(AquaTrail)
asset.export(AquaLabel)
asset.meta = {
Name = "Aqua",

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "ARGOS",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (ARGOS)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_argos",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=argos&FORMAT=kvn",
Filename = "argos.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=argos&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "argos",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "argos.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "ARGOS",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,21 +1,49 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Disaster Monitoring",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Disaster Monitoring)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_dmc",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=dmc&FORMAT=kvn",
Filename = "dmc.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=dmc&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "dmc",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "dmc.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Disaster Monitoring",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {
Name = "Satellites Weather - Disaster Monitoring",
Version = "1.0",
Description = [[Satellites asset for Weather - Disaster Monitoring. Data from
Celestrak]],
Description = "Satellites asset for Weather - Disaster Monitoring. Data from Celestrak",
Author = "OpenSpace Team",
URL = "https://celestrak.com/NORAD/elements/",
License = "Celestrak"

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Earth Resources",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Earth Resources)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_resource",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=resource&FORMAT=kvn",
Filename = "resource.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=resource&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "resource",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "resource.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Earth Resources",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "GOES",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (GOES)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_goes",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=goes&FORMAT=kvn",
Filename = "goes.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=goes&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "goes",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "goes.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "GOES",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "NOAA",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (NOAA)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_noaa",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=noaa&FORMAT=kvn",
Filename = "noaa.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=noaa&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "noaa",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "noaa.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "NOAA",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Planet",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Planet)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_planet",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=planet&FORMAT=kvn",
Filename = "planet.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=planet&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "planet",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "planet.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Planet",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Search & Rescue (SARSAT)",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Search & Rescue (SARSAT))",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_sarsat",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=sarsat&FORMAT=kvn",
Filename = "sarsat.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=sarsat&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "sarsat",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "sarsat.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Search & Rescue (SARSAT)",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,110 +1,104 @@
local satelliteHelper = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local sunTransforms = asset.require("scene/solarsystem/sun/transforms")
local url = "http://celestrak.com/NORAD/elements/gp.php?CATNR=37849"
local identifier = "SNPP"
local filename = "SNPP.txt"
local nodes = {}
local tle = satelliteHelper.downloadTLEFile(asset, url, identifier, filename)
local omm = asset.syncedResource({
Name = "Satellite OMM Data (SNPP)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_snpp",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?CATNR=37849&FORMAT=kvn",
Filename = "SNPP.txt",
Override = true
})
local SNPP = {
Identifier = "SNPP",
Parent = transforms.EarthInertial.Identifier,
InteractionSphere = 30,
Transform = {
Translation = {
Type = "GPTranslation",
Observer = transforms.EarthInertial.Identifier,
File = omm .. "SNPP.txt",
Format = "OMM"
},
Rotation = {
Type = "SpiceRotation",
SourceFrame = "GALACTIC",
DestinationFrame = "J2000",
}
},
Tag = { "earth_satellite", "SNPP" },
GUI = {
Name = "SNPP",
Path = "/Solar System/Planets/Earth/Satellites/SNPP"
}
}
local SNPPTrail = {
Identifier = "SNPP_trail",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableTrailOrbit",
Translation = {
Type = "GPTranslation",
Observer = transforms.EarthInertial.Identifier,
File = omm .. "SNPP.txt",
Format = "OMM",
RenderBinMode = "PostDeferredTransparent"
},
Color = { 0.9, 0.6715, 0.0 },
Fade = 1.5,
Resolution = 320
},
Tag = { "earth_satellite", "SNPP" },
GUI = {
Name = "SNPP Trail",
Path = "/Solar System/Planets/Earth/Satellites/SNPP"
}
}
local SNPPLabel = {
Identifier = "SNPPLabel",
Parent = SNPP.Identifier,
Renderable = {
Enabled = false,
Type = "RenderableLabels",
Text = "SNPP",
FontSize = 70.0,
Size = 4.0,
MinMaxSize = { 1, 100 },
OrientationOption = "Camera View Direction",
BlendMode = "Additive",
EnableFading = true,
FadeUnit = "au",
FadeDistances = { 1.5, 15.0 },
FadeWidths = { 1.0, 25.0 }
},
Tag = { "solarsystem_labels" },
GUI = {
Name = "SNPP Label",
Path = "/Solar System/Planets/Earth"
}
}
asset.onInitialize(function ()
local lineElement = satelliteHelper.makeSingleLineElement(tle, filename)
local period = satelliteHelper.getPeriodFromElement(lineElement)
local path = tle .. "/" .. filename
local SNPP = {
Identifier = "SNPP",
Parent = transforms.EarthInertial.Identifier,
InteractionSphere = 30,
Transform = {
Translation = {
Type = "TLETranslation",
Body = identifier,
Observer = transforms.EarthInertial.Identifier,
File = path,
LineNumber = 1
},
Rotation = {
Type = "SpiceRotation",
SourceFrame = "GALACTIC",
DestinationFrame = "J2000",
}
},
Tag = { "earth_satellite", "SNPP" },
GUI = {
Path = "/Solar System/Planets/Earth/Satellites/SNPP"
}
}
local SNPPTrail = {
Identifier = identifier .. "_trail",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableTrailOrbit",
Translation = {
Type = "TLETranslation",
Body = identifier,
Observer = transforms.EarthInertial.Identifier,
File = path,
LineNumber = 1,
RenderBinMode = "PostDeferredTransparent"
},
Color = { 0.9, 0.6715, 0.0 },
Fade = 1.5,
Period = period,
Resolution = 320
},
Tag = { "earth_satellite", "SNPP" },
GUI = {
Name = "SNPP Trail",
Path = "/Solar System/Planets/Earth/Satellites/SNPP"
}
}
local SNPPLabel = {
Identifier = "SNPPLabel",
Parent = SNPP.Identifier,
Renderable = {
Enabled = false,
Type = "RenderableLabels",
Text = "SNPP",
FontSize = 70.0,
Size = 4.0,
MinMaxSize = { 1, 100 },
OrientationOption = "Camera View Direction",
BlendMode = "Additive",
EnableFading = true,
FadeUnit = "au",
FadeDistances = { 1.5, 15.0 },
FadeWidths = { 1.0, 25.0 }
},
Tag = { "solarsystem_labels" },
GUI = {
Name = "SNPP Label",
Path = "/Solar System/Planets/Earth"
}
}
table.insert(nodes, SNPP)
table.insert(nodes, SNPPTrail)
table.insert(nodes, SNPPLabel)
for _, node in ipairs(nodes) do
openspace.addSceneGraphNode(node)
end
local s = openspace.space.readKeplerFile(omm .. "SNPP.txt", "OMM")
SNPPTrail.Renderable.Period = s[0].Period / (60 * 60 * 24)
openspace.addSceneGraphNode(SNPP)
openspace.addSceneGraphNode(SNPPTrail)
openspace.addSceneGraphNode(SNPPLabel)
end)
asset.onDeinitialize(function ()
for _, node in ipairs(nodes) do
openspace.removeSceneGraphNode(node)
end
openspace.removeSceneGraphNode(SNPPLabel)
openspace.removeSceneGraphNode(SNPPTrail)
openspace.removeSceneGraphNode(SNPP)
end)
asset.export(SNPP)
asset.export(SNPPTrail)
asset.export(SNPPLabel)
asset.meta = {
Name = "SNPP",

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Spire",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Spire)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_spire",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=spire&FORMAT=kvn",
Filename = "spire.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=spire&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "spire",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "spire.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Spire",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Tracking and Data Relay Satellite System (TDRSS)",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Tracking and Data Relay Satellite System (TDRSS))",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_tdrss",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=tdrss&FORMAT=kvn",
Filename = "tdrss.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=tdrss&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "tdrss",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "tdrss.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Tracking and Data Relay Satellite System (TDRSS)",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,110 +1,107 @@
local satelliteHelper = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local sunTransforms = asset.require("scene/solarsystem/sun/transforms")
local url = "http://celestrak.com/NORAD/elements/gp.php?CATNR=25994"
local identifier = "Terra"
local filename = "Terra.txt"
local nodes = {}
local tle = satelliteHelper.downloadTLEFile(asset, url, identifier, filename)
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Terra)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_terra",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?CATNR=25994&FORMAT=kvn",
Filename = "Terra.txt",
Override = true
})
asset.onInitialize(function ()
local lineElement = satelliteHelper.makeSingleLineElement(tle, filename)
local period = satelliteHelper.getPeriodFromElement(lineElement)
local path = tle .. "/" .. filename
local Terra = {
Identifier = "Terra",
Parent = transforms.EarthInertial.Identifier,
InteractionSphere = 30,
Transform = {
Translation = {
Type = "TLETranslation",
Body = identifier,
Observer = transforms.EarthInertial.Identifier,
File = path,
LineNumber = 1
},
Rotation = {
Type = "SpiceRotation",
SourceFrame = "GALACTIC",
DestinationFrame = "J2000",
}
local Terra = {
Identifier = "Terra",
Parent = transforms.EarthInertial.Identifier,
InteractionSphere = 30,
Transform = {
Translation = {
Type = "GPTranslation",
Observer = transforms.EarthInertial.Identifier,
File = omm .. "Terra.txt",
Format = "OMM"
},
Tag = { "earth_satellite", "Terra" },
GUI = {
Path = "/Solar System/Planets/Earth/Satellites/Terra"
Rotation = {
Type = "SpiceRotation",
SourceFrame = "GALACTIC",
DestinationFrame = "J2000",
}
},
Tag = { "earth_satellite", "Terra" },
GUI = {
Name = "Terra",
Path = "/Solar System/Planets/Earth/Satellites/Terra"
}
}
local TerraTrail = {
Identifier = identifier .. "_trail",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableTrailOrbit",
Translation = {
Type = "TLETranslation",
Body = identifier,
Observer = transforms.EarthInertial.Identifier,
File = path,
LineNumber = 1,
RenderBinMode = "PostDeferredTransparent"
},
Color = { 0.9, 0.6715, 0.0 },
Fade = 1.5,
Period = period,
Resolution = 320
local TerraTrail = {
Identifier = "Terra_trail",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableTrailOrbit",
Translation = {
Type = "GPTranslation",
Observer = transforms.EarthInertial.Identifier,
File = omm .. "Terra.txt",
Format = "OMM",
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellite", "Terra" },
GUI = {
Name = "Terra Trail",
Path = "/Solar System/Planets/Earth/Satellites/Terra"
}
Color = { 0.9, 0.6715, 0.0 },
Fade = 1.5,
Resolution = 320
},
Tag = { "earth_satellite", "Terra" },
GUI = {
Name = "Terra Trail",
Path = "/Solar System/Planets/Earth/Satellites/Terra"
}
}
-- @TODO (emmbr, 2021-05-27) this label is not visible. Too large fade distances?
-- Might also be affected by the rendering issues for labels
local TerraLabel = {
Identifier = "TerraLabel",
Parent = Terra.Identifier,
Renderable = {
Enabled = false,
Type = "RenderableLabels",
Text = "Terra",
FontSize = 70.0,
Size = 4.0,
MinMaxSize = { 1, 100 },
OrientationOption = "Camera View Direction",
BlendMode = "Additive",
EnableFading = true,
FadeUnit = "au",
FadeDistances = { 1.5, 15.0 },
FadeWidths = { 1.0, 25.0 }
},
Tag = { "solarsystem_labels" },
GUI = {
Name = "Terra Label",
Path = "/Solar System/Planets/Earth"
}
-- @TODO (emmbr, 2021-05-27) this label is not visible. Too large fade distances?
-- Might also be affected by the rendering issues for labels
local TerraLabel = {
Identifier = "TerraLabel",
Parent = Terra.Identifier,
Renderable = {
Enabled = false,
Type = "RenderableLabels",
Text = "Terra",
FontSize = 70.0,
Size = 4.0,
MinMaxSize = { 1, 100 },
OrientationOption = "Camera View Direction",
BlendMode = "Additive",
EnableFading = true,
FadeUnit = "au",
FadeDistances = { 1.5, 15.0 },
FadeWidths = { 1.0, 25.0 }
},
Tag = { "solarsystem_labels" },
GUI = {
Name = "Terra Label",
Path = "/Solar System/Planets/Earth"
}
}
table.insert(nodes, Terra)
table.insert(nodes, TerraTrail)
table.insert(nodes, TerraLabel)
for _, node in ipairs(nodes) do
openspace.addSceneGraphNode(node)
end
asset.onInitialize(function()
local t = openspace.space.readKeplerFile(omm .. "Terra.txt", "OMM")
TerraTrail.Renderable.Period = t[0].Period / (60 * 60 * 24)
openspace.addSceneGraphNode(Terra)
openspace.addSceneGraphNode(TerraTrail)
openspace.addSceneGraphNode(TerraLabel)
end)
asset.onDeinitialize(function ()
for _, node in ipairs(nodes) do
openspace.removeSceneGraphNode(node)
end
openspace.removeSceneGraphNode(TerraLabel)
openspace.removeSceneGraphNode(TerraTrail)
openspace.removeSceneGraphNode(Terra)
end)
asset.export(Terra)
asset.export(TerraTrail)
asset.export(TerraLabel)
asset.meta = {
Name = "Terra",

View File

@@ -1,14 +1,43 @@
local shared = asset.require("util/tle_helper")
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
local group = {
Title = "Weather",
local omm = asset.syncedResource({
Name = "Satellite OMM Data (Weather)",
Type = "UrlSynchronization",
Identifier = "satellite_omm_data_weather",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=weather&FORMAT=kvn",
Filename = "weather.txt",
Url = "https://www.celestrak.com/NORAD/elements/gp.php?GROUP=weather&FORMAT=TLE",
TrailColor = { 0.75, 0.75, 0.35 }
Override = true
})
local satellites = {
Identifier = "weather",
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = omm .. "weather.txt",
Format = "OMM",
SegmentQuality = 3,
Color = { 0.75, 0.75, 0.35 },
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent"
},
Tag = { "earth_satellites" },
GUI = {
Name = "Weather",
Path = "/Solar System/Planets/Earth/Satellites"
}
}
local tle = shared.downloadTLEFile(asset, group.Url, group.Title, group.Filename)
shared.registerSatelliteGroupObjects(asset, group, tle, true)
asset.onInitialize(function()
openspace.addSceneGraphNode(satellites)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(satellites)
end)
asset.export(satellites)
asset.meta = {

View File

@@ -1,16 +1,31 @@
local sharedSssb = asset.require("./sssb_shared")
local transforms = asset.require("scene/solarsystem/sun/transforms")
local filepath = sharedSssb.downloadSssbDatabaseFile(asset, "amor_asteroid",
"sssb_data_amor_asteroid")
local object = sharedSssb.createSssbGroupObject("sssb_data_amor_asteroid.csv",
"Amor Asteroids", filepath, { 1.0, 1.0, 1.0 })
object.Renderable.Enabled = false
object.Renderable.SegmentQuality = 4
object.Renderable.TrailFade = 11
object.GUI.Description = [[Earth-approaching Near-Earth-Asteroids with orbits exterior to
Earth's but interior to Mars']]
local sssb = asset.syncedResource({
Name = "Small SolarSystem Body Data (Amor Asteroid)",
Type = "HttpSynchronization",
Identifier = "sssb_data_amor_asteroid",
Version = 1
})
local object = {
Identifier = "sssb_amor_asteroid",
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = sssb .. "sssb_data_amor_asteroid.csv",
Format = "SBDB",
Segments = 200,
SegmentQuality = 4,
Color = { 1.0, 1.0, 1.0 },
TrailFade = 11
},
GUI = {
Name = "Amor Asteroids",
Path = "/Solar System/Small Bodies",
Description = [[Earth-approaching Near-Earth-Asteroids with orbits exterior to
Earth's but interior to Mars'.]]
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(object)

View File

@@ -1,15 +1,31 @@
local sharedSssb = asset.require("./sssb_shared")
local transforms = asset.require("scene/solarsystem/sun/transforms")
local filepath = sharedSssb.downloadSssbDatabaseFile(asset, "apollo_asteroid",
"sssb_data_apollo_asteroid")
local object = sharedSssb.createSssbGroupObject("sssb_data_apollo_asteroid.csv",
"Apollo Asteroids", filepath, { 0.7, 0.7, 1.0 })
object.Renderable.Enabled = false
object.Renderable.SegmentQuality = 6
object.Renderable.TrailFade = 10
local sssb = asset.syncedResource({
Name = "Small SolarSystem Body Data (Apollo Asteroid)",
Type = "HttpSynchronization",
Identifier = "sssb_data_apollo_asteroid",
Version = 1
})
object.GUI.Description = [[Earth-crossing Near-Earth-Asteroids with semi-major axes
larger than Earth's. ]]
local object = {
Identifier = "sssb_apollo_asteroid",
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = sssb .. "sssb_data_apollo_asteroid.csv",
Format = "SBDB",
Segments = 200,
SegmentQuality = 6,
Color = { 0.7, 0.7, 1.0 },
TrailFade = 10
},
GUI = {
Name = "Apollo Asteroids",
Path = "/Solar System/Small Bodies",
Description = [[Earth-crossing Near-Earth-Asteroids with semi-major axes larger than
Earth's.]]
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(object)

View File

@@ -1,16 +1,31 @@
local sharedSssb = asset.require("./sssb_shared")
local transforms = asset.require("scene/solarsystem/sun/transforms")
local filepath = sharedSssb.downloadSssbDatabaseFile(asset, "aten_asteroid",
"sssb_data_aten_asteroid")
local object = sharedSssb.createSssbGroupObject("sssb_data_aten_asteroid.csv",
"Aten Asteroids", filepath, { 0.15, 0.15, 1.0 })
object.Renderable.Enabled = false
object.Renderable.SegmentQuality = 2
object.Renderable.TrailFade = 18
object.GUI.Description = [[ Earth-crossing Near-Earth-Asteroids with semi-major axes
smaller than Earth's. ]]
local sssb = asset.syncedResource({
Name = "Small SolarSystem Body Data (Aten Asteroid)",
Type = "HttpSynchronization",
Identifier = "sssb_data_aten_asteroid",
Version = 1
})
local object = {
Identifier = "sssb_aten_asteroid",
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = sssb .. "sssb_data_aten_asteroid.csv",
Format = "SBDB",
Segments = 200,
SegmentQuality = 2,
Color = { 0.15, 0.15, 1.0 },
TrailFade = 18
},
GUI = {
Name = "Aten Asteroids",
Path = "/Solar System/Small Bodies",
Description = [[Earth-crossing Near-Earth-Asteroids with semi-major axes smaller than
Earth's.]]
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(object)

View File

@@ -1,16 +1,31 @@
local sharedSssb = asset.require("./sssb_shared")
local transforms = asset.require("scene/solarsystem/sun/transforms")
local filepath = sharedSssb.downloadSssbDatabaseFile(asset, "atira_asteroid",
"sssb_data_atira_asteroid")
local object = sharedSssb.createSssbGroupObject("sssb_data_atira_asteroid.csv",
"Atira Asteroids", filepath, { 0.5, 0.8, 1.0 })
object.Renderable.Enabled = false
object.Renderable.SegmentQuality = 2
object.Renderable.TrailFade = 25
object.GUI.Description = [[ Near-Earth-Asteroids whose orbits are contained entirely
within the orbit of the Earth. ]]
local sssb = asset.syncedResource({
Name = "Small SolarSystem Body Data (Atira Asteroid)",
Type = "HttpSynchronization",
Identifier = "sssb_data_atira_asteroid",
Version = 1
})
local object = {
Identifier = "sssb_atira_asteroid",
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = sssb .. "sssb_data_atira_asteroid.csv",
Format = "SBDB",
Segments = 200,
SegmentQuality = 2,
Color = { 0.5, 0.8, 1.0 },
TrailFade = 25
},
GUI = {
Name = "Atira Asteroids",
Path = "/Solar System/Small Bodies",
Description = [[Near-Earth-Asteroids whose orbits are contained entirely within the
orbit of the Earth.]]
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(object)

View File

@@ -68,7 +68,7 @@ asset.export(C2019Y4AtlasTrail)
asset.meta = {
Name = "C/2019 Y4 Atlas",
Version = "1.2",
Description = [[This asset contains the trail and position of C/2019 Y4 Atlas from
Description = [[This asset contains the trail and position of C/2019 Y4 Atlas from
1950 JAN 01 00:00:00 to 2100 JAN 01 00:00:00. Data from JPL Horizons]],
Author = "OpenSpace Team",
URL = "https://ssd.jpl.nasa.gov/horizons.cgi",

View File

@@ -1,16 +1,31 @@
local sharedSssb = asset.require("./sssb_shared")
local transforms = asset.require("scene/solarsystem/sun/transforms")
local filepath = sharedSssb.downloadSssbDatabaseFile(asset, "centaur_asteroid",
"sssb_data_centaur_asteroid")
local object = sharedSssb.createSssbGroupObject("sssb_data_centaur_asteroid.csv",
"Centaur Asteroids", filepath, { 0.94, 0.96, 0.94 })
object.Renderable.Enabled = false
object.Renderable.SegmentQuality = 6
object.Renderable.TrailFade = 18
object.GUI.Description = [[ Asteroids with either a perihelion or a semi-major axis
between those of the four outer planets. ]]
local sssb = asset.syncedResource({
Name = "Small SolarSystem Body Data (Centaur Asteroid)",
Type = "HttpSynchronization",
Identifier = "sssb_data_centaur_asteroid",
Version = 1
})
local object = {
Identifier = "sssb_centaur_asteroid",
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = sssb .. "sssb_data_centaur_asteroid.csv",
Format = "SBDB",
Segments = 200,
SegmentQuality = 6,
Color = { 0.94, 0.96, 0.94 },
TrailFade = 18
},
GUI = {
Name = "Centaur Asteroids",
Path = "/Solar System/Small Bodies",
Description = [[Asteroids with either a perihelion or a semi-major axis between those
of the four outer planets.]]
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(object)

View File

@@ -1,16 +1,31 @@
local sharedSssb = asset.require("./sssb_shared")
local transforms = asset.require("scene/solarsystem/sun/transforms")
local filepath = sharedSssb.downloadSssbDatabaseFile(asset, "chiron-type_comet",
"sssb_data_chiron-type_comet")
local object = sharedSssb.createSssbGroupObject("sssb_data_chiron-type_comet.csv",
"Chiron-type Comets", filepath, { 0.15 ,0.1 ,1.0 })
object.Renderable.Enabled = false
object.Renderable.SegmentQuality = 10
object.Renderable.TrailFade = 25
object.GUI.Description = [[ Comets with a Tisserand's parameter with respect to Jupiter of
greater than 3 and a semi-major axis greater than that of Jupiter. ]]
local sssb = asset.syncedResource({
Name = "Small SolarSystem Body Data (Chiron-type Comet)",
Type = "HttpSynchronization",
Identifier = "sssb_data_chiron-type_comet",
Version = 1
})
local object = {
Identifier = "sssb_chiron-type_comet",
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = sssb .. "sssb_data_chiron-type_comet.csv",
Format = "SBDB",
Segments = 200,
SegmentQuality = 10,
Color = { 0.15, 0.1, 1.0 },
TrailFade = 25
},
GUI = {
Name = "Chiron-type Comets",
Path = "/Solar System/Small Bodies",
Description = [[Comets with a Tisserand's parameter with respect to Jupiter of
greater than 3 and a semi-major axis greater than that of Jupiter.]]
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(object)

View File

@@ -1,16 +1,31 @@
local sharedSssb = asset.require("./sssb_shared")
local transforms = asset.require("scene/solarsystem/sun/transforms")
local filepath = sharedSssb.downloadSssbDatabaseFile(asset, "encke-type_comet",
"sssb_data_encke-type_comet")
local object = sharedSssb.createSssbGroupObject("sssb_data_encke-type_comet.csv",
"Encke-type Comets", filepath, { 0.8, 0.34, 1.0 })
object.Renderable.Enabled = false
object.Renderable.SegmentQuality = 2
object.Renderable.TrailFade = 23
object.GUI.Description = [[ Comets with a Tisserand's parameter with respect to Jupiter of
greater than 3 and a semi-major axis less than that of Jupiter. ]]
local sssb = asset.syncedResource({
Name = "Small SolarSystem Body Data (Encke-type Comet)",
Type = "HttpSynchronization",
Identifier = "sssb_data_encke-type_comet",
Version = 1
})
local object = {
Identifier = "sssb_encke-type_comet",
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = sssb .. "sssb_data_encke-type_comet.csv",
Format = "SBDB",
Segments = 200,
SegmentQuality = 2,
Color = { 0.8, 0.34, 1.0 },
TrailFade = 23
},
GUI = {
Name = "Encke-type Comets",
Path = "/Solar System/Small Bodies",
Description = [[Comets with a Tisserand's parameter with respect to Jupiter of
greater than 3 and a semi-major axis less than that of Jupiter.]]
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(object)

View File

@@ -1,15 +1,30 @@
local sharedSssb = asset.require("./sssb_shared")
local transforms = asset.require("scene/solarsystem/sun/transforms")
local filepath = sharedSssb.downloadSssbDatabaseFile(asset, "halley-type_comet",
"sssb_data_halley-type_comet")
local object = sharedSssb.createSssbGroupObject("sssb_data_halley-type_comet.csv",
"Halley-type Comets", filepath, { 0.66, 0.66, 0.66 })
object.Renderable.Enabled = false
object.Renderable.SegmentQuality = 9
object.Renderable.TrailFade = 18
local sssb = asset.syncedResource({
Name = "Small SolarSystem Body Data (Halley-type Comet)",
Type = "HttpSynchronization",
Identifier = "sssb_data_halley-type_comet",
Version = 1
})
object.GUI.Description = [[ Periodic comets with an orbital period between 20 and 200
years. ]]
local object = {
Identifier = "sssb_halley-type_comet",
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = sssb .. "sssb_data_halley-type_comet.csv",
Format = "SBDB",
Segments = 200,
SegmentQuality = 9,
Color = { 0.66, 0.66, 0.66 },
TrailFade = 18
},
GUI = {
Name = "Halley-type Comets",
Path = "/Solar System/Small Bodies",
Description = "Periodic comets with an orbital period between 20 and 200 years."
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(object)

View File

@@ -1,16 +1,31 @@
local sharedSssb = asset.require("./sssb_shared")
local transforms = asset.require("scene/solarsystem/sun/transforms")
local filepath = sharedSssb.downloadSssbDatabaseFile(asset, "inner_main_belt_asteroid",
"sssb_data_inner_main_belt_asteroid")
local object = sharedSssb.createSssbGroupObject("sssb_data_inner_main_belt_asteroid.csv",
"Inner Main Asteroid Belt", filepath, { 1.0, 1.0, 0.0 })
object.Renderable.Enabled = false
object.Renderable.SegmentQuality = 1
object.Renderable.TrailFade = 0.5
object.GUI.Description = [[ Asteroids with a semi-major axis less than 2.0 au and a
perihelion distance greater than 1.666 au. ]]
local sssb = asset.syncedResource({
Name = "Small SolarSystem Body Data (Inner Main Belt Asteroid)",
Type = "HttpSynchronization",
Identifier = "sssb_data_inner_main_belt_asteroid",
Version = 1
})
local object = {
Identifier = "sssb_inner_main_belt_asteroid",
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = sssb .. "sssb_data_inner_main_belt_asteroid.csv",
Format = "SBDB",
Segments = 200,
SegmentQuality = 1,
Color = { 1.0, 1.0, 0.0 },
TrailFade = 0.5
},
GUI = {
Name = "Inner Main Asteroid Belt",
Path = "/Solar System/Small Bodies",
Description = [[Asteroids with a semi-major axis less than 2.0 AU and a perihelion
distance greater than 1.666 AU.]]
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(object)

View File

@@ -75,7 +75,7 @@ local ItokawaModel = {
Name = "Itokawa Model",
Path = "/Solar System/SSSB/Itokawa",
Description = [[Model of asteroid 25143 Itokawa. 3D model from
https://solarsystem.nasa.gov/resources/2377/asteroid-itokawa-3d-model/]]
https://solarsystem.nasa.gov/resources/2377/asteroid-itokawa-3d-model]]
}
}

View File

@@ -1,16 +1,31 @@
local sharedSssb = asset.require("./sssb_shared")
local transforms = asset.require("scene/solarsystem/sun/transforms")
local filepath = sharedSssb.downloadSssbDatabaseFile(asset, "jupiter-family_comet",
"sssb_data_jupiter-family_comet")
local object = sharedSssb.createSssbGroupObject("sssb_data_jupiter-family_comet.csv",
"Jupiter-family Comets", filepath, { 0.2, 0.8, 0.2 })
object.Renderable.Enabled = false
object.Renderable.SegmentQuality = 10
object.Renderable.TrailFade = 28
object.GUI.Description = [[ Comets with a Tisserand's parameter with respect to Jupiter of
between 2 and 3. ]]
local sssb = asset.syncedResource({
Name = "Small SolarSystem Body Data (Jupiter Family Comet)",
Type = "HttpSynchronization",
Identifier = "sssb_data_jupiter-family_comet",
Version = 1
})
local object = {
Identifier = "sssb_jupiter-family_comet",
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = sssb .. "sssb_data_jupiter-family_comet.csv",
Format = "SBDB",
Segments = 200,
SegmentQuality = 10,
Color = { 0.2, 0.8, 0.2 },
TrailFade = 28
},
GUI = {
Name = "Jupiter-family Comets",
Path = "/Solar System/Small Bodies",
Description = [[Comets with a Tisserand's parameter with respect to Jupiter of between
2 and 3.]]
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(object)

View File

@@ -1,16 +1,31 @@
local sharedSssb = asset.require("./sssb_shared")
local transforms = asset.require("scene/solarsystem/sun/transforms")
local filepath = sharedSssb.downloadSssbDatabaseFile(asset, "jupiter_trojan_asteroid",
"sssb_data_jupiter_trojan_asteroid")
local object = sharedSssb.createSssbGroupObject("sssb_data_jupiter_trojan_asteroid.csv",
"Jupiter Trojan Asteroids", filepath, { 0.5, 0.8, 0.5 })
object.Renderable.Enabled = false
object.Renderable.SegmentQuality = 1
object.Renderable.TrailFade = 5
object.GUI.Description = [[ Asteroids trapped in Jupiter's L4/L5 Lagrange points
(semimajor axis of between 4.6 and 5.5 au), with an eccentricity of less than 0.3. ]]
local sssb = asset.syncedResource({
Name = "Small SolarSystem Body Data (Jupiter Trojan Asteroid)",
Type = "HttpSynchronization",
Identifier = "sssb_data_jupiter_trojan_asteroid",
Version = 1
})
local object = {
Identifier = "sssb_jupiter_trojan_asteroid",
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = sssb .. "sssb_data_jupiter_trojan_asteroid.csv",
Format = "SBDB",
Segments = 200,
SegmentQuality = 1,
Color = { 0.5, 0.8, 0.5 },
TrailFade = 5
},
GUI = {
Name = "Jupiter Trojan Asteroids",
Path = "/Solar System/Small Bodies",
Description = [[Asteroids trapped in Jupiter's L4/L5 Lagrange points (semimajor axis
of between 4.6 and 5.5 AU), with an eccentricity of less than 0.3.]]
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(object)

View File

@@ -1,17 +1,32 @@
local sharedSssb = asset.require("./sssb_shared")
local transforms = asset.require("scene/solarsystem/sun/transforms")
local filepath = sharedSssb.downloadSssbDatabaseFile(asset, "main_belt_asteroid",
"sssb_data_main_belt_asteroid")
local object = sharedSssb.createSssbGroupObject("sssb_data_main_belt_asteroid.csv",
"Main Asteroid Belt", filepath, { 0.0, 0.5, 0.0 })
object.Renderable.Enabled = false
object.Renderable.SegmentQuality = 1
object.Renderable.TrailFade = 0.1
object.Renderable.UpperLimit = 50000
object.GUI.Description = [[ Asteroids with a semi-major axis of between 2.0 and 3.2 au,
and a perihelion distance greater than 1.666 au. ]]
local sssb = asset.syncedResource({
Name = "Small SolarSystem Body Data (Main Belt Asteroid)",
Type = "HttpSynchronization",
Identifier = "sssb_data_main_belt_asteroid",
Version = 1
})
local object = {
Identifier = "sssb_main_belt_asteroid",
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = sssb .. "sssb_data_main_belt_asteroid.csv",
Format = "SBDB",
Segments = 200,
SegmentQuality = 1,
Color = { 0.0, 0.5, 0.0 },
TrailFade = 0.1,
RenderSize = 50000
},
GUI = {
Name = "Main Asteroid Belt",
Path = "/Solar System/Small Bodies",
Description = [[Asteroids with a semi-major axis of between 2.0 and 3.2 AU,
and a perihelion distance greater than 1.666 AU.]]
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(object)

View File

@@ -1,16 +1,31 @@
local sharedSssb = asset.require("./sssb_shared")
local transforms = asset.require("scene/solarsystem/sun/transforms")
local filepath = sharedSssb.downloadSssbDatabaseFile(asset, "mars-crossing_asteroid",
"sssb_data_mars-crossing_asteroid")
local object = sharedSssb.createSssbGroupObject("sssb_data_mars-crossing_asteroid.csv",
"Mars-crossing Asteroids", filepath, { 0.814, 0.305, 0.220 })
object.Renderable.Enabled = false
object.Renderable.SegmentQuality = 1
object.Renderable.TrailFade = 13
object.GUI.Description = [[ Asteroids that cross the orbit of Mars, with a semi-major axis
of less than 3.2 au, and a perihelion distance of between 1.3 and 1.666 au. ]]
local sssb = asset.syncedResource({
Name = "Small SolarSystem Body Data (Mars-Crossing Asteroid)",
Type = "HttpSynchronization",
Identifier = "sssb_data_mars-crossing_asteroid",
Version = 1
})
local object = {
Identifier = "sssb_mars-crossing_asteroid",
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = sssb .. "sssb_data_mars-crossing_asteroid.csv",
Format = "SBDB",
Segments = 200,
SegmentQuality = 1,
Color = { 0.814, 0.305, 0.22 },
TrailFade = 13
},
GUI = {
Name = "Mars-crossing Asteroids",
Path = "/Solar System/Small Bodies",
Description = [[Asteroids that cross the orbit of Mars, with a semi-major axis
of less than 3.2 AU, and a perihelion distance of between 1.3 and 1.666 AU.]]
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(object)

View File

@@ -1,21 +1,30 @@
local sharedSssb = asset.require("./sssb_shared")
local transforms = asset.require("scene/solarsystem/sun/transforms")
local filepath = sharedSssb.downloadSssbDatabaseFile(
asset,
"outer_main_belt_asteroid",
"sssb_data_outer_main_belt_asteroid"
)
local object = sharedSssb.createSssbGroupObject(
"sssb_data_outer_main_belt_asteroid.csv",
"Outer Main Asteroid Belt",
filepath,
{ 0.4, 0.4, 1.0 }
)
object.Renderable.Enabled = false
object.Renderable.SegmentQuality = 1
object.Renderable.TrailFade = 2
local sssb = asset.syncedResource({
Name = "Small SolarSystem Body Data (Outer Main Belt Asteroid)",
Type = "HttpSynchronization",
Identifier = "sssb_data_outer_main_belt_asteroid",
Version = 1
})
object.GUI.Description = [[ Asteroids with a semi-major axis of between 3.2 and 4.6 au. ]]
local object = {
Identifier = "sssb_outer_main_belt_asteroid",
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = sssb .. "sssb_data_outer_main_belt_asteroid.csv",
Format = "SBDB",
Segments = 200,
SegmentQuality = 1,
Color = { 0.4, 0.4, 1.0 },
TrailFade = 2
},
GUI = {
Name = "Outer Main Asteroid Belt",
Path = "/Solar System/Small Bodies",
Description = [[Asteroids with a semi-major axis of between 3.2 and 4.6 AU.]]
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(object)

View File

@@ -1,20 +1,33 @@
local sharedSssb = asset.require("./sssb_shared")
local transforms = asset.require("scene/solarsystem/sun/transforms")
local filepath = sharedSssb.downloadSssbDatabaseFile(asset, "pha", "sssb_data_pha")
local object = sharedSssb.createSssbGroupObject("sssb_data_pha.csv",
"Potentially Hazardous Asteroids",
filepath,
{ 0.98, 0.09, 0.06 }
)
object.Renderable.Enabled = false
object.Renderable.SegmentQuality = 3
object.Renderable.TrailFade = 17
object.GUI.Description = [[ Asteroids that are deemed potentially hazardous to Earth
based on their close approaches. All asteroids with an Earth Minimum Orbit
Intersection Distance (MOID) of 0.05 au or less, and with an absolute magnitude (H)
of 22.0 or less. ]]
local sssb = asset.syncedResource({
Name = "Small SolarSystem Body Data (Potentially hazardous Asteroids)",
Type = "HttpSynchronization",
Identifier = "sssb_data_pha",
Version = 1
})
local object = {
Identifier = "sssb_pha",
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = sssb .. "sssb_data_pha.csv",
Format = "SBDB",
Segments = 200,
SegmentQuality = 3,
Color = { 0.98, 0.09, 0.06 },
TrailFade = 17
},
GUI = {
Name = "Potentially Hazardous Asteroids",
Path = "/Solar System/Small Bodies",
Description = [[Asteroids that are deemed potentially hazardous to Earth based on
their close approaches. All asteroids with an Earth Minimum Orbit Intersection
Distance (MOID) of 0.05 AU or less, and with an absolute magnitude (H) of 22.0 or
less.]]
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(object)

View File

@@ -1,50 +0,0 @@
local transforms = asset.require("scene/solarsystem/sun/transforms")
function downloadSssbDatabaseFile(sceneAsset, name, identifier)
assert(sceneAsset, "'asset' needs to be provided")
assert(name, "'name' needs to be provided")
assert(identifier, "'identifier'needs to be provided")
return sceneAsset.syncedResource({
Name = "Small SolarSystem Body Data (" .. name .. ")",
Type = "HttpSynchronization",
Identifier = identifier,
Version = 1
})
end
local createSssbGroupObject = function(filename, guiName, sssbFolder, trailColor)
assert(filename, "'filename' needs to be provided")
assert(sssbFolder, "'sssbFolder' needs to be provided")
trailColor = trailColor or { 0.75, 0.1, 0.1 }
local filenameSansExt = filename:gsub(filename:match("(%.%w+)$"), "")
return {
Identifier = "sssb_" .. filenameSansExt,
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableSmallBody",
Path = sssbFolder .. filename,
Segments = 200,
Color = trailColor,
TrailFade = 0.5,
},
GUI = {
Path = "/Solar System/Small Bodies",
Name = guiName
}
}
end
asset.export("downloadSssbDatabaseFile", downloadSssbDatabaseFile)
asset.export("createSssbGroupObject", createSssbGroupObject)
asset.meta = {
Name = "SSSB shared",
Version = "1.0",
Description = [[ UTILITY ASSET; This asset is for builders and coders]],
Author = "OpenSpace Team",
URL = "http://openspaceproject.com",
License = "MIT license"
}

View File

@@ -1,22 +1,31 @@
local sharedSssb = asset.require("./sssb_shared")
local transforms = asset.require("scene/solarsystem/sun/transforms")
local filepath = sharedSssb.downloadSssbDatabaseFile(asset,
"transneptunian_object_asteroid",
"sssb_data_transneptunian_object_asteroid"
)
local object = sharedSssb.createSssbGroupObject(
"sssb_data_transneptunian_object_asteroid.csv",
"Transneptunian Object Asteroids",
filepath,
{ 0.56, 0.64, 0.95 }
)
object.Renderable.Enabled = false
object.Renderable.SegmentQuality = 8
object.Renderable.TrailFade = 10
object.GUI.Description = [[ Any minor or dwarf planets in the solar system that orbit
the Sun at a greater average distance than Neptune (semi-major axis of 30.1 AU). ]]
local sssb = asset.syncedResource({
Name = "Small SolarSystem Body Data (Trans-Neptunian Object Asteroid)",
Type = "HttpSynchronization",
Identifier = "sssb_data_transneptunian_object_asteroid",
Version = 1
})
local object = {
Identifier = "sssb_transneptunian_object_asteroid",
Parent = transforms.SunECLIPJ2000.Identifier,
Renderable = {
Type = "RenderableOrbitalKepler",
Path = sssb .. "sssb_data_transneptunian_object_asteroid.csv",
Format = "SBDB",
Segments = 200,
SegmentQuality = 8,
Color = { 0.56, 0.64, 0.95 },
TrailFade = 10
},
GUI = {
Name = "Transneptunian Object Asteroids",
Path = "/Solar System/Small Bodies",
Description = [[Any minor or dwarf planets in the solar system that orbit the Sun at a
greater average distance than Neptune (semi-major axis of 30.1 AU).]]
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(object)

View File

@@ -1,124 +0,0 @@
local transforms = asset.require("scene/solarsystem/planets/earth/transforms")
function downloadTLEFile(sceneAsset, url, name, filename)
local identifier = name
identifier = identifier:gsub(" ", "")
identifier = identifier:gsub("&", "")
identifier = identifier:gsub("-", "")
local urlSyncTable = {
Name = "Satellite TLE Data (" .. name .. ")",
Type = "UrlSynchronization",
Identifier = "satellite_tle_data_" .. identifier,
Url = url,
Override = true
}
if (filename ~= "") then
urlSyncTable.Filename = filename
end
return sceneAsset.syncedResource(urlSyncTable)
end
-- Check format of a set of 3 TLE file lines and return nonzero if there is a format error
function isValidTLEFileFormat(lineArr)
local function isEmpty(s) return s == nil or s == "" end
if isEmpty(lineArr[1]) or isEmpty(lineArr[2]) or isEmpty(lineArr[3]) then
return false
end
if string.sub(lineArr[2], 1, 2) ~= "1 " then
return false
end
if string.sub(lineArr[3], 1, 2) ~= "2 " then
return false
end
return true
end
local makeSingleLineElement = function(tle, filename)
local path = tle .. "/" .. filename
local file = io.open(path, "r")
assert(file, "File not found: " .. path)
local line = {
file:read("*l"), --title line
file:read("*l"),
file:read("*l")
}
assert(isValidTLEFileFormat(line), "TLE file syntax error on line " .. 1 .. ": " .. path)
-- Trim string
line[1] = line[1]:gsub("^%s*(.-)%s*$", "%1")
line[1] = line[1]:gsub("%s+", "_")
line[1] = line[1]:gsub("[%-()]", "")
--local title = line[1]
return line
end
function numLinesInFile(filename)
local ctr = 0
for _ in io.lines(filename) do ctr = ctr + 1 end
return ctr
end
local getPeriodFromElement = function(element)
-- Get period from correct location of the string
local per = tonumber(string.sub(element[3], 53, 63))
-- Trail for 2x a single revolution
per = 1.0 / per
return per
end
function satellites(title, file, color, group)
return {
Identifier = title,
Parent = transforms.EarthInertial.Identifier,
Renderable = {
Type = "RenderableSatellites",
Path = file,
SegmentQuality = 3,
Color = color,
Fade = 1.5,
RenderBinMode = "PostDeferredTransparent",
StartRenderIdx = group.StartRenderIdx,
RenderSize = group.RenderSize
},
Tag = { "earth_satellites" },
GUI = {
Name = group.Title,
Path = "/Solar System/Planets/Earth/Satellites",
Description = group.Description
}
}
end
local registerSatelliteGroupObjects = function(containingAsset, group, tleFolder, shouldAddDuplicates)
local filename = group.Filename
local filenameSansExt = filename:gsub(filename:match("(%.%w+)$"), "")
local path = tleFolder .. "/" .. filename
local SatelliteBatch = satellites(filenameSansExt, path, group.TrailColor, group)
containingAsset.onInitialize(function()
openspace.addSceneGraphNode(SatelliteBatch)
end)
containingAsset.onDeinitialize(function()
openspace.removeSceneGraphNode(SatelliteBatch)
end)
containingAsset.export(SatelliteBatch)
end
asset.export("downloadTLEFile", downloadTLEFile)
asset.export("isValidTLEFileFormat", isValidTLEFileFormat)
asset.export("numLinesInFile", numLinesInFile)
asset.export("makeSingleLineElement", makeSingleLineElement)
asset.export("getPeriodFromElement", getPeriodFromElement)
asset.export("registerSatelliteGroupObjects", registerSatelliteGroupObjects)

View File

@@ -26,19 +26,18 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
set(HEADER_FILES
horizonsfile.h
kepler.h
speckloader.h
rendering/renderableconstellationbounds.h
rendering/renderablefluxnodes.h
rendering/renderablehabitablezone.h
rendering/renderablerings.h
rendering/renderableorbitalkepler.h
rendering/renderablesatellites.h
rendering/renderablesmallbody.h
rendering/renderablestars.h
rendering/renderabletravelspeed.h
translation/gptranslation.h
translation/keplertranslation.h
translation/spicetranslation.h
translation/tletranslation.h
translation/horizonstranslation.h
rotation/spicerotation.h
)
@@ -46,6 +45,7 @@ source_group("Header Files" FILES ${HEADER_FILES})
set(SOURCE_FILES
horizonsfile.cpp
kepler.cpp
spacemodule_lua.inl
speckloader.cpp
rendering/renderableconstellationbounds.cpp
@@ -53,13 +53,11 @@ set(SOURCE_FILES
rendering/renderablehabitablezone.cpp
rendering/renderablerings.cpp
rendering/renderableorbitalkepler.cpp
rendering/renderablesatellites.cpp
rendering/renderablesmallbody.cpp
rendering/renderablestars.cpp
rendering/renderabletravelspeed.cpp
translation/gptranslation.cpp
translation/keplertranslation.cpp
translation/spicetranslation.cpp
translation/tletranslation.cpp
translation/horizonstranslation.cpp
rotation/spicerotation.cpp
)

751
modules/space/kepler.cpp Normal file
View File

@@ -0,0 +1,751 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2022 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/space/kepler.h>
#include <ghoul/filesystem/cachemanager.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/misc.h>
#include <scn/scn.h>
#include <scn/tuple_return.h>
#include <fstream>
#include <optional>
#include <sstream>
namespace {
constexpr std::string_view _loggerCat = "Kepler";
constexpr int8_t CurrentCacheVersion = 1;
// The list of leap years only goes until 2056 as we need to touch this file then
// again anyway ;)
constexpr const std::array<int, 36> LeapYears = {
1956, 1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992, 1996,
2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036, 2040,
2044, 2048, 2052, 2056
};
constexpr const std::array<int, 12> DaysOfMonths = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
// Count the number of full days since the beginning of 2000 to the beginning of
// the parameter 'year'
int countDays(int year) {
// Find the position of the current year in the vector, the difference
// between its position and the position of 2000 (for J2000) gives the
// number of leap years
constexpr const int Epoch = 2000;
constexpr const int DaysRegularYear = 365;
constexpr const int DaysLeapYear = 366;
if (year == Epoch) {
return 0;
}
// Get the position of the most recent leap year
const auto lb = std::lower_bound(LeapYears.begin(), LeapYears.end(), year);
// Get the position of the epoch
const auto y2000 = std::find(LeapYears.begin(), LeapYears.end(), Epoch);
// The distance between the two iterators gives us the number of leap years
const int nLeapYears = static_cast<int>(std::abs(std::distance(y2000, lb)));
const int nYears = std::abs(year - Epoch);
const int nRegularYears = nYears - nLeapYears;
// Get the total number of days as the sum of leap years + non leap years
const int result = nRegularYears * DaysRegularYear + nLeapYears * DaysLeapYear;
return result;
}
// Returns the number of leap seconds that lie between the {year, dayOfYear}
// time point and { 2000, 1 }
int countLeapSeconds(int year, int dayOfYear) {
// Find the position of the current year in the vector; its position in the vector
// gives the number of leap seconds
struct LeapSecond {
int year;
int dayOfYear;
bool operator<(const LeapSecond& rhs) const {
return std::tie(year, dayOfYear) < std::tie(rhs.year, rhs.dayOfYear);
}
};
constexpr const LeapSecond LeapEpoch = { 2000, 1 };
// List taken from: https://www.ietf.org/timezones/data/leap-seconds.list
constexpr const std::array<LeapSecond, 28> LeapSeconds = {
LeapSecond{ 1972, 1 },
LeapSecond{ 1972, 183 },
LeapSecond{ 1973, 1 },
LeapSecond{ 1974, 1 },
LeapSecond{ 1975, 1 },
LeapSecond{ 1976, 1 },
LeapSecond{ 1977, 1 },
LeapSecond{ 1978, 1 },
LeapSecond{ 1979, 1 },
LeapSecond{ 1980, 1 },
LeapSecond{ 1981, 182 },
LeapSecond{ 1982, 182 },
LeapSecond{ 1983, 182 },
LeapSecond{ 1985, 182 },
LeapSecond{ 1988, 1 },
LeapSecond{ 1990, 1 },
LeapSecond{ 1991, 1 },
LeapSecond{ 1992, 183 },
LeapSecond{ 1993, 182 },
LeapSecond{ 1994, 182 },
LeapSecond{ 1996, 1 },
LeapSecond{ 1997, 182 },
LeapSecond{ 1999, 1 },
LeapSecond{ 2006, 1 },
LeapSecond{ 2009, 1 },
LeapSecond{ 2012, 183 },
LeapSecond{ 2015, 182 },
LeapSecond{ 2017, 1 }
};
// Get the position of the last leap second before the desired date
LeapSecond date{ year, dayOfYear };
const auto it = std::lower_bound(LeapSeconds.begin(), LeapSeconds.end(), date);
// Get the position of the Epoch
const auto y2000 = std::lower_bound(
LeapSeconds.begin(),
LeapSeconds.end(),
LeapEpoch
);
// The distance between the two iterators gives us the number of leap years
const int nLeapSeconds = static_cast<int>(std::abs(std::distance(y2000, it)));
return nLeapSeconds;
}
int daysIntoGivenYear(int year, int month, int dayOfMonth) {
// month and dayCount are zero-based.
month -= 1;
int dayCount = dayOfMonth - 1;
constexpr int February = 1;
const bool isInLeapYear =
std::find(LeapYears.begin(), LeapYears.end(), year) != LeapYears.end();
for (int m = 0; m < month; ++m) {
dayCount += DaysOfMonths[m];
if (m == February && isInLeapYear) {
dayCount += 1;
}
}
return dayCount;
}
double calculateSemiMajorAxis(double meanMotion) {
constexpr const double GravitationalConstant = 6.6740831e-11;
constexpr const double MassEarth = 5.9721986e24;
constexpr const double muEarth = GravitationalConstant * MassEarth;
// Use Kepler's 3rd law to calculate semimajor axis
// a^3 / P^2 = mu / (2pi)^2
// <=> a = ((mu * P^2) / (2pi^2))^(1/3)
// with a = semimajor axis
// P = period in seconds
// mu = G*M_earth
const double period =
std::chrono::seconds(std::chrono::hours(24)).count() / meanMotion;
constexpr const double pisq = glm::pi<double>() * glm::pi<double>();
const double semiMajorAxis = pow(
(muEarth * period * period) / (4 * pisq),
1.0 / 3.0
);
// We need the semi major axis in km instead of m
return semiMajorAxis / 1000.0;
}
double epochFromSubstring(const std::string& epoch) {
// The epochString is in the form:
// YYDDD.DDDDDDDD
// With YY being the last two years of the launch epoch, the first DDD the day of
// the year and the remaning a fractional part of the day
// The main overview of this function:
// 1. Reconstruct the full year from the YY part
// 2. Calculate the number of days since the beginning of the year
// 3. Convert the number of days to a number of seconds
// 4. Get the number of leap seconds since January 1st, 2000 and remove them
// 5. Adjust for the fact the epoch starts on 1st Januaray at 12:00:00, not
// midnight
// According to https://celestrak.com/columns/v04n03/
// Apparently, US Space Command sees no need to change the two-line element set
// format yet since no artificial earth satellites existed prior to 1957. By their
// reasoning, two-digit years from 57-99 correspond to 1957-1999 and those from
// 00-56 correspond to 2000-2056. We'll see each other again in 2057!
// 1,2. Get the full year and days
auto [res, year, daysInYear] = scn::scan_tuple<int, double>(epoch, "{:2}{}");
if (!res) {
throw ghoul::RuntimeError(fmt::format("Error parsing epoch '{}'", epoch));
}
year += year > 57 ? 1900 : 2000;
const int daysSince2000 = countDays(year);
// 3
using namespace std::chrono;
const int SecondsPerDay = static_cast<int>(seconds(hours(24)).count());
//Need to subtract 1 from daysInYear since it is not a zero-based count
const double nSecondsSince2000 = (daysSince2000 + daysInYear - 1) * SecondsPerDay;
// 4
// We need to remove additional leap seconds past 2000 and add them prior to
// 2000 to sync up the time zones
const double nLeapSecondsOffset = countLeapSeconds(
year,
static_cast<int>(std::floor(daysInYear))
);
// 5
const double nSecondsEpochOffset =
static_cast<double>(seconds(hours(12)).count());
// Combine all of the values
return nSecondsSince2000 - nLeapSecondsOffset - nSecondsEpochOffset;
}
double epochFromYMDdSubstring(const std::string& epoch) {
// The epochString is in the form:
// YYYYMMDD.ddddddd
// With YYYY as the year, MM the month (1 - 12), DD the day of month (1-31),
// and dddd the fraction of that day.
// The main overview of this function:
// 1. Read the year value
// 2. Calculate the number of days since the beginning of the year
// 3. Convert the number of days to a number of seconds
// 4. Get the number of leap seconds since January 1st, 2000 and remove them
// 5. Adjust for the fact the epoch starts on 1st January at 12:00:00, not
// midnight
// 1, 2
auto [res, year, monthNum, dayOfMonthNum, fractionOfDay] =
scn::scan_tuple<int, int, int, double>(epoch, "{:4}{:2}{:2}{}");
if (!res) {
throw ghoul::RuntimeError(fmt::format("Error parsing epoch '{}'", epoch));
}
const int daysSince2000 = countDays(year);
int wholeDaysInto = daysIntoGivenYear(year, monthNum, dayOfMonthNum);
double daysInYear = static_cast<double>(wholeDaysInto) + fractionOfDay;
// 3
using namespace std::chrono;
const int SecondsPerDay = static_cast<int>(seconds(hours(24)).count());
//Need to subtract 1 from daysInYear since it is not a zero-based count
const double nSecondsSince2000 = (daysSince2000 + daysInYear - 1) * SecondsPerDay;
// 4
// We need to remove additional leap seconds past 2000 and add them prior to
// 2000 to sync up the time zones
const double nLeapSecondsOffset = -countLeapSeconds(
year,
static_cast<int>(std::floor(daysInYear))
);
// 5
const double offset = static_cast<double>(seconds(hours(12)).count());
// Combine all of the values
return nSecondsSince2000 + nLeapSecondsOffset - offset;
}
double epochFromOmmString(const std::string& epoch) {
// The epochString is in the form:
// YYYY-MM-DDThh:mm:ss[.d->d][Z]
// or
// YYYY-DDDThh:mm:ss[.d->d][Z]
// The main overview of this function:
// 0. Determine which type it is
// 1. Read the year value
// 2. Calculate the number of days since the beginning of the year
// 3. Convert the number of days to a number of seconds
// 4. Get the number of leap seconds since January 1st, 2000 and remove them
// 5. Add the hh:mm:ss component
// 6. Adjust for the fact the epoch starts on 1st January at 12:00:00, not
// midnight
std::string e = epoch;
if (e.back() == 'Z') {
e.pop_back();
}
struct {
int year;
int nDays;
int hours;
int minutes;
double seconds;
} date;
// 1, 2
const size_t pos = epoch.find('T');
if (pos == 10) {
// We have the first form
int month;
int days;
auto res = scn::scan(
epoch, "{:4}-{:2}-{:2}T{:2}:{:2}:{}",
date.year, month, days, date.hours, date.minutes, date.seconds
);
if (!res) {
throw ghoul::RuntimeError(fmt::format("Error parsing epoch '{}'", epoch));
}
date.nDays = daysIntoGivenYear(date.year, month, days);
}
else if (pos == 8) {
// We have the second form
auto res = scn::scan(
epoch, "{:4}-{:3}T{:2}:{:2}:{}",
date.year, date.nDays, date.hours, date.minutes, date.seconds
);
if (!res) {
throw ghoul::RuntimeError(fmt::format("Error parsing epoch '{}'", epoch));
}
}
else {
throw ghoul::RuntimeError(fmt::format("Malformed epoch string '{}'", epoch));
}
const int daysSince2000 = countDays(date.year);
// 3
using namespace std::chrono;
const int SecondsPerDay = static_cast<int>(seconds(hours(24)).count());
const double nSecondsSince2000 = (daysSince2000 + date.nDays) * SecondsPerDay;
// 4
// We need to remove additional leap seconds past 2000 and add them prior to
// 2000 to sync up the time zones
const double nLeapSecondsOffset = -countLeapSeconds(
date.year,
static_cast<int>(std::floor(date.nDays))
);
// 5
const long long totalSeconds =
std::chrono::seconds(std::chrono::hours(date.hours)).count() +
std::chrono::seconds(std::chrono::minutes(date.minutes)).count();
// 6
const long long offset = std::chrono::seconds(std::chrono::hours(12)).count();
// Combine all of the values
return
nSecondsSince2000 + totalSeconds + nLeapSecondsOffset - offset + date.seconds;
}
} // namespace
namespace openspace::kepler {
std::vector<Parameters> readTleFile(std::filesystem::path file) {
ghoul_assert(std::filesystem::is_regular_file(file), "File must exist");
std::vector<Parameters> result;
std::ifstream f;
f.open(file);
int lineNum = 1;
std::string header;
while (std::getline(f, header)) {
Parameters p;
// Header
p.name = header;
// First line
// Field Columns Content
// 1 01-01 Line number
// 2 03-07 Satellite number
// 3 08-08 Classification (U = Unclassified)
// 4 10-11 International Designator (Last two digits of launch year)
// 5 12-14 International Designator (Launch number of the year)
// 6 15-17 International Designator(piece of the launch) A
// 7 19-20 Epoch Year(last two digits of year)
// 8 21-32 Epoch(day of the year and fractional portion of the day)
// 9 34-43 First Time Derivative of the Mean Motion divided by two
// 10 45-52 Second Time Derivative of Mean Motion divided by six
// 11 54-61 BSTAR drag term(decimal point assumed)[10] - 11606 - 4
// 12 63-63 The "Ephemeris type"
// 13 65-68 Element set number.Incremented when a new TLE is generated
// 14 69-69 Checksum (modulo 10)
std::string firstLine;
std::getline(f, firstLine);
if (f.bad() || firstLine[0] != '1') {
throw ghoul::RuntimeError(fmt::format(
"Malformed TLE file '{}' at line {}", file, lineNum + 1
));
}
// The id only contains the last two digits of the launch year, so we have to
// patch it to the full year
{
std::string id = firstLine.substr(9, 6);
std::string prefix = [y = id.substr(0, 2)](){
int year = std::atoi(y.c_str());
return year >= 57 ? "19" : "20";
}();
p.id = fmt::format("{}{}-{}", prefix, id.substr(0, 2), id.substr(3));
}
p.epoch = epochFromSubstring(firstLine.substr(18, 14)); // should be 13?
// Second line
// Field Columns Content
// 1 01-01 Line number
// 2 03-07 Satellite number
// 3 09-16 Inclination (degrees)
// 4 18-25 Right ascension of the ascending node (degrees)
// 5 27-33 Eccentricity (decimal point assumed)
// 6 35-42 Argument of perigee (degrees)
// 7 44-51 Mean Anomaly (degrees)
// 8 53-63 Mean Motion (revolutions per day)
// 9 64-68 Revolution number at epoch (revolutions)
// 10 69-69 Checksum (modulo 10)
std::string secondLine;
std::getline(f, secondLine);
if (f.bad() || secondLine[0] != '2') {
throw ghoul::RuntimeError(fmt::format(
"Malformed TLE file '{}' at line {}", file, lineNum + 1
));
}
std::stringstream stream;
stream.exceptions(std::ios::failbit);
// Get inclination
stream.str(secondLine.substr(8, 8));
stream >> p.inclination;
stream.clear();
// Get Right ascension of the ascending node
stream.str(secondLine.substr(17, 8));
stream >> p.ascendingNode;
stream.clear();
// Get Eccentricity
stream.str("0." + secondLine.substr(26, 7));
stream >> p.eccentricity;
stream.clear();
// Get argument of periapsis
stream.str(secondLine.substr(34, 8));
stream >> p.argumentOfPeriapsis;
stream.clear();
// Get mean anomaly
stream.str(secondLine.substr(43, 8));
stream >> p.meanAnomaly;
stream.clear();
// Get mean motion
stream.str(secondLine.substr(52, 11));
float meanMotion;
stream >> meanMotion;
p.semiMajorAxis = calculateSemiMajorAxis(meanMotion);
p.period = std::chrono::seconds(std::chrono::hours(24)).count() / meanMotion;
result.push_back(p);
lineNum = lineNum + 3;
}
return result;
}
std::vector<Parameters> readOmmFile(std::filesystem::path file) {
ghoul_assert(std::filesystem::is_regular_file(file), "File must exist");
std::vector<Parameters> result;
std::ifstream f;
f.open(file);
int lineNum = 1;
std::optional<Parameters> current = std::nullopt;
std::string line;
while (std::getline(f, line)) {
if (line.empty()) {
continue;
}
// Tokenize the line
std::vector<std::string> parts = ghoul::tokenizeString(line, '=');
for (std::string& p : parts) {
ghoul::trimWhitespace(p);
}
if (parts.size() != 2) {
throw ghoul::RuntimeError(fmt::format(
"Malformed line '{}' at {}", line, lineNum
));
}
if (parts[0] == "CCSDS_OMM_VERS") {
if (parts[1] != "2.0") {
LWARNINGC(
"OMM",
fmt::format(
"Only version 2.0 is currently supported but found {}. "
"Parsing might fail",
parts[1]
)
);
}
// We start a new value so we need to store the last one...
if (current.has_value()) {
result.push_back(*current);
}
// ... and start a new one
current = Parameters();
}
ghoul_assert(current.has_value(), "No current element");
if (parts[0] == "OBJECT_NAME") {
current->name = parts[1];
}
else if (parts[0] == "OBJECT_ID") {
current->id = parts[1];
}
else if (parts[0] == "EPOCH") {
current->epoch = epochFromOmmString(parts[1]);
}
else if (parts[0] == "MEAN_MOTION") {
float mm = std::stof(parts[1]);
current->semiMajorAxis = calculateSemiMajorAxis(mm);
current->period = std::chrono::seconds(std::chrono::hours(24)).count() / mm;
}
else if (parts[0] == "SEMI_MAJOR_AXIS") {
}
else if (parts[0] == "ECCENTRICITY") {
current->eccentricity = std::stof(parts[1]);
}
else if (parts[0] == "INCLINATION") {
current->inclination = std::stof(parts[1]);
}
else if (parts[0] == "RA_OF_ASC_NODE") {
current->ascendingNode = std::stof(parts[1]);
}
else if (parts[0] == "ARG_OF_PERICENTER") {
current->argumentOfPeriapsis = std::stof(parts[1]);
}
else if (parts[0] == "MEAN_ANOMALY") {
current->meanAnomaly = std::stof(parts[1]);
}
}
if (current.has_value()) {
result.push_back(*current);
}
return result;
}
std::vector<Parameters> readSbdbFile(std::filesystem::path file) {
constexpr int NDataFields = 9;
constexpr std::string_view ExpectedHeader = "full_name,epoch_cal,e,a,i,om,w,ma,per";
ghoul_assert(std::filesystem::is_regular_file(file), "File must exist");
std::ifstream f;
f.open(file);
std::string line;
std::getline(f, line);
if (line != ExpectedHeader) {
throw ghoul::RuntimeError(fmt::format(
"Expected JPL SBDB file to start with '{}' but found '{}' instead",
ExpectedHeader, line
));
}
std::vector<Parameters> result;
while (std::getline(f, line)) {
constexpr double AuToKm = 1.496e8;
std::vector<std::string> parts = ghoul::tokenizeString(line, ',');
if (parts.size() != NDataFields) {
throw ghoul::RuntimeError(fmt::format(
"Malformed line {}, expected 8 data fields, got {}", line, parts.size()
));
}
Parameters p;
ghoul::trimWhitespace(parts[0]);
p.name = parts[0];
p.epoch = epochFromYMDdSubstring(parts[1]);
p.eccentricity = std::stod(parts[2]);
p.semiMajorAxis = std::stod(parts[3]) * AuToKm;
auto importAngleValue = [](const std::string& angle) {
if (angle.empty()) {
return 0.0;
}
double output = std::stod(angle);
output = std::fmod(output, 360.0);
if (output < 0.0) {
output += 360.0;
}
return output;
};
p.inclination = importAngleValue(parts[4]);
p.ascendingNode = importAngleValue(parts[5]);
p.argumentOfPeriapsis = importAngleValue(parts[6]);
p.meanAnomaly = importAngleValue(parts[7]);
p.period =
std::stod(parts[8]) * std::chrono::seconds(std::chrono::hours(24)).count();
result.push_back(std::move(p));
}
return result;
}
void saveCache(const std::vector<Parameters>& params, std::filesystem::path file) {
std::ofstream stream(file, std::ofstream::binary);
stream.write(reinterpret_cast<const char*>(&CurrentCacheVersion), sizeof(int8_t));
uint32_t size = static_cast<uint32_t>(params.size());
stream.write(reinterpret_cast<const char*>(&size), sizeof(uint32_t));
for (const Parameters& param : params) {
uint32_t nameLength = static_cast<uint32_t>(param.name.size());
stream.write(reinterpret_cast<const char*>(&nameLength), sizeof(uint32_t));
stream.write(param.name.data(), nameLength * sizeof(char));
uint32_t idLength = static_cast<uint32_t>(param.id.size());
stream.write(reinterpret_cast<const char*>(&idLength), sizeof(uint32_t));
stream.write(param.id.data(), idLength * sizeof(char));
stream.write(reinterpret_cast<const char*>(&param.inclination), sizeof(double));
stream.write(reinterpret_cast<const char*>(&param.semiMajorAxis), sizeof(double));
stream.write(reinterpret_cast<const char*>(&param.ascendingNode), sizeof(double));
stream.write(reinterpret_cast<const char*>(&param.eccentricity), sizeof(double));
stream.write(
reinterpret_cast<const char*>(&param.argumentOfPeriapsis),
sizeof(double)
);
stream.write(reinterpret_cast<const char*>(&param.meanAnomaly), sizeof(double));
stream.write(reinterpret_cast<const char*>(&param.epoch), sizeof(double));
stream.write(reinterpret_cast<const char*>(&param.period), sizeof(double));
}
}
std::optional<std::vector<Parameters>> loadCache(std::filesystem::path file) {
std::ifstream stream(file, std::ifstream::binary);
int8_t version = 0;
stream.read(reinterpret_cast<char*>(&version), sizeof(int8_t));
if (version != CurrentCacheVersion) {
LINFO("The format of the cached file has changed");
return std::nullopt;
}
uint32_t size = 0;
stream.read(reinterpret_cast<char*>(&size), sizeof(uint32_t));
std::vector<Parameters> res;
res.reserve(size);
for (uint32_t i = 0; i < size; i++) {
Parameters param;
uint32_t nameLength = 0;
stream.read(reinterpret_cast<char*>(&nameLength), sizeof(uint32_t));
param.name.resize(nameLength);
stream.read(param.name.data(), nameLength * sizeof(char));
uint32_t idLength = 0;
stream.read(reinterpret_cast<char*>(&idLength), sizeof(uint32_t));
param.id.resize(idLength);
stream.read(param.id.data(), idLength * sizeof(char));
stream.read(reinterpret_cast<char*>(&param.inclination), sizeof(double));
stream.read(reinterpret_cast<char*>(&param.semiMajorAxis), sizeof(double));
stream.read(reinterpret_cast<char*>(&param.ascendingNode), sizeof(double));
stream.read(reinterpret_cast<char*>(&param.eccentricity), sizeof(double));
stream.read(reinterpret_cast<char*>(&param.argumentOfPeriapsis), sizeof(double));
stream.read(reinterpret_cast<char*>(&param.meanAnomaly), sizeof(double));
stream.read(reinterpret_cast<char*>(&param.epoch), sizeof(double));
stream.read(reinterpret_cast<char*>(&param.period), sizeof(double));
res.push_back(std::move(param));
}
return res;
}
std::vector<Parameters> readFile(std::filesystem::path file, Format format) {
std::filesystem::path cachedFile = FileSys.cacheManager()->cachedFilename(file);
if (std::filesystem::is_regular_file(cachedFile)) {
LINFO(fmt::format(
"Cached file {} used for Kepler file {}", cachedFile, file
));
std::optional<std::vector<Parameters>> res = loadCache(cachedFile);
if (res.has_value()) {
return *res;
}
// If there is no value in the optional, the cached loading failed
}
std::vector<Parameters> res;
switch (format) {
case Format::TLE:
res = readTleFile(file);
break;
case Format::OMM:
res = readOmmFile(file);
break;
case Format::SBDB:
res = readSbdbFile(file);
break;
default:
throw ghoul::MissingCaseException();
}
LINFO(fmt::format("Saving cache {} for Kepler file {}", cachedFile, file));
saveCache(res, cachedFile);
return res;
}
} // namespace openspace::kepler

110
modules/space/kepler.h Normal file
View File

@@ -0,0 +1,110 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2022 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_SPACE___KEPLER___H__
#define __OPENSPACE_MODULE_SPACE___KEPLER___H__
#include <filesystem>
#include <string>
#include <vector>
namespace openspace::kepler {
struct Parameters {
// Some human-readable name for the object represented by this kepler parameter set
std::string name;
// Some form of unique identifier for the object represented by this data
std::string id;
double inclination = 0.0;
double semiMajorAxis = 0.0;
double ascendingNode = 0.0;
double eccentricity = 0.0;
double argumentOfPeriapsis = 0.0;
double meanAnomaly = 0.0;
double epoch = 0.0;
double period = 0.0;
};
/**
* Reads the object information from the provided \p file and returns them as individual
* values.
*
* \param file The file to the TLE file. This file must be a valid file
* \return Information about all of the contained objects in the \p file
*
* \pre \p file must be a file and must exist
* \throw ghoul::RuntimeError If the provided \p file is not a valid TLE file
*/
std::vector<Parameters> readTleFile(std::filesystem::path file);
/**
* Reads the object information from the provided \p file and returns them as individual
* values.
*
* \param file The file to the OMM file. This file must be a valid file
* \return Information about all of the contained objects in the \p file
*
* \pre \p file must be a file and must exist
* \throw ghoul::RuntimeError If the provided \p file is not a valid OMM file
*/
std::vector<Parameters> readOmmFile(std::filesystem::path file);
/**
* Reads the object information from a CSV file following JPL's Small Body Database
* format, which provides the Epoch, eccentricity, semi-major axis (in AU), inclination,
* ascending node, argument of periapsis, mean anomaly, and period in that order.
*
* \param file The CSV file containing the information about the objects
* \return Information about all of the contained objects in the \p file
*
* \pre \p file must be a file and must exist
* \throw ghoul::RuntimeError If the provided \p is not a valid JPL SBDB CSV format
*/
std::vector<Parameters> readSbdbFile(std::filesystem::path file);
/**
* The different formats that the #readFile function is capable of loading
*/
enum class Format {
TLE,
OMM,
SBDB
};
/**
* Reads the object information from the provided file.
*
* \param file The file containing the information about the objects
* \param format The format of the provided \p file
* \return Information about all of the contained objects in the \p file
*
* \pre \p file must be a file and must exist
* \throw ghoul::RuntimeError If the provided \p is not in the provided file
*/
std::vector<Parameters> readFile(std::filesystem::path file, Format format);
} // namespace openspace::kepler
#endif // __OPENSPACE_MODULE_SPACE___KEPLER___H__

View File

@@ -25,7 +25,6 @@
#include <modules/space/rendering/renderableorbitalkepler.h>
#include <modules/space/translation/keplertranslation.h>
#include <modules/space/translation/tletranslation.h>
#include <modules/space/spacemodule.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/rendering/renderengine.h>
@@ -42,130 +41,10 @@
#include <chrono>
#include <fstream>
#include <math.h>
#include <random>
#include <vector>
namespace {
constexpr std::array<int, 36> LeapYears = {
1956, 1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992, 1996,
2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036, 2040,
2044, 2048, 2052, 2056
};
constexpr std::array<int, 12> DaysOfMonths = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
// Find the position of the current year in the vector; its position in
// the vector gives the number of leap seconds
struct LeapSecond {
int year;
int dayOfYear;
bool operator<(const LeapSecond& rhs) const {
return std::tie(year, dayOfYear) < std::tie(rhs.year, rhs.dayOfYear);
}
};
constexpr LeapSecond LeapEpoch = { 2000, 1 };
// List taken from: https://www.ietf.org/timezones/data/leap-seconds.list
constexpr std::array<LeapSecond, 28> LeapSeconds = {
LeapSecond{ 1972, 1 },
LeapSecond{ 1972, 183 },
LeapSecond{ 1973, 1 },
LeapSecond{ 1974, 1 },
LeapSecond{ 1975, 1 },
LeapSecond{ 1976, 1 },
LeapSecond{ 1977, 1 },
LeapSecond{ 1978, 1 },
LeapSecond{ 1979, 1 },
LeapSecond{ 1980, 1 },
LeapSecond{ 1981, 182 },
LeapSecond{ 1982, 182 },
LeapSecond{ 1983, 182 },
LeapSecond{ 1985, 182 },
LeapSecond{ 1988, 1 },
LeapSecond{ 1990, 1 },
LeapSecond{ 1991, 1 },
LeapSecond{ 1992, 183 },
LeapSecond{ 1993, 182 },
LeapSecond{ 1994, 182 },
LeapSecond{ 1996, 1 },
LeapSecond{ 1997, 182 },
LeapSecond{ 1999, 1 },
LeapSecond{ 2006, 1 },
LeapSecond{ 2009, 1 },
LeapSecond{ 2012, 183 },
LeapSecond{ 2015, 182 },
LeapSecond{ 2017, 1 }
};
// Count the number of full days since the beginning of 2000 to the beginning of
// the parameter 'year'
int countDays(int year) {
// Find the position of the current year in the vector, the difference
// between its position and the position of 2000 (for J2000) gives the
// number of leap years
constexpr int Epoch = 2000;
constexpr int DaysRegularYear = 365;
constexpr int DaysLeapYear = 366;
if (year == Epoch) {
return 0;
}
// Get the position of the most recent leap year
const auto lb = std::lower_bound(LeapYears.begin(), LeapYears.end(), year);
// Get the position of the epoch
const auto y2000 = std::find(LeapYears.begin(), LeapYears.end(), Epoch);
// The distance between the two iterators gives us the number of leap years
const int nLeapYears = static_cast<int>(std::abs(std::distance(y2000, lb)));
const int nYears = std::abs(year - Epoch);
const int nRegularYears = nYears - nLeapYears;
// Get the total number of days as the sum of leap years + non leap years
const int result = nRegularYears * DaysRegularYear + nLeapYears * DaysLeapYear;
return result;
}
// Returns the number of leap seconds that lie between the {year, dayOfYear}
// time point and { 2000, 1 }
int countLeapSeconds(int year, int dayOfYear) {
// Get the position of the last leap second before the desired date
LeapSecond date{ year, dayOfYear };
const auto it = std::lower_bound(LeapSeconds.begin(), LeapSeconds.end(), date);
// Get the position of the Epoch
const auto y2000 = std::lower_bound(
LeapSeconds.begin(),
LeapSeconds.end(),
LeapEpoch
);
// The distance between the two iterators gives us the number of leap years
const int nLeapSeconds = static_cast<int>(std::abs(std::distance(y2000, it)));
return nLeapSeconds;
}
int daysIntoGivenYear(int year, int month, int dayOfMonth) {
//month and dayCount are zero-based.
month -= 1;
int dayCount = dayOfMonth - 1;
const int February = 1;
const bool isInLeapYear =
std::find(LeapYears.begin(), LeapYears.end(), year)
!= LeapYears.end();
for (int m = 0; m < month; ++m) {
dayCount += DaysOfMonths[m];
if (m == February && isInLeapYear) {
dayCount += 1;
}
}
return dayCount;
}
constexpr openspace::properties::Property::PropertyInfo PathInfo = {
"Path",
"Path",
@@ -213,10 +92,30 @@ namespace {
"Contiguous Size of Render Block",
"Number of objects to render sequentially from StartRenderIdx"
};
constexpr openspace::properties::Property::PropertyInfo ContiguousModeInfo = {
"ContiguousMode",
"Contiguous Mode",
"If enabled, then the contiguous set of objects starting from StartRenderIdx "
"of size RenderSize will be rendered. If disabled, then the number of objects "
"defined by UpperLimit will rendered from an evenly dispersed sample of the "
"full length of the data file."
};
struct [[codegen::Dictionary(RenderableOrbitalKepler)]] Parameters {
// [[codegen::verbatim(PathInfo.description)]]
std::string path;
std::filesystem::path path;
enum class [[codegen::map(openspace::kepler::Format)]] Format {
// A NORAD-style Two-Line element
TLE,
// Orbit Mean-Elements Message in the KVN notation
OMM,
// JPL's Small Bodies Database
SBDB
};
// The file format that is contained in the file
Format format;
// [[codegen::verbatim(SegmentQualityInfo.description)]]
int segmentQuality;
@@ -235,6 +134,9 @@ namespace {
// [[codegen::verbatim(RenderSizeInfo.description)]]
std::optional<int> renderSize;
// [[codegen::verbatim(ContiguousModeInfo.description)]]
std::optional<bool> contiguousMode;
};
#include "renderableorbitalkepler_codegen.cpp"
} // namespace
@@ -245,140 +147,20 @@ documentation::Documentation RenderableOrbitalKepler::Documentation() {
return codegen::doc<Parameters>("space_renderableorbitalkepler");
}
double RenderableOrbitalKepler::calculateSemiMajorAxis(double meanMotion) const {
constexpr double GravitationalConstant = 6.6740831e-11;
constexpr double MassEarth = 5.9721986e24;
constexpr double muEarth = GravitationalConstant * MassEarth;
// Use Kepler's 3rd law to calculate semimajor axis
// a^3 / P^2 = mu / (2pi)^2
// <=> a = ((mu * P^2) / (2pi^2))^(1/3)
// with a = semimajor axis
// P = period in seconds
// mu = G*M_earth
const double period =
std::chrono::seconds(std::chrono::hours(24)).count() / meanMotion;
constexpr double pisq = glm::pi<double>() * glm::pi<double>();
const double semiMajorAxis = pow((muEarth * period*period) / (4 * pisq), 1.0 / 3.0);
// We need the semi major axis in km instead of m
return semiMajorAxis / 1000.0;
}
double RenderableOrbitalKepler::epochFromSubstring(const std::string& epochString) const {
// The epochString is in the form:
// YYDDD.DDDDDDDD
// With YY being the last two years of the launch epoch, the first DDD the day
// of the year and the remaning a fractional part of the day
// The main overview of this function:
// 1. Reconstruct the full year from the YY part
// 2. Calculate the number of days since the beginning of the year
// 3. Convert the number of days to a number of seconds
// 4. Get the number of leap seconds since January 1st, 2000 and remove them
// 5. Adjust for the fact the epoch starts on 1st Januaray at 12:00:00, not
// midnight
// According to https://celestrak.com/columns/v04n03/
// Apparently, US Space Command sees no need to change the two-line element
// set format yet since no artificial earth satellites existed prior to 1957.
// By their reasoning, two-digit years from 57-99 correspond to 1957-1999 and
// those from 00-56 correspond to 2000-2056. We'll see each other again in 057!
// 1. Get the full year
std::string yearPrefix =
std::atoi(epochString.substr(0, 2).c_str()) > 57 ? "19" : "20";
const int year = std::atoi((yearPrefix + epochString.substr(0, 2)).c_str());
const int daysSince2000 = countDays(year);
// 2.
double daysInYear = std::atof(epochString.substr(2).c_str());
// 3
using namespace std::chrono;
const int SecondsPerDay = static_cast<int>(seconds(hours(24)).count());
//Need to subtract 1 from daysInYear since it is not a zero-based count
const double nSecondsSince2000 = (daysSince2000 + daysInYear - 1) * SecondsPerDay;
// 4
// We need to remove additional leap seconds past 2000 and add them prior to
// 2000 to sync up the time zones
const double nLeapSecondsOffset = -countLeapSeconds(
year,
static_cast<int>(std::floor(daysInYear))
);
// 5
const double nSecondsEpochOffset = static_cast<double>(seconds(hours(12)).count());
// Combine all of the values
const double epoch = nSecondsSince2000 + nLeapSecondsOffset - nSecondsEpochOffset;
return epoch;
}
double RenderableOrbitalKepler::epochFromYMDdSubstring(const std::string& epochString) {
// The epochString is in the form:
// YYYYMMDD.ddddddd
// With YYYY as the year, MM the month (1 - 12), DD the day of month (1-31),
// and dddd the fraction of that day.
// The main overview of this function:
// 1. Read the year value
// 2. Calculate the number of days since the beginning of the year
// 3. Convert the number of days to a number of seconds
// 4. Get the number of leap seconds since January 1st, 2000 and remove them
// 5. Adjust for the fact the epoch starts on 1st January at 12:00:00, not
// midnight
// 1
int year = std::atoi(epochString.substr(0, 4).c_str());
const int daysSince2000 = countDays(year);
// 2.
int monthNum = std::atoi(epochString.substr(4, 2).c_str());
int dayOfMonthNum = std::atoi(epochString.substr(6, 2).c_str());
int wholeDaysInto = daysIntoGivenYear(year, monthNum, dayOfMonthNum);
double fractionOfDay = std::atof(epochString.substr(9, 7).c_str());
double daysInYear = static_cast<double>(wholeDaysInto) + fractionOfDay;
// 3
using namespace std::chrono;
const int SecondsPerDay = static_cast<int>(seconds(hours(24)).count());
//Need to subtract 1 from daysInYear since it is not a zero-based count
const double nSecondsSince2000 = (daysSince2000 + daysInYear - 1) * SecondsPerDay;
// 4
// We need to remove additional leap seconds past 2000 and add them prior to
// 2000 to sync up the time zones
const double nLeapSecondsOffset = -countLeapSeconds(
year,
static_cast<int>(std::floor(daysInYear))
);
// 5
const double nSecondsEpochOffset = static_cast<double>(seconds(hours(12)).count());
// Combine all of the values
const double epoch = nSecondsSince2000 + nLeapSecondsOffset - nSecondsEpochOffset;
return epoch;
}
RenderableOrbitalKepler::RenderableOrbitalKepler(const ghoul::Dictionary& dict)
: Renderable(dict)
, _segmentQuality(SegmentQualityInfo, 2, 1, 10)
, _startRenderIdx(StartRenderIdxInfo, 0, 0, 1)
, _sizeRender(RenderSizeInfo, 1, 1, 2)
, _path(PathInfo)
, _contiguousMode(ContiguousModeInfo, false)
{
_reinitializeTrailBuffers = std::function<void()>([this] { initializeGL(); });
const Parameters p = codegen::bake<Parameters>(dict);
addProperty(_opacity);
_segmentQuality = static_cast<unsigned int>(p.segmentQuality);
_segmentQuality.onChange(_reinitializeTrailBuffers);
_segmentQuality.onChange([this]() { initializeGL(); });
addProperty(_segmentQuality);
_appearance.lineColor = p.color;
@@ -386,13 +168,37 @@ RenderableOrbitalKepler::RenderableOrbitalKepler(const ghoul::Dictionary& dict)
_appearance.lineWidth = p.lineWidth.value_or(2.f);
addPropertySubOwner(_appearance);
_path = p.path;
_path.onChange(_reinitializeTrailBuffers);
_path = p.path.string();
_path.onChange([this]() { initializeGL(); });
addProperty(_path);
_format = codegen::map<kepler::Format>(p.format);
_startRenderIdx = p.startRenderIdx.value_or(0);
_startRenderIdx.onChange([this]() {
if (_contiguousMode) {
if ((_numObjects - _startRenderIdx) < _sizeRender) {
_sizeRender = static_cast<unsigned int>(_numObjects - _startRenderIdx);
}
_updateDataBuffersAtNextRender = true;
}
});
addProperty(_startRenderIdx);
_sizeRender = p.renderSize.value_or(0u);
_sizeRender.onChange([this]() {
if (_contiguousMode) {
if (_sizeRender > (_numObjects - _startRenderIdx)) {
_startRenderIdx = static_cast<unsigned int>(_numObjects - _sizeRender);
}
}
_updateDataBuffersAtNextRender = true;
});
addProperty(_sizeRender);
_contiguousMode = p.contiguousMode.value_or(false);
_contiguousMode.onChange([this]() { _updateDataBuffersAtNextRender = true; });
addProperty(_contiguousMode);
}
void RenderableOrbitalKepler::initializeGL() {
@@ -420,14 +226,6 @@ void RenderableOrbitalKepler::initializeGL() {
_uniformCache.opacity = _programObject->uniformLocation("opacity");
updateBuffers();
double maxSemiMajorAxis = 0.0;
for (const KeplerParameters& kp : _data) {
if (kp.semiMajorAxis > maxSemiMajorAxis) {
maxSemiMajorAxis = kp.semiMajorAxis;
}
}
setBoundingSphere(maxSemiMajorAxis * 1000);
}
void RenderableOrbitalKepler::deinitializeGL() {
@@ -447,14 +245,16 @@ bool RenderableOrbitalKepler::isReady() const {
return _programObject != nullptr;
}
void RenderableOrbitalKepler::render(const RenderData& data, RendererTasks&) {
if (_data.empty()) {
return;
}
void RenderableOrbitalKepler::update(const UpdateData&) {
if (_updateDataBuffersAtNextRender) {
_updateDataBuffersAtNextRender = false;
initializeGL();
updateBuffers();
}
}
void RenderableOrbitalKepler::render(const RenderData& data, RendererTasks&) {
if (_vertexBufferData.empty()) {
return;
}
_programObject->activate();
@@ -472,9 +272,7 @@ void RenderableOrbitalKepler::render(const RenderData& data, RendererTasks&) {
);
// Because we want the property to work similar to the planet trails
const float fade = static_cast<float>(
pow(_appearance.lineFade.maxValue() - _appearance.lineFade, 2.0)
);
const float fade = pow(_appearance.lineFade.maxValue() - _appearance.lineFade, 2.f);
_programObject->setUniform(_uniformCache.projection, data.camera.projectionMatrix());
_programObject->setUniform(_uniformCache.color, _appearance.lineColor);
@@ -482,7 +280,7 @@ void RenderableOrbitalKepler::render(const RenderData& data, RendererTasks&) {
glLineWidth(_appearance.lineWidth);
const size_t nrOrbits = _data.size();
const size_t nrOrbits = _segmentSize.size();
gl::GLint vertices = 0;
//glDepthMask(false);
@@ -499,21 +297,83 @@ void RenderableOrbitalKepler::render(const RenderData& data, RendererTasks&) {
}
void RenderableOrbitalKepler::updateBuffers() {
readDataFile(_path);
std::vector<kepler::Parameters> parameters = kepler::readFile(
_path.value(),
_format
);
_numObjects = parameters.size();
if (_startRenderIdx < 0 || _startRenderIdx >= _numObjects) {
throw ghoul::RuntimeError(fmt::format(
"Start index {} out of range [0, {}]", _startRenderIdx, _numObjects
));
}
long long endElement = _startRenderIdx + _sizeRender - 1;
endElement = (endElement >= _numObjects) ? _numObjects - 1 : endElement;
if (endElement < 0 || endElement >= _numObjects) {
throw ghoul::RuntimeError(fmt::format(
"End index {} out of range [0, {}]", endElement, _numObjects
));
}
_startRenderIdx.setMaxValue(static_cast<unsigned int>(_numObjects - 1));
_sizeRender.setMaxValue(static_cast<unsigned int>(_numObjects));
if (_sizeRender == 0u) {
_sizeRender = static_cast<unsigned int>(_numObjects);
}
if (_contiguousMode) {
if (_startRenderIdx >= parameters.size() ||
(_startRenderIdx + _sizeRender) >= parameters.size())
{
throw ghoul::RuntimeError(fmt::format(
"Tried to load {} objects but only {} are available",
_startRenderIdx + _sizeRender, parameters.size()
));
}
// Extract subset that starts at _startRenderIdx and contains _sizeRender obejcts
parameters = std::vector<kepler::Parameters>(
parameters.begin() + _startRenderIdx,
parameters.begin() + _startRenderIdx + _sizeRender
);
}
else {
// First shuffle the whole array
std::default_random_engine rng;
std::shuffle(parameters.begin(), parameters.end(), rng);
// Then take the first _sizeRender values
parameters = std::vector<kepler::Parameters>(
parameters.begin(),
parameters.begin() + _sizeRender
);
}
_segmentSize.clear();
for (const kepler::Parameters& p : parameters) {
const double scale = static_cast<double>(_segmentQuality) * 10.0;
_segmentSize.push_back(
static_cast<size_t>(scale + (scale / pow(1 - p.eccentricity, 1.2)))
);
}
size_t nVerticesTotal = 0;
int numOrbits = static_cast<int>(_data.size());
int numOrbits = static_cast<int>(parameters.size());
for (int i = 0; i < numOrbits; ++i) {
nVerticesTotal += _segmentSize[i] + 1;
}
_vertexBufferData.resize(nVerticesTotal);
size_t vertexBufIdx = 0;
KeplerTranslation keplerTranslator;
for (int orbitIdx = 0; orbitIdx < numOrbits; ++orbitIdx) {
const KeplerParameters& orbit = _data[orbitIdx];
const kepler::Parameters& orbit = parameters[orbitIdx];
_keplerTranslator.setKeplerElements(
keplerTranslator.setKeplerElements(
orbit.eccentricity,
orbit.semiMajorAxis,
orbit.inclination,
@@ -526,9 +386,9 @@ void RenderableOrbitalKepler::updateBuffers() {
for (size_t j = 0 ; j < (_segmentSize[orbitIdx] + 1); ++j) {
double timeOffset = orbit.period *
static_cast<double>(j)/ static_cast<double>(_segmentSize[orbitIdx]);
static_cast<double>(j) / static_cast<double>(_segmentSize[orbitIdx]);
glm::dvec3 position = _keplerTranslator.position({
glm::dvec3 position = keplerTranslator.position({
{},
Time(timeOffset + orbit.epoch),
Time(0.0)
@@ -556,7 +416,7 @@ void RenderableOrbitalKepler::updateBuffers() {
);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(TrailVBOLayout), nullptr);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(TrailVBOLayout), nullptr);
glEnableVertexAttribArray(1);
glVertexAttribPointer(
@@ -569,6 +429,14 @@ void RenderableOrbitalKepler::updateBuffers() {
);
glBindVertexArray(0);
double maxSemiMajorAxis = 0.0;
for (const kepler::Parameters& kp : parameters) {
if (kp.semiMajorAxis > maxSemiMajorAxis) {
maxSemiMajorAxis = kp.semiMajorAxis;
}
}
setBoundingSphere(maxSemiMajorAxis * 1000);
}
} // namespace opensapce

View File

@@ -28,6 +28,7 @@
#include <openspace/rendering/renderable.h>
#include <modules/base/rendering/renderabletrail.h>
#include <modules/space/kepler.h>
#include <modules/space/translation/keplertranslation.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/scalar/uintproperty.h>
@@ -47,65 +48,21 @@ public:
void deinitializeGL() override;
bool isReady() const override;
void update(const UpdateData& data) override;
void render(const RenderData& data, RendererTasks& rendererTask) override;
/**
* Reads the provided data file and calls the KeplerTranslation::setKeplerElments
* method with the correct values. If \p filename is a valid data file but contains
* disallowed values (see KeplerTranslation::setKeplerElements), a
* KeplerTranslation::RangeError is thrown.
*
* \param filename The path to the file that contains the data file.
*
* \throw ghoul::RuntimeError if the data file does not exist or there is a
* problem with its format.
* \pre The \p filename must exist
*/
virtual void readDataFile(const std::string& filename) = 0;
protected:
static documentation::Documentation Documentation();
double calculateSemiMajorAxis(double meanMotion) const;
double epochFromSubstring(const std::string& epochString) const;
double epochFromYMDdSubstring(const std::string& epochString);
private:
void updateBuffers();
std::function<void()> _reinitializeTrailBuffers;
std::function<void()> _updateStartRenderIdxSelect;
std::function<void()> _updateRenderSizeSelect;
struct KeplerParameters {
double inclination = 0.0;
double semiMajorAxis = 0.0;
double ascendingNode = 0.0;
double eccentricity = 0.0;
double argumentOfPeriapsis = 0.0;
double meanAnomaly = 0.0;
double meanMotion = 0.0;
double epoch = 0.0;
double period = 0.0;
};
bool _updateDataBuffersAtNextRender = false;
std::streamoff _numObjects;
bool _isFileReadinitialized = false;
inline static constexpr double convertAuToKm = 1.496e8;
inline static constexpr double convertDaysToSecs = 86400.0;
std::vector<KeplerParameters> _data;
std::streamoff _numObjects;
std::vector<size_t> _segmentSize;
properties::UIntProperty _segmentQuality;
properties::UIntProperty _startRenderIdx;
properties::UIntProperty _sizeRender;
properties::Property::OnChangeHandle _startRenderIdxCallbackHandle;
properties::Property::OnChangeHandle _sizeRenderCallbackHandle;
private:
struct Vertex {
glm::vec3 position = glm::vec3(0.f);
glm::vec3 color = glm::vec3(0.f);
glm::vec2 texcoord = glm::vec2(0.f);
};
/// The layout of the VBOs
struct TrailVBOLayout {
@@ -117,20 +74,17 @@ private:
double period = 0.0;
};
KeplerTranslation _keplerTranslator;
/// The backend storage for the vertex buffer object containing all points for this
/// trail.
/// The backend storage for the vertex buffer object containing all points
std::vector<TrailVBOLayout> _vertexBufferData;
GLuint _vertexArray;
GLuint _vertexBuffer;
ghoul::opengl::ProgramObject* _programObject;
properties::StringProperty _path;
properties::BoolProperty _contiguousMode;
kepler::Format _format;
RenderableTrail::Appearance _appearance;
glm::vec3 _position = glm::vec3(0.f);
UniformCache(modelView, projection, lineFade, inGameTime, color, opacity,
numberOfSegments) _uniformCache;

View File

@@ -1,253 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2022 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/space/rendering/renderablesatellites.h>
#include <modules/space/translation/keplertranslation.h>
#include <modules/space/translation/tletranslation.h>
#include <modules/space/spacemodule.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/engine/globals.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <openspace/util/time.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/filesystem/file.h>
#include <ghoul/misc/csvreader.h>
#include <ghoul/opengl/programobject.h>
#include <ghoul/logging/logmanager.h>
#include <chrono>
#include <math.h>
#include <filesystem>
#include <fstream>
#include <vector>
namespace {
constexpr std::string_view _loggerCat = "Satellites";
constexpr openspace::properties::Property::PropertyInfo SegmentsInfo = {
"Segments",
"Segments",
"The number of segments to use for each orbit ellipse"
};
struct [[codegen::Dictionary(RenderableSatellites)]] Parameters {
// [[codegen::verbatim(SegmentsInfo.description)]]
double segments;
};
#include "renderablesatellites_codegen.cpp"
}
namespace openspace {
documentation::Documentation RenderableSatellites::Documentation() {
return codegen::doc<Parameters>(
"space_renderablesatellites",
RenderableOrbitalKepler::Documentation()
);
}
RenderableSatellites::RenderableSatellites(const ghoul::Dictionary& dictionary)
: RenderableOrbitalKepler(dictionary)
{
// Commented out right now as its not super clear how it works with inheritance. We'd
// probably want a codegen::check function that only does the checking without
// actually creating a Parameter objects
// codegen::bake<Parameters>(dictionary);
addProperty(_startRenderIdx);
addProperty(_sizeRender);
_updateStartRenderIdxSelect = [this]() {
if ((_numObjects - _startRenderIdx) < _sizeRender) {
_sizeRender = static_cast<unsigned int>(_numObjects - _startRenderIdx);
}
updateBuffers();
};
_updateRenderSizeSelect = [this]() {
if (_sizeRender > (_numObjects - _startRenderIdx)) {
_startRenderIdx = static_cast<unsigned int>(_numObjects - _sizeRender);
}
updateBuffers();
};
_startRenderIdxCallbackHandle = _startRenderIdx.onChange(_updateStartRenderIdxSelect);
_sizeRenderCallbackHandle = _sizeRender.onChange(_updateRenderSizeSelect);
}
void RenderableSatellites::readDataFile(const std::string& filename) {
if (!std::filesystem::is_regular_file(filename)) {
throw ghoul::RuntimeError(fmt::format(
"Satellite TLE file {} does not exist", filename
));
}
_data.clear();
_segmentSize.clear();
std::ifstream file;
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
file.open(filename);
std::streamoff numberOfLines = std::count(
std::istreambuf_iterator<char>(file),
std::istreambuf_iterator<char>(),
'\n'
);
file.seekg(std::ios_base::beg); // reset iterator to beginning of file
_numObjects = numberOfLines / nLineEntriesPerSatellite;
if (!_isFileReadinitialized) {
_isFileReadinitialized = true;
initializeFileReading();
}
std::string line = "-";
std::string name;
long long endElement = _startRenderIdx + _sizeRender - 1;
endElement = (endElement >= _numObjects) ? _numObjects - 1 : endElement;
//Burn lines if not starting at first element
for (unsigned int k = 0; k < _startRenderIdx; ++k) {
skipSingleEntryInFile(file);
}
for (std::streamoff i = _startRenderIdx; i <= endElement; i++) {
//Read title line
std::getline(file, name);
KeplerParameters keplerElements;
std::getline(file, line);
if (line[0] == '1') {
// First line
// Field Columns Content
// 1 01-01 Line number
// 2 03-07 Satellite number
// 3 08-08 Classification (U = Unclassified)
// 4 10-11 International Designator (Last two digits of launch year)
// 5 12-14 International Designator (Launch number of the year)
// 6 15-17 International Designator(piece of the launch) A
name += " " + line.substr(2, 15);
if (_startRenderIdx == i && _sizeRender == 1) {
LINFO(fmt::format(
"Set render block to start at object {}",
name
));
}
// 7 19-20 Epoch Year(last two digits of year)
// 8 21-32 Epoch(day of the year and fractional portion of the day)
// 9 34-43 First Time Derivative of the Mean Motion divided by two
// 10 45-52 Second Time Derivative of Mean Motion divided by six
// 11 54-61 BSTAR drag term(decimal point assumed)[10] - 11606 - 4
// 12 63-63 The "Ephemeris type"
// 13 65-68 Element set number.Incremented when a new TLE is generated
// 14 69-69 Checksum (modulo 10)
keplerElements.epoch = epochFromSubstring(line.substr(18, 14));
}
else {
throw ghoul::RuntimeError(fmt::format(
"File {} entry {} does not have '1' header", filename, i + 1
));
}
std::getline(file, line);
if (line[0] == '2') {
// Second line
// Field Columns Content
// 1 01-01 Line number
// 2 03-07 Satellite number
// 3 09-16 Inclination (degrees)
// 4 18-25 Right ascension of the ascending node (degrees)
// 5 27-33 Eccentricity (decimal point assumed)
// 6 35-42 Argument of perigee (degrees)
// 7 44-51 Mean Anomaly (degrees)
// 8 53-63 Mean Motion (revolutions per day)
// 9 64-68 Revolution number at epoch (revolutions)
// 10 69-69 Checksum (modulo 10)
std::stringstream stream;
stream.exceptions(std::ios::failbit);
// Get inclination
stream.str(line.substr(8, 8));
stream >> keplerElements.inclination;
stream.clear();
// Get Right ascension of the ascending node
stream.str(line.substr(17, 8));
stream >> keplerElements.ascendingNode;
stream.clear();
// Get Eccentricity
stream.str("0." + line.substr(26, 7));
stream >> keplerElements.eccentricity;
stream.clear();
// Get argument of periapsis
stream.str(line.substr(34, 8));
stream >> keplerElements.argumentOfPeriapsis;
stream.clear();
// Get mean anomaly
stream.str(line.substr(43, 8));
stream >> keplerElements.meanAnomaly;
stream.clear();
// Get mean motion
stream.str(line.substr(52, 11));
stream >> keplerElements.meanMotion;
}
else {
throw ghoul::RuntimeError(fmt::format(
"File {} entry {} does not have '2' header", filename, i + 1
));
}
// Calculate the semi major axis based on the mean motion using kepler's laws
keplerElements.semiMajorAxis = calculateSemiMajorAxis(keplerElements.meanMotion);
using namespace std::chrono;
double period = seconds(hours(24)).count() / keplerElements.meanMotion;
keplerElements.period = period;
_data.push_back(keplerElements);
_segmentSize.push_back(_segmentQuality * 16);
}
file.close();
}
void RenderableSatellites::initializeFileReading() {
_startRenderIdx.setMaxValue(static_cast<unsigned int>(_numObjects - 1));
_sizeRender.setMaxValue(static_cast<unsigned int>(_numObjects));
if (_sizeRender == 0u) {
_sizeRender = static_cast<unsigned int>(_numObjects);
}
}
void RenderableSatellites::skipSingleEntryInFile(std::ifstream& file) {
std::string line;
for (unsigned int i = 0; i < nLineEntriesPerSatellite; i++) {
std::getline(file, line);
}
}
}

View File

@@ -1,424 +0,0 @@
/****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2022 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/space/rendering/renderablesmallbody.h>
#include <modules/space/rendering/renderablesatellites.h>
#include <modules/space/translation/keplertranslation.h>
#include <modules/space/translation/tletranslation.h>
#include <modules/space/spacemodule.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/engine/globals.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <openspace/util/time.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/filesystem/file.h>
#include <ghoul/misc/csvreader.h>
#include <ghoul/opengl/programobject.h>
#include <ghoul/logging/logmanager.h>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <math.h>
#include <vector>
namespace {
constexpr std::string_view _loggerCat = "SmallSolarSystemBody";
constexpr openspace::properties::Property::PropertyInfo ContiguousModeInfo = {
"ContiguousMode",
"Contiguous Mode",
"If enabled, then the contiguous set of objects starting from StartRenderIdx "
"of size RenderSize will be rendered. If disabled, then the number of objects "
"defined by UpperLimit will rendered from an evenly dispersed sample of the "
"full length of the data file"
};
constexpr openspace::properties::Property::PropertyInfo UpperLimitInfo = {
"UpperLimit",
"Upper Limit",
"Upper limit on the number of objects for this renderable, regardless of "
"how many objects are contained in the data file. Produces an evenly-distributed"
"sample from the data file"
};
double importAngleValue(const std::string& angle) {
if (angle.empty()) {
return 0.0;
}
double output = std::stod(angle);
output = std::fmod(output, 360.0);
if (output < 0.0) {
output += 360.0;
}
return output;
}
std::string& formatObjectName(std::string& name) {
const std::string trimChars = "\t\n\v\f\r\" ";
name.erase(0, name.find_first_not_of(trimChars));
name.erase(name.find_last_not_of(trimChars) + 1);
return name;
}
struct [[codegen::Dictionary(RenderableSmallBody)]] Parameters {
// [[codegen::verbatim(ContiguousModeInfo.description)]]
std::optional<bool> contiguousMode;
// [[codegen::verbatim(UpperLimitInfo.description)]]
std::optional<int> upperLimit;
};
#include "renderablesmallbody_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation RenderableSmallBody::Documentation() {
return codegen::doc<Parameters>(
"space_renderablesmallbody",
RenderableOrbitalKepler::Documentation()
);
}
RenderableSmallBody::RenderableSmallBody(const ghoul::Dictionary& dictionary)
: RenderableOrbitalKepler(dictionary)
, _contiguousMode(ContiguousModeInfo, false)
, _upperLimit(UpperLimitInfo, 1000, 1, 1000000)
{
codegen::bake<Parameters>(dictionary);
addProperty(_startRenderIdx);
addProperty(_sizeRender);
addProperty(_contiguousMode);
addProperty(_upperLimit);
if (dictionary.hasValue<double>(UpperLimitInfo.identifier)) {
_upperLimit = static_cast<unsigned int>(
dictionary.value<double>(UpperLimitInfo.identifier));
}
else {
_upperLimit = 0u;
}
if (dictionary.hasValue<bool>(ContiguousModeInfo.identifier)) {
_contiguousMode = dictionary.value<bool>(ContiguousModeInfo.identifier);
}
else {
_contiguousMode = false;
}
_updateStartRenderIdxSelect = std::function<void()>([this] {
if (_contiguousMode) {
if ((_numObjects - _startRenderIdx) < _sizeRender) {
_sizeRender = static_cast<unsigned int>(_numObjects - _startRenderIdx);
}
_updateDataBuffersAtNextRender = true;
}
});
_updateRenderSizeSelect = std::function<void()>([this] {
if (_contiguousMode) {
if (_sizeRender > (_numObjects - _startRenderIdx)) {
_startRenderIdx = static_cast<unsigned int>(_numObjects - _sizeRender);
}
_updateDataBuffersAtNextRender = true;
}
});
_updateRenderUpperLimitSelect = std::function<void()>([this] {
if (!_contiguousMode) {
_updateDataBuffersAtNextRender = true;
}
});
_updateContiguousModeSelect = std::function<void()>([this] {
_updateDataBuffersAtNextRender = true;
});
_startRenderIdxCallbackHandle = _startRenderIdx.onChange(_updateStartRenderIdxSelect);
_sizeRenderCallbackHandle = _sizeRender.onChange(_updateRenderSizeSelect);
_upperLimitCallbackHandle = _upperLimit.onChange(_updateRenderUpperLimitSelect);
_contiguousModeCallbackhandle =
_contiguousMode.onChange(_updateContiguousModeSelect);
}
void RenderableSmallBody::readDataFile(const std::string& filename) {
if (!std::filesystem::is_regular_file(filename)) {
throw ghoul::RuntimeError(fmt::format(
"JPL SBDB file {} does not exist", filename
));
}
std::ifstream file;
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
file.open(filename);
std::streamoff numberOfLines = std::count(
std::istreambuf_iterator<char>(file),
std::istreambuf_iterator<char>(),
'\n'
);
file.seekg(std::ios_base::beg); // reset iterator to beginning of file
_data.clear();
_sbNames.clear();
_segmentSize.clear();
std::string line;
unsigned int csvLine = 0;
int fieldCount = 0;
const std::string expectedHeaderLine = "full_name,epoch_cal,e,a,i,om,w,ma,per";
try {
std::getline(file, line); // get rid of first line (header)
numberOfLines -= 1;
if (_numObjects != numberOfLines) {
_isFileReadinitialized = false;
}
_numObjects = numberOfLines;
float lineSkipFraction = 1.0;
if (!_isFileReadinitialized) {
_isFileReadinitialized = true;
initializeFileReading();
}
unsigned int startElement = 0;
unsigned int endElement;
if (_contiguousMode) {
lineSkipFraction = 1.0;
startElement = _startRenderIdx;
endElement = _startRenderIdx + _sizeRender - 1;
}
else {
lineSkipFraction = static_cast<float>(_upperLimit)
/ static_cast<float>(_numObjects);
endElement = static_cast<unsigned int>(_numObjects - 1);
}
if (line.compare(expectedHeaderLine) != 0) {
LERROR(fmt::format(
"File {} does not have the appropriate JPL SBDB header at line 1",
filename
));
return;
}
unsigned int sequentialLineErrors = 0;
endElement =
(endElement >= _numObjects) ?
static_cast<unsigned int>(_numObjects - 1) :
endElement;
// Burn lines if not starting at first element
for (unsigned int k = 0; k < startElement; ++k) {
skipSingleLineInFile(file);
}
bool firstDataLine = true;
int lastLineCount = -1;
for (csvLine = startElement + 1;
csvLine <= endElement + 1;
csvLine++, sequentialLineErrors++)
{
float currLineFraction = static_cast<float>(csvLine - 1) * lineSkipFraction;
int currLineCount = static_cast<int>(currLineFraction);
if (currLineCount > lastLineCount) {
try {
readOrbitalParamsFromThisLine(firstDataLine, fieldCount, csvLine,
file);
sequentialLineErrors = 0;
}
catch (std::invalid_argument&) {
constexpr std::string_view errMsg = "Unable to convert field {} to "
"double value (invalid_argument exception). Ignoring line {}/{} "
"of {}";
LINFO(fmt::format(
errMsg,
fieldCount, csvLine + 1, numberOfLines, filename
));
}
catch (std::out_of_range&) {
constexpr std::string_view errMsg = "Unable to convert field {} to "
"double value (out_of_range exception). Ignoring line {}/{} of "
"{}";
LINFO(fmt::format(
errMsg,
fieldCount, csvLine + 1, numberOfLines, filename
));
}
catch (std::ios_base::failure&) {
throw;
}
if (sequentialLineErrors == 4) {
_data.clear();
_sbNames.clear();
LERROR(fmt::format(
"Abandoning data file {} (too many sequential line errors)",
filename
));
break;
}
}
else {
skipSingleLineInFile(file);
}
lastLineCount = currLineCount;
firstDataLine = false;
}
}
catch (const std::ios_base::failure&) {
LERROR(fmt::format(
"File read exception (ios_base::failure) while trying to read field {} at "
"line {}/{} of {}",
fieldCount, csvLine + 1, numberOfLines, filename
));
}
}
void RenderableSmallBody::initializeFileReading() {
_startRenderIdx.setMaxValue(static_cast<unsigned int>(_numObjects - 1));
_sizeRender.setMaxValue(static_cast<unsigned int>(_numObjects));
if (_sizeRender == 0u) {
_sizeRender = static_cast<unsigned int>(_numObjects);
}
_upperLimit.setMaxValue(static_cast<unsigned int>(_numObjects));
if (_upperLimit == 0u) {
_upperLimit = static_cast<unsigned int>(_numObjects);
}
}
void RenderableSmallBody::skipSingleLineInFile(std::ifstream& file) {
std::string line;
std::getline(file, line);
}
void RenderableSmallBody::readOrbitalParamsFromThisLine(bool firstDataLine,
int& fieldCount,
unsigned int& csvLine,
std::ifstream& file)
{
const int numDataFields = 8;
std::string name;
std::string field;
KeplerParameters keplerElements;
//If there was a read/conversion error in the previous line, then read the remainder
// of that line and throw it out first before proceeding with the next line.
if (fieldCount != (numDataFields + 1) && !firstDataLine) {
std::getline(file, field);
}
fieldCount = 0;
// Object designator string
std::getline(file, name, ',');
if (_startRenderIdx > 0 && _startRenderIdx == (csvLine - 1) && _sizeRender == 1) {
formatObjectName(name);
LINFO(fmt::format("Set render block to start at object {}", name));
}
fieldCount++;
// Epoch
if (!std::getline(file, field, ',')) {
throw std::invalid_argument(
"Unable to read epoch from line" + std::to_string(csvLine + 1)
);
}
keplerElements.epoch = epochFromYMDdSubstring(field);
fieldCount++;
// Eccentricity (unit-less)
if (!std::getline(file, field, ',')) {
throw std::invalid_argument(
"Unable to read eccentricity from line" + std::to_string(csvLine + 1)
);
}
keplerElements.eccentricity = std::stod(field);
fieldCount++;
// Semi-major axis (astronomical units - au)
if (!std::getline(file, field, ',')) {
throw std::invalid_argument(
"Unable to read semi-major axis from line" + std::to_string(csvLine + 1)
);
}
keplerElements.semiMajorAxis = std::stod(field);
keplerElements.semiMajorAxis *= convertAuToKm;
fieldCount++;
// Inclination (degrees)
if (!std::getline(file, field, ',')) {
throw std::invalid_argument(
"Unable to read inclination from line" + std::to_string(csvLine + 1)
);
}
keplerElements.inclination = importAngleValue(field);
fieldCount++;
// Longitude of ascending node (degrees)
if (!std::getline(file, field, ',')) {
throw std::invalid_argument(
"Unable to read ascending node from line" + std::to_string(csvLine + 1)
);
}
keplerElements.ascendingNode = importAngleValue(field);
fieldCount++;
// Argument of Periapsis (degrees)
if (!std::getline(file, field, ',')) {
throw std::invalid_argument(
"Unable to read arg of periapsis from line" + std::to_string(csvLine + 1)
);
}
keplerElements.argumentOfPeriapsis = importAngleValue(field);
fieldCount++;
// Mean Anomaly (degrees)
if (!std::getline(file, field, ',')) {
throw std::invalid_argument(
"Unable to read mean anomaly from line" + std::to_string(csvLine + 1)
);
}
keplerElements.meanAnomaly = importAngleValue(field);
fieldCount++;
// Period (days)
if (!std::getline(file, field)) {
throw std::invalid_argument(
"Unable to read period from line" + std::to_string(csvLine + 1)
);
}
keplerElements.period = std::stod(field);
keplerElements.period *= convertDaysToSecs;
fieldCount++;
_data.push_back(keplerElements);
_sbNames.push_back(name);
const double scale = static_cast<double>(_segmentQuality) * 10.0;
_segmentSize.push_back(
static_cast<size_t>(scale + (scale / pow(1 - keplerElements.eccentricity, 1.2)))
);
}
} // namespace openspace

View File

@@ -27,14 +27,13 @@
#include <modules/space/rendering/renderableconstellationbounds.h>
#include <modules/space/rendering/renderablefluxnodes.h>
#include <modules/space/rendering/renderablehabitablezone.h>
#include <modules/space/rendering/renderableorbitalkepler.h>
#include <modules/space/rendering/renderablerings.h>
#include <modules/space/rendering/renderablesatellites.h>
#include <modules/space/rendering/renderablesmallbody.h>
#include <modules/space/rendering/renderablestars.h>
#include <modules/space/rendering/renderabletravelspeed.h>
#include <modules/space/translation/keplertranslation.h>
#include <modules/space/translation/spicetranslation.h>
#include <modules/space/translation/tletranslation.h>
#include <modules/space/translation/gptranslation.h>
#include <modules/space/translation/horizonstranslation.h>
#include <modules/space/rotation/spicerotation.h>
#include <openspace/documentation/documentation.h>
@@ -83,8 +82,7 @@ void SpaceModule::internalInitialize(const ghoul::Dictionary& dictionary) {
fRenderable->registerClass<RenderableFluxNodes>("RenderableFluxNodes");
fRenderable->registerClass<RenderableHabitableZone>("RenderableHabitableZone");
fRenderable->registerClass<RenderableRings>("RenderableRings");
fRenderable->registerClass<RenderableSatellites>("RenderableSatellites");
fRenderable->registerClass<RenderableSmallBody>("RenderableSmallBody");
fRenderable->registerClass<RenderableOrbitalKepler>("RenderableOrbitalKepler");
fRenderable->registerClass<RenderableStars>("RenderableStars");
fRenderable->registerClass<RenderableTravelSpeed>("RenderableTravelSpeed");
@@ -94,7 +92,7 @@ void SpaceModule::internalInitialize(const ghoul::Dictionary& dictionary) {
fTranslation->registerClass<KeplerTranslation>("KeplerTranslation");
fTranslation->registerClass<SpiceTranslation>("SpiceTranslation");
fTranslation->registerClass<TLETranslation>("TLETranslation");
fTranslation->registerClass<GPTranslation>("GPTranslation");
fTranslation->registerClass<HorizonsTranslation>("HorizonsTranslation");
ghoul::TemplateFactory<Rotation>* fRotation =
@@ -120,13 +118,12 @@ std::vector<documentation::Documentation> SpaceModule::documentations() const {
RenderableFluxNodes::Documentation(),
RenderableHabitableZone::Documentation(),
RenderableRings::Documentation(),
RenderableSatellites::Documentation(),
RenderableSmallBody::Documentation(),
RenderableOrbitalKepler::Documentation(),
RenderableStars::Documentation(),
RenderableTravelSpeed::Documentation(),
SpiceRotation::Documentation(),
SpiceTranslation::Documentation(),
TLETranslation::Documentation()
GPTranslation::Documentation()
};
}
@@ -135,7 +132,8 @@ scripting::LuaLibrary SpaceModule::luaLibrary() const {
"space",
{
codegen::lua::ConvertFromRaDec,
codegen::lua::ConvertToRaDec
codegen::lua::ConvertToRaDec,
codegen::lua::ReadKeplerFile
}
};
}

View File

@@ -22,6 +22,8 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include "kepler.h"
namespace {
/**
@@ -77,6 +79,43 @@ namespace {
return { raDecPair.first, raDecPair.second, deg.z };
}
[[codegen::luawrap]]
std::vector<ghoul::Dictionary> readKeplerFile(std::filesystem::path p, std::string type)
{
openspace::kepler::Format f;
if (type == "TLE") {
f = openspace::kepler::Format::TLE;
}
else if (type == "OMM") {
f = openspace::kepler::Format::OMM;
}
else if (type == "SBDB") {
f = openspace::kepler::Format::SBDB;
}
else {
throw ghoul::lua::LuaError(fmt::format("Unsupported format '{}'", type));
}
std::vector<openspace::kepler::Parameters> params = openspace::kepler::readFile(p, f);
std::vector<ghoul::Dictionary> res;
res.reserve(params.size());
for (const openspace::kepler::Parameters& param : params) {
ghoul::Dictionary d;
d.setValue("Name", param.name);
d.setValue("ID", param.id);
d.setValue("inclination", param.inclination);
d.setValue("SemiMajorAxis", param.semiMajorAxis);
d.setValue("AscendingNode", param.ascendingNode);
d.setValue("Eccentricity", param.eccentricity);
d.setValue("ArgumentOfPeriapsis", param.argumentOfPeriapsis);
d.setValue("MeanAnomaly", param.meanAnomaly);
d.setValue("Epoch", param.epoch);
d.setValue("Period", param.period);
res.push_back(d);
}
return res;
}
#include "spacemodule_lua_codegen.cpp"
} // namespace

View File

@@ -28,7 +28,7 @@
#include <openspace/util/task.h>
#include <openspace/util/time.h>
#include <modules/space/rendering/renderablesatellites.h>
#include <modules/space/rendering/renderableorbitalkepler.h>
#include <modules/space/translation/keplertranslation.h>
@@ -50,8 +50,6 @@ public:
std::string _gridType;
protected:
private:
std::string _rawVolumeOutputPath;
std::string _dictionaryOutputPath;

View File

@@ -1,4 +1,4 @@
/****************************************************************************************
/*****************************************************************************************
* *
* OpenSpace *
* *
@@ -22,50 +22,71 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_SPACE___RENDERABLESMALLBODY___H__
#define __OPENSPACE_MODULE_SPACE___RENDERABLESMALLBODY___H__
#include <modules/space/translation/gptranslation.h>
#include <modules/space/rendering/renderableorbitalkepler.h>
#include <openspace/rendering/renderable.h>
#include <modules/space/kepler.h>
#include <openspace/documentation/verifier.h>
#include <filesystem>
#include <optional>
#include <modules/base/rendering/renderabletrail.h>
#include <modules/space/translation/keplertranslation.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/scalar/uintproperty.h>
#include <ghoul/glm.h>
#include <ghoul/misc/objectmanager.h>
#include <ghoul/opengl/programobject.h>
namespace {
struct [[codegen::Dictionary(GPTranslation)]] Parameters {
// Specifies the filename of the general pertubation file
std::filesystem::path file;
enum class [[codegen::map(openspace::kepler::Format)]] Format {
// A NORAD-style Two-Line element
TLE,
// Orbit Mean-Elements Message in the KVN notation
OMM,
// JPL's Small Bodies Database
SBDB
};
// The file format that is contained in the file
Format format;
// Specifies the element within the file that should be used in case the file
// provides multiple general pertubation elements. Defaults to 1.
std::optional<int> element [[codegen::greater(0)]];
};
#include "gptranslation_codegen.cpp"
} // namespace
namespace openspace {
namespace documentation { struct Documentation; }
documentation::Documentation GPTranslation::Documentation() {
return codegen::doc<Parameters>("space_transform_gp");
}
class RenderableSmallBody : public RenderableOrbitalKepler {
public:
RenderableSmallBody(const ghoul::Dictionary& dictionary);
static documentation::Documentation Documentation();
GPTranslation::GPTranslation(const ghoul::Dictionary& dictionary) {
const Parameters p = codegen::bake<Parameters>(dictionary);
if (!std::filesystem::is_regular_file(p.file)) {
throw ghoul::RuntimeError("The provided TLE file must exist");
}
private:
void readOrbitalParamsFromThisLine(bool firstDataLine, int& fieldCount,
unsigned int& csvLine, std::ifstream& file);
virtual void readDataFile(const std::string& filename) override;
void initializeFileReading();
void skipSingleLineInFile(std::ifstream& file);
int element = p.element.value_or(1);
std::vector<std::string> _sbNames;
std::function<void()> _updateContiguousModeSelect;
std::function<void()> _updateRenderUpperLimitSelect;
std::vector<kepler::Parameters> parameters = kepler::readFile(
p.file,
codegen::map<kepler::Format>(p.format)
);
if (parameters.size() < element) {
throw ghoul::RuntimeError(fmt::format(
"Requested element {} but only {} are available", element, parameters.size()
));
}
/// The index array that is potentially used in the draw call. If this is empty, no
/// element draw call is used.
std::vector<unsigned int> _indexBufferData;
properties::BoolProperty _contiguousMode;
properties::UIntProperty _upperLimit;
properties::Property::OnChangeHandle _contiguousModeCallbackhandle;
properties::Property::OnChangeHandle _upperLimitCallbackHandle;
};
kepler::Parameters param = parameters[element - 1];
setKeplerElements(
param.eccentricity,
param.semiMajorAxis,
param.inclination,
param.ascendingNode,
param.argumentOfPeriapsis,
param.meanAnomaly,
param.period,
param.epoch
);
}
} // namespace openspace
#endif // __OPENSPACE_MODULE_SPACE___RENDERABLESMALLBODY___H__

View File

@@ -22,37 +22,38 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_SPACE___RENDERABLESATELLITES___H__
#define __OPENSPACE_MODULE_SPACE___RENDERABLESATELLITES___H__
#ifndef __OPENSPACE_MODULE_SPACE___GPTRANSLATION___H__
#define __OPENSPACE_MODULE_SPACE___GPTRANSLATION___H__
#include <modules/space/rendering/renderableorbitalkepler.h>
#include <openspace/rendering/renderable.h>
#include <modules/base/rendering/renderabletrail.h>
#include <modules/space/translation/keplertranslation.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/scalar/uintproperty.h>
#include <ghoul/glm.h>
#include <ghoul/misc/objectmanager.h>
#include <ghoul/opengl/programobject.h>
namespace openspace {
namespace documentation { struct Documentation; }
class RenderableSatellites : public RenderableOrbitalKepler {
/**
* A specialization of the KeplerTranslation that utilizes general pertubation file
* formats to extracts the Keplerian elements
*/
class GPTranslation : public KeplerTranslation {
public:
RenderableSatellites(const ghoul::Dictionary& dictionary);
virtual void readDataFile(const std::string& filename) override;
static documentation::Documentation Documentation();
void initializeFileReading();
/**
* Constructor for the GPTranslation class. The \p dictionary must contain a key for
* the file that contains the general pertubation information as well as the file
* format that is to be used.
*
* \param The ghoul::Dictionary that contains the information for this TLETranslation
*/
explicit GPTranslation(const ghoul::Dictionary& dictionary);
private:
void skipSingleEntryInFile(std::ifstream& file);
const unsigned int nLineEntriesPerSatellite = 3;
/**
* Method returning the openspace::Documentation that describes the ghoul::Dictionary
* that can be passed to the constructor.
*
* \return The openspace::Documentation that describes the ghoul::Dicitonary that can
* be passed to the constructor
*/
static documentation::Documentation Documentation();
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_SPACE___RENDERABLESATELLITES___H__
#endif // __OPENSPACE_MODULE_SPACE___GPTRANSLATION___H__

View File

@@ -71,7 +71,7 @@ public:
glm::dvec3 position(const UpdateData& data) const override;
/**
* Method returning the openspace::Documentation that describes the ghoul::Dictinoary
* Method returning the openspace::Documentation that describes the ghoul::Dictionary
* that can be passed to the constructor.
*
* \return The openspace::Documentation that describes the ghoul::Dicitonary that can

View File

@@ -1,364 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2022 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/space/translation/tletranslation.h>
#include <openspace/documentation/verifier.h>
#include <ghoul/filesystem/file.h>
#include <ghoul/filesystem/filesystem.h>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <optional>
#include <vector>
namespace {
// The list of leap years only goes until 2056 as we need to touch this file then
// again anyway ;)
const std::vector<int> LeapYears = {
1956, 1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992, 1996,
2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036, 2040,
2044, 2048, 2052, 2056
};
// Count the number of full days since the beginning of 2000 to the beginning of
// the parameter 'year'
int countDays(int year) {
// Find the position of the current year in the vector, the difference
// between its position and the position of 2000 (for J2000) gives the
// number of leap years
constexpr int Epoch = 2000;
constexpr int DaysRegularYear = 365;
constexpr int DaysLeapYear = 366;
if (year == Epoch) {
return 0;
}
// Get the position of the most recent leap year
const auto lb = std::lower_bound(LeapYears.begin(), LeapYears.end(), year);
// Get the position of the epoch
const auto y2000 = std::find(LeapYears.begin(), LeapYears.end(), Epoch);
// The distance between the two iterators gives us the number of leap years
const int nLeapYears = static_cast<int>(std::abs(std::distance(y2000, lb)));
const int nYears = std::abs(year - Epoch);
const int nRegularYears = nYears - nLeapYears;
// Get the total number of days as the sum of leap years + non leap years
const int result = nRegularYears * DaysRegularYear + nLeapYears * DaysLeapYear;
return result;
}
// Returns the number of leap seconds that lie between the {year, dayOfYear}
// time point and { 2000, 1 }
int countLeapSeconds(int year, int dayOfYear) {
// Find the position of the current year in the vector; its position in
// the vector gives the number of leap seconds
struct LeapSecond {
int year;
int dayOfYear;
bool operator<(const LeapSecond& rhs) const {
return std::tie(year, dayOfYear) < std::tie(rhs.year, rhs.dayOfYear);
}
};
const LeapSecond Epoch = { 2000, 1 };
// List taken from: https://www.ietf.org/timezones/data/leap-seconds.list
static const std::vector<LeapSecond> LeapSeconds = {
{ 1972, 1 },
{ 1972, 183 },
{ 1973, 1 },
{ 1974, 1 },
{ 1975, 1 },
{ 1976, 1 },
{ 1977, 1 },
{ 1978, 1 },
{ 1979, 1 },
{ 1980, 1 },
{ 1981, 182 },
{ 1982, 182 },
{ 1983, 182 },
{ 1985, 182 },
{ 1988, 1 },
{ 1990, 1 },
{ 1991, 1 },
{ 1992, 183 },
{ 1993, 182 },
{ 1994, 182 },
{ 1996, 1 },
{ 1997, 182 },
{ 1999, 1 },
{ 2006, 1 },
{ 2009, 1 },
{ 2012, 183 },
{ 2015, 182 },
{ 2017, 1 }
};
// Get the position of the last leap second before the desired date
LeapSecond date { year, dayOfYear };
const auto it = std::lower_bound(LeapSeconds.begin(), LeapSeconds.end(), date);
// Get the position of the Epoch
const auto y2000 = std::lower_bound(
LeapSeconds.begin(),
LeapSeconds.end(),
Epoch
);
// The distance between the two iterators gives us the number of leap years
const int nLeapSeconds = static_cast<int>(std::abs(std::distance(y2000, it)));
return nLeapSeconds;
}
double epochFromSubstring(const std::string& epochString) {
// The epochString is in the form:
// YYDDD.DDDDDDDD
// With YY being the last two years of the launch epoch, the first DDD the day
// of the year and the remaning a fractional part of the day
// The main overview of this function:
// 1. Reconstruct the full year from the YY part
// 2. Calculate the number of days since the beginning of the year
// 3. Convert the number of days to a number of seconds
// 4. Get the number of leap seconds since January 1st, 2000 and remove them
// 5. Adjust for the fact the epoch starts on 1st Januaray at 12:00:00, not
// midnight
// According to https://celestrak.com/columns/v04n03/
// Apparently, US Space Command sees no need to change the two-line element
// set format yet since no artificial earth satellites existed prior to 1957.
// By their reasoning, two-digit years from 57-99 correspond to 1957-1999 and
// those from 00-56 correspond to 2000-2056. We'll see each other again in 2057!
// 1. Get the full year
std::string yearPrefix = [y = epochString.substr(0, 2)](){
int year = std::atoi(y.c_str());
return year >= 57 ? "19" : "20";
}();
const int year = std::atoi((yearPrefix + epochString.substr(0, 2)).c_str());
const int daysSince2000 = countDays(year);
// 2.
double daysInYear = std::atof(epochString.substr(2).c_str());
// 3
using namespace std::chrono;
const int SecondsPerDay = static_cast<int>(seconds(hours(24)).count());
//Need to subtract 1 from daysInYear since it is not a zero-based count
const double nSecondsSince2000 = (daysSince2000 + daysInYear - 1) * SecondsPerDay;
// 4
// We need to remove additionbal leap seconds past 2000 and add them prior to
// 2000 to sync up the time zones
const double nLeapSecondsOffset = -countLeapSeconds(
year,
static_cast<int>(std::floor(daysInYear))
);
// 5
const double nSecondsEpochOffset = static_cast<double>(
seconds(hours(12)).count()
);
// Combine all of the values
const double epoch = nSecondsSince2000 + nLeapSecondsOffset - nSecondsEpochOffset;
return epoch;
}
double calculateSemiMajorAxis(double meanMotion) {
constexpr double GravitationalConstant = 6.6740831e-11;
constexpr double MassEarth = 5.9721986e24;
constexpr double muEarth = GravitationalConstant * MassEarth;
// Use Kepler's 3rd law to calculate semimajor axis
// a^3 / P^2 = mu / (2pi)^2
// <=> a = ((mu * P^2) / (2pi^2))^(1/3)
// with a = semimajor axis
// P = period in seconds
// mu = G*M_earth
double period = std::chrono::seconds(std::chrono::hours(24)).count() / meanMotion;
constexpr double pisq = glm::pi<double>() * glm::pi<double>();
double semiMajorAxis = pow((muEarth * period*period) / (4 * pisq), 1.0 / 3.0);
// We need the semi major axis in km instead of m
return semiMajorAxis / 1000.0;
}
struct [[codegen::Dictionary(TLETranslation)]] Parameters {
// Specifies the filename of the Two-Line-Element file
std::string file;
// Specifies the line number within the file where the group of 3 TLE lines begins
// (1-based). Defaults to 1
std::optional<int> lineNumber [[codegen::greater(0)]];
};
#include "tletranslation_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation TLETranslation::Documentation() {
return codegen::doc<Parameters>("space_transform_tle");
}
TLETranslation::TLETranslation(const ghoul::Dictionary& dictionary) {
const Parameters p = codegen::bake<Parameters>(dictionary);
int lineNum = p.lineNumber.value_or(1);
readTLEFile(p.file, lineNum);
}
void TLETranslation::readTLEFile(const std::string& filename, int lineNum) {
ghoul_assert(std::filesystem::is_regular_file(filename), "The filename must exist");
std::ifstream file;
file.exceptions(std::ofstream::failbit | std::ofstream::badbit);
file.open(filename);
// All of the Kepler element information
struct {
double inclination = 0.0;
double semiMajorAxis = 0.0;
double ascendingNode = 0.0;
double eccentricity = 0.0;
double argumentOfPeriapsis = 0.0;
double meanAnomaly = 0.0;
double meanMotion = 0.0;
double epoch = 0.0;
} keplerElements;
std::string line;
// Loop through and throw out lines until getting to the linNum of interest
for (int i = 1; i < lineNum; ++i) {
std::getline(file, line);
}
std::getline(file, line); // Throw out the TLE title line (1st)
std::getline(file, line); // Get line 1 of TLE format
if (line[0] == '1') {
// First line
// Field Columns Content
// 1 01-01 Line number
// 2 03-07 Satellite number
// 3 08-08 Classification (U = Unclassified)
// 4 10-11 International Designator (Last two digits of launch year)
// 5 12-14 International Designator (Launch number of the year)
// 6 15-17 International Designator(piece of the launch) A
// 7 19-20 Epoch Year(last two digits of year)
// 8 21-32 Epoch(day of the year and fractional portion of the day)
// 9 34-43 First Time Derivative of the Mean Motion divided by two
// 10 45-52 Second Time Derivative of Mean Motion divided by six
// 11 54-61 BSTAR drag term(decimal point assumed)[10] - 11606 - 4
// 12 63-63 The "Ephemeris type"
// 13 65-68 Element set number.Incremented when a new TLE is generated
// 14 69-69 Checksum (modulo 10)
keplerElements.epoch = epochFromSubstring(line.substr(18, 14));
}
else {
throw ghoul::RuntimeError(fmt::format(
"File {} @ line {} does not have '1' header", filename, lineNum + 1
));
}
std::getline(file, line); // Get line 2 of TLE format
if (line[0] == '2') {
// Second line
// Field Columns Content
// 1 01-01 Line number
// 2 03-07 Satellite number
// 3 09-16 Inclination (degrees)
// 4 18-25 Right ascension of the ascending node (degrees)
// 5 27-33 Eccentricity (decimal point assumed)
// 6 35-42 Argument of perigee (degrees)
// 7 44-51 Mean Anomaly (degrees)
// 8 53-63 Mean Motion (revolutions per day)
// 9 64-68 Revolution number at epoch (revolutions)
// 10 69-69 Checksum (modulo 10)
std::stringstream stream;
stream.exceptions(std::ios::failbit);
// Get inclination
stream.str(line.substr(8, 8));
stream >> keplerElements.inclination;
stream.clear();
// Get Right ascension of the ascending node
stream.str(line.substr(17, 8));
stream >> keplerElements.ascendingNode;
stream.clear();
// Get Eccentricity
stream.str("0." + line.substr(26, 7));
stream >> keplerElements.eccentricity;
stream.clear();
// Get argument of periapsis
stream.str(line.substr(34, 8));
stream >> keplerElements.argumentOfPeriapsis;
stream.clear();
// Get mean anomaly
stream.str(line.substr(43, 8));
stream >> keplerElements.meanAnomaly;
stream.clear();
// Get mean motion
stream.str(line.substr(52, 11));
stream >> keplerElements.meanMotion;
}
else {
throw ghoul::RuntimeError(fmt::format(
"File {} @ line {} does not have '2' header", filename, lineNum + 2
));
}
file.close();
// Calculate the semi major axis based on the mean motion using kepler's laws
keplerElements.semiMajorAxis = calculateSemiMajorAxis(keplerElements.meanMotion);
// Converting the mean motion (revolutions per day) to period (seconds per revolution)
using namespace std::chrono;
double period = seconds(hours(24)).count() / keplerElements.meanMotion;
setKeplerElements(
keplerElements.eccentricity,
keplerElements.semiMajorAxis,
keplerElements.inclination,
keplerElements.ascendingNode,
keplerElements.argumentOfPeriapsis,
keplerElements.meanAnomaly,
period,
keplerElements.epoch
);
}
} // namespace openspace

View File

@@ -1,83 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2022 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_SPACE___TLETRANSLATION___H__
#define __OPENSPACE_MODULE_SPACE___TLETRANSLATION___H__
#include <modules/space/translation/keplertranslation.h>
namespace openspace {
/**
* A specialization of the KeplerTranslation that extracts the Keplerian elements from a
* two-line element as described by the US Space Command
* https://celestrak.com/columns/v04n03
* The ghoul::Dictionary passed to the constructor must contain the pointer to a file that
* will be read.
*/
class TLETranslation : public KeplerTranslation {
public:
struct FileFormatError : public ghoul::RuntimeError {
explicit FileFormatError(std::string offense);
std::string offense;
};
/**
* Constructor for the TLETranslation class. The \p dictionary must contain a key for
* the file that contains the TLE information. The ghoul::Dictionary will be tested
* against the openspace::Documentation returned by Documentation.
* \param The ghoul::Dictionary that contains the information for this TLETranslation
(*/
TLETranslation(const ghoul::Dictionary& dictionary = ghoul::Dictionary());
/**
* Method returning the openspace::Documentation that describes the ghoul::Dictinoary
* that can be passed to the constructor.
* \return The openspace::Documentation that describes the ghoul::Dicitonary that can
* be passed to the constructor
*/
static documentation::Documentation Documentation();
private:
/**
* Reads the provided TLE file and calles the KeplerTranslation::setKeplerElments
* method with the correct values. If \p filename is a valid TLE file but contains
* disallowed values (see KeplerTranslation::setKeplerElements), a
* KeplerTranslation::RangeError is thrown.
*
* \param filename The path to the file that contains the TLE file.
* \param lineNum The line number in the file where the set of 3 TLE lines starts
*
* \throw std::system_error if the TLE file is malformed (does not contain at least
* two lines that start with \c 1 and \c 2.
* \throw KeplerTranslation::RangeError If the Keplerian elements are outside of
* the valid range supported by Kepler::setKeplerElements
* \pre The \p filename must exist
*/
void readTLEFile(const std::string& filename, int lineNum);
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_SPACE___TLETRANSLATION___H__

View File

@@ -875,14 +875,14 @@ void SessionRecording::saveScriptKeyframeToTimeline(std::string script) {
bool SessionRecording::doesStartWithSubstring(const std::string& s,
const std::string& matchSubstr)
{
return (s.substr(0, matchSubstr.length()).compare(matchSubstr) == 0);
return s.substr(0, matchSubstr.length()) == matchSubstr;
}
void SessionRecording::saveScriptKeyframeToPropertiesBaseline(std::string script) {
Timestamps times
= generateCurrentTimestamp3(global::windowDelegate->applicationTime());
size_t indexIntoScriptKeyframesFromMainTimeline
= _keyframesSavePropertiesBaseline_scripts.size();
Timestamps times =
generateCurrentTimestamp3(global::windowDelegate->applicationTime());
size_t indexIntoScriptKeyframesFromMainTimeline =
_keyframesSavePropertiesBaseline_scripts.size();
_keyframesSavePropertiesBaseline_scripts.push_back(std::move(script));
addKeyframeToTimeline(
_keyframesSavePropertiesBaseline_timeline,
@@ -2327,7 +2327,7 @@ std::string SessionRecording::convertFile(std::string filename, int depth) {
// correct version of the file to be converted, then call getLegacy() to recurse
// to the next level down in the legacy subclasses until we get the right
// version, then proceed with conversion from there.
if (fileVersion.compare(fileFormatVersion()) != 0) {
if (fileVersion != fileFormatVersion()) {
//conversionInStream.seekg(conversionInStream.beg);
newFilename = getLegacyConversionResult(filename, depth + 1);
removeTrailingPathSlashes(newFilename);