Satellite: Difference between revisions
Dvb-nomail (talk | contribs) (→) |
(Added section with example for FootprintSettings property) |
||
(46 intermediate revisions by 3 users not shown) | |||
Line 119: | Line 119: | ||
==== ISatelliteFootprint class ==== | ==== ISatelliteFootprint class ==== | ||
Defines a footprint. The properties ViewAngle, TiltAngle and RotateAngle defines the size and position of the footprint. These properties are used to calculate the | Defines a footprint. The properties ViewAngle, TiltAngle and RotateAngle defines the size and position of the footprint. These properties are used to calculate the | ||
dimensions of the footprint with the Satellite.CalculateFootprintDimensions method. Actual geo-points for the footprint is then calculated with SatelliteFootprintRenderer.CreateFootprintPoints. | dimensions of the footprint with the Satellite.CalculateFootprintDimensions method. Actual geo-points for the footprint is then calculated with SatelliteFootprintRenderer.CreateFootprintPoints. | ||
===== FootprintSettings property ===== | |||
Use property FootprintSettings to define how the footprint is rendered. The individual settings can be added/subtracted using logical operators. See code example below.<syntaxhighlight lang="c#"> | |||
public enum FootprintSettings | |||
{ | |||
/// <summary> | |||
/// No hub lines are rendered | |||
/// </summary> | |||
None = 0x1, | |||
/// <summary> | |||
/// The north hub line is rendered. | |||
/// </summary> | |||
Forward = 0x2, | |||
/// <summary> | |||
/// The east hub line is rendered. | |||
/// </summary> | |||
Right = 0x4, | |||
/// <summary> | |||
/// The south hub line is rendered. | |||
/// </summary> | |||
Back = 0x8, | |||
/// <summary> | |||
/// The west hub line is rendered. | |||
/// </summary> | |||
Left = 0x10, | |||
/// <summary> | |||
/// The hub lines are rendered geographical correct. | |||
/// </summary> | |||
GeoCorrect = 0x20, | |||
} | |||
// Example use for drawing all hub lines. | |||
footprint.FootprintSettings = FootprintSettings.Forward | FootprintSettings.Right | FootprintSettings.Back | FootprintSettings.Left; | |||
</syntaxhighlight> | |||
=== ISatelliteLineOfSight class === | === ISatelliteLineOfSight class === | ||
Line 125: | Line 159: | ||
=== ISatelliteTimeTicks class === | === ISatelliteTimeTicks class === | ||
This add time ticks to the trajectory rendering. It is implemented by the Satellite class so that these settings can be handled before an actual trajectory is added to the satellite object. | This add time ticks to the trajectory rendering. It is implemented by the Satellite class so that these settings can be handled before an actual trajectory is added to the satellite object. It is also implemented by the SatelliteLayerViewModel class, ticks values set on the viewmodel will be used on all satellites which have no set tick values. | ||
== How to create satellite objects == | == How to create satellite objects == | ||
Line 196: | Line 230: | ||
This class updates the satellite system time. | This class updates the satellite system time. | ||
=== | === SatellitesStore === | ||
This | This class handles the satellite system time and automatically updating the positions. | ||
== Historic satellites == | == Historic satellites == | ||
A satellite's TLE is updated over time since the trajectory changes slightly for each turn. It might therefore be of interest to render the different trajectories and positions for a satellite.The historic instances of the satellite will all have the same satellite ID so this property can not be used to differentiate between the current satellite object and the historic satellite objects. | A satellite's TLE is updated over time since the trajectory changes slightly for each turn. It might therefore be of interest to render the different trajectories and positions for a satellite.The historic instances of the satellite will all have the same satellite ID so this property can not be used directly to differentiate between the current satellite object and the historic satellite objects. Instead the historic SatelliteId is set to the original satellite's ID plus an historic offset and an index number equal to number of historic satellites. | ||
=== ISatelliteHistory class === | === ISatelliteHistory class === | ||
Line 337: | Line 261: | ||
/// </summary> | /// </summary> | ||
new bool IsSatelliteClass { get; } | new bool IsSatelliteClass { get; } | ||
/// <summary> | |||
/// Returns the non-historic SatelliteId | |||
/// </summary> | |||
int RegularSatelliteId { get; } | |||
} | } | ||
</source> | </source> | ||
The IsSatelliteClass will be false for historic satellites and true for regular satellite. For an historic satellite SatelliteId = RegularSatelliteId + HistoryOffset + HistoryIndex. | The IsSatelliteClass will be false for historic satellites and true for regular satellite. For an historic satellite SatelliteId = RegularSatelliteId + HistoryOffset + HistoryIndex. This value can easily be retrieved by RegularSatelliteId. | ||
=== Create a historic satellite object === | === Create a historic satellite object === | ||
Line 360: | Line 289: | ||
</source> | </source> | ||
This test code adds | This test code is more realistic since it adds the historic satellite to the viewmodel: | ||
<source lang="c#"> | <source lang="c#"> | ||
var satellite = new GeoFramework.SatelliteLayer.Satellite(); | var satellite = new GeoFramework.SatelliteLayer.Satellite(); | ||
Line 386: | Line 315: | ||
=== Observation points in the satellite layer === | === Observation points in the satellite layer === | ||
The ISatelliteLineOfSight adds line-of-sight functionality like adding and removing points to the satellite layer. | The ISatelliteLineOfSight adds line-of-sight functionality like adding and removing observation points to the satellite layer. | ||
This code adds an observation point to the satellite layer. The point will track the given satellite. | This code adds an observation point to the satellite layer. The point will track the given satellite. | ||
Line 414: | Line 343: | ||
==== SatelliteInSight ==== | ==== SatelliteInSight ==== | ||
Checks if a specific satellite is tracked by any line-of-sight points or if a specific line-of-sight point tracks a specific satellite. | Checks if a specific satellite is tracked by any line-of-sight points or if a specific line-of-sight point tracks a specific satellite. | ||
=== Defining an observation point === | === Defining an observation point === | ||
Line 605: | Line 533: | ||
</source> | </source> | ||
=== Check if observation points are in any satellite's footprint === | |||
The method ISatelliteLineOfSight.ComputeInFootprints starts the asynchronous process of checking if an observation point is within any footprints. | |||
This test code shows how to start the footprint process | |||
<source lang="c#"> | |||
.... | |||
viewmodel.ComputeInFootprints(true); | |||
... | |||
Assert.IsTrue(viewmodel.HasInsideFootprintComputations); | |||
var results = viewmodel.LineOfSightViewPoint(id); | |||
var status = results.Status.First(); | |||
var computations = status.Value.FootprintComputations.Computations; | |||
.... | |||
</source> | |||
=== When are satellites inside footprints === | |||
The method ISatelliteLineOfSight.ComputeWhenInFootprints starts the asynchronous process of checking when the satellite is inside a footprint. | |||
This test code shows how to start the footprint process | |||
<source lang="c#"> | |||
.... | |||
viewmodel.ComputeWhenInFootprints(startTime, stopTime, steptTime, false); | |||
... | |||
Assert.IsTrue(viewmodel.HasInsideFootprintComputations); | |||
var results = viewmodel.LineOfSightViewPoint(id); | |||
var status = results.Status.First(); | |||
var computations = status.Value.FootprintComputations.Computations; | |||
.... | |||
</source> | |||
=== Analyze results as XML === | |||
The method ISatelliteLineOfSight.StatusAsXml returns the analyze results as XML. The XML content is dependent of type of analyze process. | |||
This test code shows how to get the results as XML | |||
<source lang="c#"> | |||
.... | |||
viewmodel.ComputeInSight(true); | |||
... | |||
var xml = viewmodel.StatusAsXml(); | |||
.... | |||
</source> | |||
EXample of analyze process as XML: | |||
[[File:Xml analyze.png|frameless|XML]] | |||
== Implement satellite functionality in GDK layer == | |||
The satellites can easily be integrated in a GDK layer since the handling of the satellites are extracted into a dedicated classes. Automatic updating of all satellites is also extracted from the GDK layer. | |||
=== Handling satellite objects === | |||
The satellites are handled by a dedicated store object that can be shared between several layers, for example 2D and 3D. | |||
==== The satellite store object ==== | |||
The store object can either be created by the viewmodel or created separately and then passed into the viewmodel through the constructor. '''SatellitesStore''' implements the following interfaces: ISatellitesStore, ISatelliteHistories and ISatelliteListeners. | |||
==== Satellite system time ==== | |||
The system time is extracted into a dedicated class, '''SatelliteSystemTimeUpdate''', which is used by the store object. | |||
=== Connecting viewmodel and satellite store === | |||
Automatical update of satellite time and thereby positions are handled by SatellitesStore and SatelliteSystemTimeUpdate, but the viewmodel might need to be informed about the position changes. | |||
==== The listener class ==== | |||
The listener class, '''ISatelliteListeners''', handles which objects that shall be informed about position changes. | |||
==== Position changes information ==== | |||
The class, '''ISatellitesUpdate''', is used to relay information about position changes. | |||
=== Connecting everything === | |||
The following extracted code shows how everything is connected in the SatelliteLayerViewModel. | |||
<source lang="c#"> | |||
public partial class SatelliteLayerViewModel : ... ISatelliteLayerViewModel, ISatellitesUpdate, ... | |||
{ | |||
private readonly SatellitesStore _satellitesStore; | |||
//constructor without store object, layer uses its own store object | |||
public SatelliteLayerViewModel(IGeoUnitsSetting geoUnitsSetting, IGeoContext geoContext, IGeoNavigator geoNavigator, SatelliteLayerViewFactory satelliteObjectLayerViewFactory) .... | |||
{ | |||
... | |||
_satellitesStore = new SatellitesStore(); | |||
_satellitesStore.AddListener(this); | |||
} | |||
//store object is already created, use this object | |||
public SatelliteLayerViewModel(ISatellitesStore satelliteStore, IGeoUnitsSetting geoUnitsSetting, .... | |||
{ | |||
... | |||
_satellitesStore = (SatellitesStore) satelliteStore; | |||
_satellitesStore.AddListener(this); | |||
} | |||
//starts automatic updating | |||
public bool StartAutomaticSatellitePositioning(double updateFrequency) | |||
{ | |||
updateFrequency = updateFrequency < 0.3 ? 0.3 : updateFrequency; | |||
_satellitesStore.SatelliteSystemTimeUpdate.ResetAutomaticTimerBaseTime(); | |||
_satellitesStore.SatelliteSystemTimeUpdate.SetAndStartTimerTime(updateFrequency * 1000.0, SatelliteLayerSettings, GeoContext); | |||
IsAutomaticPositioning = true; | |||
} | |||
//stop updating | |||
public bool StopAutomaticSatellitePositioning() | |||
{ | |||
_satellitesStore.SatelliteSystemTimeUpdate.Stop(); | |||
IsAutomaticPositioning = false; | |||
} | |||
public override void Update() | |||
{ | |||
... | |||
//must update frequency if scale is changed | |||
if (ViewportExtentChanged && IsAutomaticPositioning ) | |||
{ | |||
_satellitesStore.SatelliteSystemTimeUpdate.SetAndStartTimerTime(SatelliteLayerSettings, GeoContext); | |||
} | |||
... | |||
} | |||
//ISatellitesUpdate implementations | |||
//callback before satellites are automatically updated | |||
public void BeforeSatellitesUpdate() | |||
{ | |||
LockRedraw = true; | |||
} | |||
//callback after satellites are automatically updated | |||
public void AfterSatellitesUpdate() | |||
{ | |||
LockRedraw = false; | |||
var trackedSatellite = _satellitesStore.TrackedSatellite(TrackSatellite); | |||
if (trackedSatellite != null) | |||
CenterToPosition(trackedSatellite as Satellite); | |||
} | |||
} | |||
</source> | |||
== Satellites in 3D == | |||
Satellites can also be displayed in 3D. This is handled by the class '''TPG.Globe3D.SatelliteLayer'''. When using 2D and 3D in the same application you should make a SatellitesStore outside the layers and inject it in both 2D and 3D layers. | |||
=== Initializing the 3D satellite layer === | |||
The following code example sets up both 2D and 3D satellite layers from the same satellite store. | |||
<source lang="c#"> | |||
// Common satellites store | |||
_satellites = new SatellitesStore(); | |||
// 2D layer | |||
var satelliteLayerViewFactory = new SatelliteLayerViewFactory(_drawObjectLayerFactory.SymbolsStore); | |||
_satelliteLayerViewModel = new SatelliteLayerViewModel(_satellites, _geoUnitsSetting, geoContext, geoNavigator, satelliteLayerViewFactory); | |||
_mapViewModel.Layers.Add(_satelliteLayerViewModel); | |||
// 3D layer | |||
_satelliteLayer3D = new SatelliteLayer(_map3DViewModel, _satellites, drawObjectDataFactory, stylingXml); | |||
</source> | |||
=== Adding and updating satellites === | |||
The 3D layer will display all visible '''ISatellite''' objects in the '''SatellitesStore''' with the same styling parameters as are used in 2D. The 3D graphics will be updated upon each ''UpdateSatellites()'' call in the '''SatellitesStore''', so if you for example control this through an '''ISatelliteSystemTimeUpdate''' interface, the satellites will be continuously updated. | |||
If you change any of the styling or other visible parameters of the satellites, you may need to update explicitly, either through a call to ''SatellitesStore.UpdateSatellites(true)'' or by calling ''SatelliteLayer.UpdateSatellites()'' directly. | |||
=== Interacting with satellites in 3D === | |||
The satellite icons are 3D items of the same type as tracks and point symbols, and you may use the ''IGlobe3DViewModel.ClickedItem'' event to respond to mouse clicks on these. Each satellite item will have an '''ItemId''' consisting of the '''SatelliteLayer''' point list id (accessible through ''ISatelliteLayer.PointListId'') and the satellite ID as ''InstanceID''. | |||
The following example illustrates how you can toggle the 3D label on a clicked satellite: | |||
<source lang="c#"> | |||
_map3DViewModel.ClickedItemEvent += clickedSatellite; | |||
private void clickedSatellite(object sender, ClickedItemEventArgs args) | |||
{ | |||
// Only react on right-click | |||
if (args.MouseButtonEventArgs.ChangedButton != MouseButton.Right) | |||
return; | |||
var itemId = args.ItemId; | |||
if (itemId.ListId == _satelliteLayer3D.PointListId) | |||
{ | |||
var satId = int.Parse(itemId.InstanceId); | |||
var sat = _satellites.GetSatellite(satId); | |||
sat.DrawLabels = !sat.DrawLabels; | |||
_satellites.UpdateSatellites(true); | |||
} | |||
} | |||
</source> | |||
[[Category:Satellite]] |
Latest revision as of 12:23, 7 November 2023
The GDK layer "Satellite" defined in the namespace TPG.GeoFramework.SatelliteLayer handles satellites which therefore easily can be separated from other draw types. Satellite objects in this layer are controlled through the TPG.GeoFramework.SatelliteLayer.SatelliteLayerViewModel class.
The TLE string
A satellite position and its trajectory is computed from a TLE string. The format of the TLE is described in https://www.space-track.org/documentation#/tle. The TLE for a satellite can for example be acquired from www.space-track.org using the TPG.Satellite.Implementation.SatelliteUtil.GetSpaceTrack method. The content of the TLE will be unique for each satellite and is also adjusted through time to give as accurate position computations as possible.
//Example of TLE text used in tests
public static string TestTle()
{
var tle = "0 ISS (ZARYA)";
tle += "\n1 25544U 98067A 20227.03014521 .00002523 00000-0 53627-4 0 9998";
tle += "\n2 25544 51.6461 62.1977 0001435 30.4470 104.2862 15.49164713240985";
return tle;
}
Convert time to satellite position
The mathematics to convert time to position based on the TLE is done with an external library. Currently the conversion is supported by two different libraries. The functionality is available through the facades TPG.Data.Satellite.Interface.ISatelliteData and TPG.Data.Satellite.Interface.ISatelliteTle. This is done so that adding new libraries can be done without affecting the satellite code.
The creation process for a satellite object, based on given TLE and time, is not available for the end user. This process will automatically select one of the implemented libraries, but it is possible to select a specific library:
//create satellite without selecting library
var satellite1 = new TPG.GeoFramework.SatelliteLayer.Satellite();
//select the first implemented library. Usually this is done through UI
var tle = new SatelliteTle();
var satellite2 = new TPG.GeoFramework.SatelliteLayer.Satellite(tle.Libraries.First());
Implemented TLE libraries
These libraries are free licenses.
The OneSgp4GdkTle library
https://www.nuget.org/packages/One_Sgp4 https://github.com/1manprojects/one_Sgp4
The SgpDotNeTle library
https://www.nuget.org/packages/SGP.NET/ https://github.com/parzivail/SGP.NET
Other possible libraries
The library http://www.zeptomoby.com/satellites/ might be of interest in the future. This library has a very extensive functionality, but has not been added since it has a very high license cost.
What is a satellite?
A satellite object consists of two draw objects; a satellite position object of type symbol and a satellite trajectory object of type line. Both object types are not required, but are usually used. The position shows where the actual satellite is at the given time. The trajectory shows where the satellite has been between a given start and stop time.
Useful internet resources:
https://in-the-sky.org/satmap_worldmap.php
https://www.phiphase.com/wp-content/uploads/2021/10/satellite_coverage-1.pdf
Satellite implementation
The Satellite class implements dedicated interfaces for different satellite functionality.
public class Satellite : ISatellite, ISatelliteTime, ISatelliteDevice, ISatelliteFootprints, ISatelliteLineOfSight, ISatelliteTimeTicks
{
.
.
}
ISatellite class
Properties for basic satellite functionality like position, trajectory, satellite ID, satellite name, visibility etc. The properties for the position and the trajectory are:
ISatellite.ISatellitePosition
The satellite position is handled by the ISatellitePosition class. The property ISatellitePosition.Position contains the (Lat,Lon) position, corresponding time for the position, satellite height at the position, and also the possibility to add additional key,value information for this point.
ISatellite.ISatelliteTrajectory
The satellite trajectory is handled by the ISatelliteTrajectory class. The property ISatelliteTrajectory.Trajectory contains a list of satellite positions which is used to draw the trajectory line. The parameters StartTime and StopTime contains the start and stop time for the trajectory.
ISatelliteTime class
Properties used in time related methods for the satellite object.
ISatelliteDevice class
Properties used in device related computations for satellite object. The most important computation is adjusting the trajectory's start and stop time so that the trajectory covers the actual device extent. This class is normally not used externally.
ISatelliteFootprints class
Adds footprint functionality to a satellite. A satellite can have many footprints with different setup where each footprint has a unique ID.
Example of satellite with several footprints:
This code adds a footprint to a satellite:
var satellite = satelliteLayerViewModel.GetSatellite(satelliteId) as Satellite;
satellite.AddFootprint(new SatelliteFootprint()
{
Id = Guid.NewGuid().ToString(),
ViewAngle = Footprint,
CircleLineColor = LineColor,
HubLineColor = OutlineColor != Colors.Black ? OutlineColor : LineColor,
HubLineThickness = OutlineColor != Colors.Black && TimerSecs < 50 ? TimerSecs : new SatelliteFootprint().HubLineThickness,
CircleLineThickness = LineWidthValue,
FootprintSettings = FootprintSetting(),
LabelPlacement = FootprintSetting(),
TiltAngle = FootprintTiltAngle,
RotateAngle = FootprintRotateAngle,
DrawFootprintAsStripe = FootprintAsStripe,
AccumulateFootprintStripe = AccumulateFootprintStripe,
StripeFillColor = stripeFillColor,
StripeLineColor = LineColor,
StripeFillStyle = FillStyle.Solid
});
In addition the class contains methods to handle footprints like retrieve and remove footprints.
ISatelliteFootprint class
Defines a footprint. The properties ViewAngle, TiltAngle and RotateAngle defines the size and position of the footprint. These properties are used to calculate the dimensions of the footprint with the Satellite.CalculateFootprintDimensions method. Actual geo-points for the footprint is then calculated with SatelliteFootprintRenderer.CreateFootprintPoints.
FootprintSettings property
Use property FootprintSettings to define how the footprint is rendered. The individual settings can be added/subtracted using logical operators. See code example below.
public enum FootprintSettings
{
/// <summary>
/// No hub lines are rendered
/// </summary>
None = 0x1,
/// <summary>
/// The north hub line is rendered.
/// </summary>
Forward = 0x2,
/// <summary>
/// The east hub line is rendered.
/// </summary>
Right = 0x4,
/// <summary>
/// The south hub line is rendered.
/// </summary>
Back = 0x8,
/// <summary>
/// The west hub line is rendered.
/// </summary>
Left = 0x10,
/// <summary>
/// The hub lines are rendered geographical correct.
/// </summary>
GeoCorrect = 0x20,
}
// Example use for drawing all hub lines.
footprint.FootprintSettings = FootprintSettings.Forward | FootprintSettings.Right | FootprintSettings.Back | FootprintSettings.Left;
ISatelliteLineOfSight class
This class has functionality to check if a satellite is visible from a given observation point.
ISatelliteTimeTicks class
This add time ticks to the trajectory rendering. It is implemented by the Satellite class so that these settings can be handled before an actual trajectory is added to the satellite object. It is also implemented by the SatelliteLayerViewModel class, ticks values set on the viewmodel will be used on all satellites which have no set tick values.
How to create satellite objects
The satellite objects are first created and then added to the satellite layer.
Create only a satellite position object
This test code shows how to create a position object:
var satellite = new TPG.GeoFramework.SatelliteLayer.Satellite
{
Tle = TestTle(),
MetadataSetting = (long)SatelliteObject.TleMetadata.All,
RecalculateTrajectoryAlways = FullCurveCalculations
};
Assert.NotNull(satellite);
//calculate the satellite position for this time
var time = new DateTime(2020, 08, 14, 7, 55, 26, DateTimeKind.Utc);
Assert.IsTrue(satellite.CreateSatellite(time));
Assert.AreEqual(1, satellite.Objects.Count);
Assert.IsNotNull(satellite.Objects.First() as ISatellitePosition);
Create a satellite with both position and trajectory objects
This test code shows how to create a complete satellite:
var satellite = new TPG.GeoFramework.SatelliteLayer.Satellite
{
Tle = TestTle(),
MetadataSetting = (long)SatelliteObject.TleMetadata.All,
RecalculateTrajectoryAlways = FullCurveCalculations
};
Assert.NotNull(satellite);
//calculate the satellite position for this time
var time = new DateTime(2020, 08, 14, 7, 55, 26, DateTimeKind.Utc);
//create trajectory 10 minutes before and after position
var span = new TimeSpan(0, 0, 10, 0);
Assert.IsTrue(satellite.CreateSatellite(time, span, span));
Assert.AreEqual(2, satellite.Objects.Count);
Assert.NotNull(satellite.SatellitePosition);
Assert.NotNull(satellite.SatelliteTrajectory);
Add satellites to the satellite layer
This code shows how to add satellites to the satellite layer:
//The satellite layer
SatelliteLayerViewModel _satelliteLayerViewModel = ...
//create satellites, details not shown
var createdSatellites = new List<ISatellite>();
createdSatellites = CreateSatellites();
//prevent refresh while we are adding satellites
_satelliteLayerViewModel.LockRedraw = true;
foreach (var satellite in createdSatellites)
_satelliteLayerViewModel.AddSatellite(satellite);
//will refresh rendering
_satelliteLayerViewModel.LockRedraw = false;
Automatic update of satellite positions
Automatic update of satellite positions is implemented in the SatelliteLayerViewModel (2D layer). The viewmodel handles the actual conversion of time to satellite positions, but the time updating is extracted to a separate class.
SatelliteSystemTimeUpdate
This class updates the satellite system time.
SatellitesStore
This class handles the satellite system time and automatically updating the positions.
Historic satellites
A satellite's TLE is updated over time since the trajectory changes slightly for each turn. It might therefore be of interest to render the different trajectories and positions for a satellite.The historic instances of the satellite will all have the same satellite ID so this property can not be used directly to differentiate between the current satellite object and the historic satellite objects. Instead the historic SatelliteId is set to the original satellite's ID plus an historic offset and an index number equal to number of historic satellites.
ISatelliteHistory class
Adds necessary functionality to differentiate between regular satellite and historic satellites.
public interface ISatelliteHistory : ISatellite
{
/// <summary>
/// Id for historic satellite.
/// </summary>
new int SatelliteId { get; set; }
/// <summary>
/// The offset added to all historic satellites
/// </summary>
int HistoryOffset { get; }
/// <summary>
/// The index for historic satellite. There can be many historic satellites for one true satellite id.
/// </summary>
int HistoryIndex { get; }
/// <summary>
/// Returns false since this is not a regular satellite.
/// </summary>
new bool IsSatelliteClass { get; }
/// <summary>
/// Returns the non-historic SatelliteId
/// </summary>
int RegularSatelliteId { get; }
}
The IsSatelliteClass will be false for historic satellites and true for regular satellite. For an historic satellite SatelliteId = RegularSatelliteId + HistoryOffset + HistoryIndex. This value can easily be retrieved by RegularSatelliteId.
Create a historic satellite object
The historic satellite is created from the satellite's TLE at a particular time. First create a regular satellite from this TLE and then create the final historic satellite based on the regular satellite.
This test code creates a stand alone historic satellite:
var satellite = new GeoFramework.SatelliteLayer.Satellite();
...
var time = new DateTime(2020, 08, 14, 7, 55, 26, DateTimeKind.Utc);
satellite.Tle = TestTle();
var span = new TimeSpan(0, 0, 10, 0);
Assert.IsTrue(satellite.CreateSatellite(time, span, span));
//if this is the first historic satellite with this ID, then historic index is 1
var historySatellite = new SatelliteHistory(satellite, 1);
Assert.IsTrue(satellite.IsSatelliteClass);
Assert.IsFalse(historySatellite.IsSatelliteClass);
This test code is more realistic since it adds the historic satellite to the viewmodel:
var satellite = new GeoFramework.SatelliteLayer.Satellite();
...
var time = new DateTime(2020, 08, 14, 7, 55, 26, DateTimeKind.Utc);
satellite.Tle = TestTle();
var span = new TimeSpan(0, 0, 10, 0);
Assert.IsTrue(satellite.CreateSatellite(time, span, span));
//adds the satellite as a historic satellite
viewmodel.AddSatelliteHistory(satellite, satellite.SatelliteTle.Epoch, true, false);
//add even simpler
viewmodel.AddSatelliteHistory(satellite, true, false);
Assert.AreEqual(1, viewmodel.SatelliteHistories.Count);
Assert.AreEqual(satellite.SatelliteId, viewmodel.SatelliteHistories[0]);
var history = viewmodel.SatelliteHistory(satellite.SatelliteId);
Assert.AreEqual(2, history.Count);
How to analyze the satellites visibility
The analyze functionality is based on observation (line-of-sight) points which tracks one or many satellites. The tracked satellites can be included or excluded from the analyze process. The results from the process can either be used directly or exported to a xml format.
Observation points in the satellite layer
The ISatelliteLineOfSight adds line-of-sight functionality like adding and removing observation points to the satellite layer.
This code adds an observation point to the satellite layer. The point will track the given satellite.
var satellite = CreateSatellite(time);
viewmodel.AddSatellite(satellite);
var groundPoint = new GeoUnits.GeoPos(61, 100);
var id = Guid.NewGuid().ToString();
var point = new SatelliteLineOfSightViewPoint(id)
{
ViewPointName = "nr1",
Position = groundPoint,
IncludeInComputation = true
};
point.TrackSatellite(satellite.SatelliteId);
Assert.IsTrue(viewmodel.AddLineOfSightViewPoint(point));
LineOfSightEnabled
Enables or disables all line-of-sight functionality.
LineOfSightViewPoint
Returns the layer's specific line-of-sight point or the line-of-sight point(s) for a specific satellite.
SatelliteInSight
Checks if a specific satellite is tracked by any line-of-sight points or if a specific line-of-sight point tracks a specific satellite.
Defining an observation point
The ISatelliteLineOfSightViewPoint class defines the observation point. The points are handled by the satellite layer.
ViewPointId
A unique value for the view point.
Position
The ground position for the view point.
IncludeInComputation
Controls if the view point is included in the analyze process.
TrackSatellite
Functionality to control which satellites are included in the view point's analyze process.
ComputeSettings
Controls how the analyze process creates visibility results around the view point.
Setting up the analyze process
The class ISatelliteLineOfSightComputeSettings controls the analyze process for a view point. The process can use one or many points around the view point so that a visibility pattern can be created for the area around the view point. In addition the process can analyze the visibility for one or many timestamps so the visibility over time can be analyzed.
This code shows how to analyze specific points around the view point
var time = new DateTime(2021, 9, 30, 15, 0, 0);
var satellite = CreateSatellite1(time);
viewmodel.AddSatellite(satellite);
var groundPoint = new GeoUnits.GeoPos(61, 100);
var id = Guid.NewGuid().ToString();
var point = new SatelliteLineOfSightViewPoint(id)
{
ViewPointName = "nr1",
Position = groundPoint,
IncludeInComputation = true
};
point.ComputeSettings.ComputePoints = new List<GeoPos>();
point.ComputeSettings.ComputePoints.Add(new GeoUnits.GeoPos(70, 90));
point.ComputeSettings.ComputePoints.Add(new GeoUnits.GeoPos(60, 90));
point.ComputeSettings.ComputePoints.Add(new GeoUnits.GeoPos(50, 90));
point.ComputeSettings.ComputePoints.Add(new GeoUnits.GeoPos(60, 120));
point.ComputeSettings.Times.Add(time);
point.TrackSatellite(satellite.SatelliteId);
Assert.IsTrue(viewmodel.AddLineOfSightViewPoint(point));
This code shows how to analyze many points in the area around the view point
var time = new DateTime(2021, 9, 30, 15, 0, 0);
var satellite = CreateSatellite1(time);
viewmodel.AddSatellite(satellite);
var groundPoint = new GeoUnits.GeoPos(61, 100);
var id = Guid.NewGuid().ToString();
var point = new SatelliteLineOfSightViewPoint(id)
{
ViewPointName = "nr1",
Position = groundPoint,
IncludeInComputation = true
};
//add three timestamps that shall be analyzed
point.ComputeSettings.Times.Clear();
point.ComputeSettings.Times.Add(time);
point.ComputeSettings.Times.Add(time.AddMinutes(-1));
point.ComputeSettings.Times.Add(time.AddMinutes(1));
point.ComputeSettings.StepCount = 10; // will create 20 x 20 points
point.ComputeSettings.StepRange = 1000; //mtrs between each point
//make sure there are not any manual points
satelliteLineOfSightViewPoint.ComputeSettings.ComputePoints = null;
point.TrackSatellite(satellite.SatelliteId);
Assert.IsTrue(viewmodel.AddLineOfSightViewPoint(point));
How to analyze visibility around observation point
The method ISatelliteLineOfSight.ComputeInSight starts the asynchronous process of checking the visibility of a satellite from the observation point.
This test code starts the visibility process:
....
var time = new DateTime(2021, 9, 30, 15, 0, 0);
var satellite = CreateSatellite1(time);
viewmodel.AddSatellite(satellite);
var groundPoint = new GeoUnits.GeoPos(61, 100);
var id = Guid.NewGuid().ToString();
var point = new SatelliteLineOfSightViewPoint(id)
{
ViewPointName = "nr1",
Position = groundPoint,
IncludeInComputation = true
};
point.ComputeSettings.ComputePoints = new List<GeoPos>();
point.ComputeSettings.ComputePoints.Add(new GeoUnits.GeoPos(70, 90));
point.ComputeSettings.ComputePoints.Add(new GeoUnits.GeoPos(60, 90));
point.ComputeSettings.Times.Add(time);
point.TrackSatellite(satellite.SatelliteId);
Assert.IsTrue(viewmodel.AddLineOfSightViewPoint(point));
_processed = false;
viewmodel.ViewPointsProcessed += ViewmodelOnLineOfSightProcessed;
viewmodel.ComputeInSight(true);
Thread.Sleep(2000);
Assert.IsTrue(_processed);
viewmodel.ViewPointsProcessed -= ViewmodelOnLineOfSightProcessed;
Assert.IsTrue(viewmodel.HasSatelliteVisibleComputations);
var results = viewmodel.LineOfSightViewPoint(id);
Assert.NotNull(results);
Assert.NotNull(results.Status);
var values = results.Status.Values.ToList();
....
}
private void ViewmodelOnLineOfSightProcessed(object sender, LineOfSightProcessedEventArgs args)
{
_processed = true;
}
Example of in sight analyze process:
When are satellites in the area
The method ISatelliteLineOfSight.ComputeWhenInArea starts the asynchronous process of checking when the satellite is visible from the observation point.
This test code starts the in-area process:
....
var time = new DateTime(2021, 9, 30, 15, 0, 0);
var satellite = CreateSatellite1(time);
viewmodel.AddSatellite(satellite);
var groundPoint = new GeoUnits.GeoPos(61, 100);
var id = Guid.NewGuid().ToString();
var point = new SatelliteLineOfSightViewPoint(id)
{
ViewPointName = "nr1",
Position = groundPoint,
IncludeInComputation = true
};
point.ComputeSettings.ComputePoints = new List<GeoPos>();
point.ComputeSettings.ComputePoints.Add(new GeoUnits.GeoPos(70, 90));
point.ComputeSettings.ComputePoints.Add(new GeoUnits.GeoPos(60, 90));
point.ComputeSettings.Times.Add(time);
point.TrackSatellite(satellite.SatelliteId);
Assert.IsTrue(viewmodel.AddLineOfSightViewPoint(point));
_processed = false;
viewmodel.ViewPointsProcessed += ViewmodelOnLineOfSightProcessed;
viewmodel.ComputeWhenInArea(time.AddMinutes(-30), time.AddMinutes(30), new TimeSpan(0, 0, 0, 10), true);
Thread.Sleep(2000);
Assert.IsTrue(_processed);
viewmodel.ViewPointsProcessed -= ViewmodelOnLineOfSightProcessed;
Assert.IsTrue(viewmodel.HasSatelliteVisibleComputations);
var results = viewmodel.LineOfSightViewPoint(id);
Assert.NotNull(results);
Assert.NotNull(results.Status);
var status = results.Status.First();
var timeComputations = status.Value.SatelliteComputations.TimeComputations;
Assert.NotNull(timeComputations);
....
}
private void ViewmodelOnLineOfSightProcessed(object sender, LineOfSightProcessedEventArgs args)
{
_processed = true;
}
Check if observation points are in any satellite's footprint
The method ISatelliteLineOfSight.ComputeInFootprints starts the asynchronous process of checking if an observation point is within any footprints.
This test code shows how to start the footprint process
....
viewmodel.ComputeInFootprints(true);
...
Assert.IsTrue(viewmodel.HasInsideFootprintComputations);
var results = viewmodel.LineOfSightViewPoint(id);
var status = results.Status.First();
var computations = status.Value.FootprintComputations.Computations;
....
When are satellites inside footprints
The method ISatelliteLineOfSight.ComputeWhenInFootprints starts the asynchronous process of checking when the satellite is inside a footprint.
This test code shows how to start the footprint process
....
viewmodel.ComputeWhenInFootprints(startTime, stopTime, steptTime, false);
...
Assert.IsTrue(viewmodel.HasInsideFootprintComputations);
var results = viewmodel.LineOfSightViewPoint(id);
var status = results.Status.First();
var computations = status.Value.FootprintComputations.Computations;
....
Analyze results as XML
The method ISatelliteLineOfSight.StatusAsXml returns the analyze results as XML. The XML content is dependent of type of analyze process.
This test code shows how to get the results as XML
....
viewmodel.ComputeInSight(true);
...
var xml = viewmodel.StatusAsXml();
....
EXample of analyze process as XML:
Implement satellite functionality in GDK layer
The satellites can easily be integrated in a GDK layer since the handling of the satellites are extracted into a dedicated classes. Automatic updating of all satellites is also extracted from the GDK layer.
Handling satellite objects
The satellites are handled by a dedicated store object that can be shared between several layers, for example 2D and 3D.
The satellite store object
The store object can either be created by the viewmodel or created separately and then passed into the viewmodel through the constructor. SatellitesStore implements the following interfaces: ISatellitesStore, ISatelliteHistories and ISatelliteListeners.
Satellite system time
The system time is extracted into a dedicated class, SatelliteSystemTimeUpdate, which is used by the store object.
Connecting viewmodel and satellite store
Automatical update of satellite time and thereby positions are handled by SatellitesStore and SatelliteSystemTimeUpdate, but the viewmodel might need to be informed about the position changes.
The listener class
The listener class, ISatelliteListeners, handles which objects that shall be informed about position changes.
Position changes information
The class, ISatellitesUpdate, is used to relay information about position changes.
Connecting everything
The following extracted code shows how everything is connected in the SatelliteLayerViewModel.
public partial class SatelliteLayerViewModel : ... ISatelliteLayerViewModel, ISatellitesUpdate, ...
{
private readonly SatellitesStore _satellitesStore;
//constructor without store object, layer uses its own store object
public SatelliteLayerViewModel(IGeoUnitsSetting geoUnitsSetting, IGeoContext geoContext, IGeoNavigator geoNavigator, SatelliteLayerViewFactory satelliteObjectLayerViewFactory) ....
{
...
_satellitesStore = new SatellitesStore();
_satellitesStore.AddListener(this);
}
//store object is already created, use this object
public SatelliteLayerViewModel(ISatellitesStore satelliteStore, IGeoUnitsSetting geoUnitsSetting, ....
{
...
_satellitesStore = (SatellitesStore) satelliteStore;
_satellitesStore.AddListener(this);
}
//starts automatic updating
public bool StartAutomaticSatellitePositioning(double updateFrequency)
{
updateFrequency = updateFrequency < 0.3 ? 0.3 : updateFrequency;
_satellitesStore.SatelliteSystemTimeUpdate.ResetAutomaticTimerBaseTime();
_satellitesStore.SatelliteSystemTimeUpdate.SetAndStartTimerTime(updateFrequency * 1000.0, SatelliteLayerSettings, GeoContext);
IsAutomaticPositioning = true;
}
//stop updating
public bool StopAutomaticSatellitePositioning()
{
_satellitesStore.SatelliteSystemTimeUpdate.Stop();
IsAutomaticPositioning = false;
}
public override void Update()
{
...
//must update frequency if scale is changed
if (ViewportExtentChanged && IsAutomaticPositioning )
{
_satellitesStore.SatelliteSystemTimeUpdate.SetAndStartTimerTime(SatelliteLayerSettings, GeoContext);
}
...
}
//ISatellitesUpdate implementations
//callback before satellites are automatically updated
public void BeforeSatellitesUpdate()
{
LockRedraw = true;
}
//callback after satellites are automatically updated
public void AfterSatellitesUpdate()
{
LockRedraw = false;
var trackedSatellite = _satellitesStore.TrackedSatellite(TrackSatellite);
if (trackedSatellite != null)
CenterToPosition(trackedSatellite as Satellite);
}
}
Satellites in 3D
Satellites can also be displayed in 3D. This is handled by the class TPG.Globe3D.SatelliteLayer. When using 2D and 3D in the same application you should make a SatellitesStore outside the layers and inject it in both 2D and 3D layers.
Initializing the 3D satellite layer
The following code example sets up both 2D and 3D satellite layers from the same satellite store.
// Common satellites store
_satellites = new SatellitesStore();
// 2D layer
var satelliteLayerViewFactory = new SatelliteLayerViewFactory(_drawObjectLayerFactory.SymbolsStore);
_satelliteLayerViewModel = new SatelliteLayerViewModel(_satellites, _geoUnitsSetting, geoContext, geoNavigator, satelliteLayerViewFactory);
_mapViewModel.Layers.Add(_satelliteLayerViewModel);
// 3D layer
_satelliteLayer3D = new SatelliteLayer(_map3DViewModel, _satellites, drawObjectDataFactory, stylingXml);
Adding and updating satellites
The 3D layer will display all visible ISatellite objects in the SatellitesStore with the same styling parameters as are used in 2D. The 3D graphics will be updated upon each UpdateSatellites() call in the SatellitesStore, so if you for example control this through an ISatelliteSystemTimeUpdate interface, the satellites will be continuously updated.
If you change any of the styling or other visible parameters of the satellites, you may need to update explicitly, either through a call to SatellitesStore.UpdateSatellites(true) or by calling SatelliteLayer.UpdateSatellites() directly.
Interacting with satellites in 3D
The satellite icons are 3D items of the same type as tracks and point symbols, and you may use the IGlobe3DViewModel.ClickedItem event to respond to mouse clicks on these. Each satellite item will have an ItemId consisting of the SatelliteLayer point list id (accessible through ISatelliteLayer.PointListId) and the satellite ID as InstanceID.
The following example illustrates how you can toggle the 3D label on a clicked satellite:
_map3DViewModel.ClickedItemEvent += clickedSatellite;
private void clickedSatellite(object sender, ClickedItemEventArgs args)
{
// Only react on right-click
if (args.MouseButtonEventArgs.ChangedButton != MouseButton.Right)
return;
var itemId = args.ItemId;
if (itemId.ListId == _satelliteLayer3D.PointListId)
{
var satId = int.Parse(itemId.InstanceId);
var sat = _satellites.GetSatellite(satId);
sat.DrawLabels = !sat.DrawLabels;
_satellites.UpdateSatellites(true);
}
}