orbitpy.coveragecalculator — Coverage Calculator
Description
Module providing classes and functions to handle coverage related calculations. Factory method pattern is used for initializing the coverage calculator
object (please see orbit.constellation — Constellation Module for details). The module provides for three types of coverage calculations which are described in detail below.
Users may additionally define their own coverage calculator classes adherent to the same interface functions
(from_dict(.), to_dict(.), execute(.), __eq__(.)) as in any of the built in coverage calculator classes.
GRID COVERAGE
The GRID COVERAGE type of coverage calculation involves find the access-times of a spacecraft over a region represented by a grid. Each GridCoverage object is specific to
a particular grid and spacecraft. The class instance is initialized using the grid object, spacecraft object and path to a data file containing the satellite propagated orbit-states.
The format of the input data file of the spacecraft states is the same as the format of the output data file of the
orbitpy.propagator module (see orbit.propagator — Propagator Module). The states must be of the type CARTESIAN_EARTH_CENTERED_INERTIAL.
GridCoverage.execute function
The coverage calculation can be executed using the execute(.) function. The instrument (in the spacecraft) and the mode of the instrument can be specified
by means of their respective identifiers (in the instru_id, mode_id input arguments). If not specified, the first instrument in the list of spacecraft’s instruments, the first mode of the instrument (see the docs of instrupy.base.Instrument)
in the list of modes of the instrument shall be selected.
Coverage is calculated for the period over which the input spacecraft propagated states are available. The time-resolution of the coverage calculation is the same as the time resolution at which the spacecraft states are available.
Note that the sceneFOV of an instrument (which may be the same as the instrument FOV) is used for coverage calculations unless it has been specified to use the field-of-regard (using the use_field_of_regard input argument).
The mid_access_only input argument can be used to specify if the access times only at the middle of an continuous access-interval.
E.g. If the access takes place at time-indices 450, 451, 452, 453, 454, and if the mid_access_only flag is specified to be true, then only the access time-index = 452 is written
in the results.
Please see Correction of access files for purely side-looking instruments with narrow along-track FOV for situations when the mid_access_only flag would need to be set true.
The out_file_access input argument is
used to specify the filepath (with filename) at which the results are to be written.
Three coverage methods (relevant for the case of sensor FOVs described by spherical-polygon vertices and Rectangular FOVs) can be
specified: (1) DirectSphericalPIP or (2) ProjectedPIP or (3) RectangularPIP.
Default method is DirectSphericalPIP.
The DirectSphericalPIP method is based on a point in spherical polygon algorithm described in the article:
R. Ketzner, V. Ravindra and M. Bramble, ‘A Robust, Fast, and Accurate Algorithm for Point in Spherical Polygon Classification with Applications in Geoscience and Remote Sensing’, Computers and Geosciences, under review.
A CoverageOutputInfo object containing meta-info about the results is returned at the end of execution of the function.
Output data file format
The output of executing the coverage calculator is a csv data file containing the access data.
The first row contains the coverage calculation type.
The second row containing the mission epoch in Julian Day UT1. The time (index) in the state data is referenced to this epoch.
The third row contains the time step-size in seconds.
The fourth row contains the duration (in days) for which coverage calculation is executed.
The fifth row contains the columns headers and the sixth row onwards contains the corresponding data.
Note that time associated with a row is: time = epoch (in JDUT1) + time-index * time-step-size (in secs) * (1/86400)
Description of the columns headers and data is given below:
Column |
Data type |
Units |
Description |
|---|---|---|---|
time index |
int |
Access time-index. |
|
GP index |
int |
Grid-point index. |
|
lat [deg] |
float |
degrees |
Latitude corresponding to the GP index. |
lon [deg] |
float |
degrees |
Longitude corresponding to the GP index. |
POINTING OPTIONS COVERAGE
This type of coverage calculations is possible for an instrument (on a spacecraft) with a set of pointing-options. A pointing-option refers to orientation of the instrument in the nadir-pointing frame. The set of pointing-options represent all the possible orientations of the instrument due to maneuverability of the instrument and/or satellite-bus. The ground-locations for each pointing-option, at each propagation time-step is calculated as the coverage result.
The class instance is initialized using the spacecraft object and path to a data file containing the satellite propagated orbit-states.
The format of the input data file of the spacecraft states is the same as the format of the output data file of the
orbitpy.propagator module (see orbit.propagator — Propagator Module). The states must be of the type CARTESIAN_EARTH_CENTERED_INERTIAL.
PointingOptionsCoverage.execute function
The coverage calculation can be executed using the execute(.) function. The instrument and the mode of the instrument (in the spacecraft) can be specified
by means of their respective identifiers (in the instru_id, mode_id input arguments). If not specified, the first instrument in the list of spacecraft’s instruments, the first mode of the instrument (see the docs of instrupy.base.Instrument)
in the list of modes of the instrument shall be selected.
A CoverageOutputInfo object containing meta-info about the results is returned at the end of execution of the function.
Output data file format
The output of executing the coverage calculator is a csv data file containing the access data.
The first row contains the coverage calculation type.
The second row containing the mission epoch in Julian Day UT1. The time (index) in the state data is referenced to this epoch.
The third row contains the time-step size in seconds.
The fourth row contains the duration (in days) for which coverage calculation is executed.
The fifth row contains the columns headers and the sixth row onwards contains the corresponding data.
Note that time associated with a row is: time = epoch (in JDUT1) + time-index * time-step-size (in secs) * (1/86400)
Description of the columns headers and data is given below:
Column |
Data type |
Units |
Description |
|---|---|---|---|
time index |
int |
Access time-index. |
|
pnt-opt index |
int |
Pointing options index. The indexing starts from 0, where 0 is the first pointing-option in the list of instrument pointing-options. |
|
lat [deg] |
float |
degrees |
Latitude of accessed ground-location. |
lon [deg] |
float |
degrees |
Longitude of accessed ground-location. |
POINTING OPTIONS WITH GRID COVERAGE
This type of coverage calculations is similar to the GRID COVERAGE, except that the coverage calculations are carried out for the list of pointing-options (see POINTING OPTIONS COVERAGE) available for an instrument.
PointingOptionsWithGridCoverage.execute function
The function behavior is similar to the execute function of the GridCoverage object. Coverage calculations are performed for a specific instrument and mode,
and the results are written out for separately for each pointing-option of the instrument/mode.
A key difference is that only the scene-field-of-view of the instrument is considered (no scope to use field-of-regard) in the coverage calculation.
Output data file format
The output of executing the coverage calculator is a csv data file containing the access data.
The first row contains the coverage calculation type.
The second row containing the mission epoch in Julian Day UT1. The time (index) in the state data is referenced to this epoch.
The third row contains the time-step size in seconds.
The fourth row contains the duration (in days) for which coverage calculation is executed.
The fifth row contains the columns headers and the sixth row onwards contains the corresponding data.
Note that time associated with a row is: time = epoch (in JDUT1) + time-index * time-step-size (in secs) * (1/86400)
Description of the columns headers and data is given below:
Column |
Data type |
Units |
Description |
|---|---|---|---|
time index |
int |
Access time-index. |
|
pnt-opt index |
int |
Pointing options index. The indexing starts from 0, where 0 is the first pointing-option in the list of instrument pointing-options. |
|
GP index |
int |
Grid-point index. |
|
lat [deg] |
float |
degrees |
Latitude corresponding to the GP index. |
lon [deg] |
float |
degrees |
Longitude corresponding to the GP index. |
Correction of access files for purely side-looking instruments with narrow along-track FOV
In case of purely side-looking instruments with narrow-FOV (eg: SARs executing Stripmap operation mode), the access to a grid-point takes place
when the grid-point is seen with no squint angle and the access is almost instantaneous (i.e. access duration is very small).
The coverage calculations is carried out with the corresponding instrument scene-field-of-view or field-of-regard (built using the scene-field-of-view)
(see instrupy package documentation).
If the instrument FOV is to be used for coverage calculations, a very very small time step-size would need to be used which to impractically leads to long computation time.
The access files in general list rows of access-time, ground-points, and thus independent access opportunities for the instrument when the scene-field-of-view / field-of-regard is used for coverage calculations. If the generated access files from the these coverage calculations of a purely side-looking, narrow along-track FOV instrument is interpreted in the same manner, it would be erroneous.
Thus the generated access files are then corrected to show access only at approximately (to the nearest propagation time-step) the middle of the access interval. This should be coupled with the required scene-scan-duration (from scene-field-of-view) to get complete information about the access.
For example, consider a SAR instrument pointing sideways as shown in the figure below. The along-track FOV is narrow corresponding to narrow strips, and a scene is built from concatenated strips. A SceneFOV is associated with the SAR and is used for access calculation over the grid point shown in the figure. Say the propagation time-step is 1s as shown in the figure. An access interval between t=100s to t=105s is registered. However as shown the actual access takes place over a small interval of time at t=103.177s.
An approximation can be applied (i.e. correction is made) that the observation time of the ground point is at the middle of the access interval as calculated using the SceneFOV, rounded of to the nearest propagation time, i.e. \(t= 100 + ((105-100)/2) % 1 = 103s\). The state of the spacecraft at \(t=103s\) and access duration corresponding to the instrument FOV (note: not the sceneFOV) (can be determined analytically) is to be used for the data-metrics calculation.
Warning
The correction method is to be used only when the instrument access-duration (which is determined from the instrument FOV) is smaller than the propagation time step (which is determined from the sceneFOV or FOR).
Examples
GRID COVERAGE example 1
The following snippet of code initializes and executes coverage calculation for a spacecraft in an equatorial orbit, and a grid about the equator. The spacecraft is aligned to the nadir-pointing frame (
instrupy.util.ReferenceFrame.NADIR_POINTING) and the instrument in turn is aligned to the spacecraft body frame (instrupy.util.ReferenceFrame.SC_BODY_FIXED). The access data shows the grid-points accessed at every time tick of the mission. The interval between the time-ticks is equal to the propagation step-size which here is 2 seconds.from orbitpy.util import OrbitState, Spacecraft, SpacecraftBus from orbitpy.propagator import J2AnalyticalPropagator from orbitpy.coveragecalculator import GridCoverage from orbitpy.grid import Grid from instrupy.base import Instrument import os out_dir = os.path.dirname(os.path.realpath(__file__)) # initialize J2 analytical propagator with 2 secs propagation step-size j2_prop = J2AnalyticalPropagator.from_dict({"@type": 'J2 ANALYTICAL PROPAGATOR', 'stepSize':2} ) # initialize orbit (initial state of the satellite) orbit = OrbitState.from_dict({"date":{"@type":"GREGORIAN_UT1", "year":2018, "month":5, "day":26, "hour":12, "minute":0, "second":0}, "state":{"@type": "KEPLERIAN_EARTH_CENTERED_INERTIAL", "sma": 6378+500, "ecc": 0.001, "inc": 0, "raan": 20, "aop": 0, "ta": 120} }) bus = SpacecraftBus.from_dict({"orientation":{"referenceFrame": "NADIR_POINTING", "convention": "REF_FRAME_ALIGNED"}}) # bus is aligned to the NADIR_POINTING frame. instru = Instrument.from_json({"@type": "Basic Sensor","fieldOfViewGeometry": {"shape": "circular", "diameter":30}, "orientation": {"referenceFrame": "SC_BODY_FIXED", "convention": "REF_FRAME_ALIGNED"}}) # instrument is aligned to the bus # spacecraft with 1 instrument sc = Spacecraft(orbitState=orbit, spacecraftBus=bus, instrument=instru) state_cart_file = os.path.dirname(os.path.realpath(__file__)) + '/cart_state.csv' # execute the propagator for duration of 0.1 days j2_prop.execute(sc, None, state_cart_file, None, duration=0.1) # make the Grid object grid = Grid.from_dict({"@type": "autogrid", "@id": 1, "latUpper":25, "latLower":-25, "lonUpper":180, "lonLower":-180, "gridRes": 2}) # set output file path out_file_access = out_dir + '/access.csv' # run the coverage calculator cov_cal = GridCoverage(grid=grid, spacecraft=sc, state_cart_file=state_cart_file) out_info = cov_cal.execute(instru_id=None, mode_id=None, use_field_of_regard=False, out_file_access=out_file_access, mid_access_only=False, method='ProjectedPIP') access.csv ----------- GRID COVERAGE Epoch [JDUT1] is 2458265.0 Step size [s] is 2.0 Mission Duration [Days] is 0.1 time index,GP index,lat [deg],lon [deg] 0,4303,0.0,76.0 1,4303,0.0,76.0 2,4303,0.0,76.0 3,4303,0.0,76.0 4,4303,0.0,76.0 5,4303,0.0,76.0 6,4303,0.0,76.0 7,4303,0.0,76.0 7,4304,0.0,78.0 8,4303,0.0,76.0 8,4304,0.0,78.0 9,4303,0.0,76.0 9,4304,0.0,78.0 10,4303,0.0,76.0 10,4304,0.0,78.0 11,4304,0.0,78.0 12,4304,0.0,78.0 ...
In below snippet the
mid_access_onlyflag is set toTrueinstead ofFalse. Observe the difference in the output access data between the above result and the below result. In the below result only access at the middle of the access time-interval is shown. E.g. The very-first access to the GP 4303 is from time-index = 0 to 10 and the mid-interval access is at time-index = 5.out_info = cov_cal.execute(instru_id=None, mode_id=None, use_field_of_regard=False, out_file_access=out_file_access, mid_access_only=True, method='ProjectedPIP') access.csv ----------- GRID COVERAGE Epoch [JDUT1] is 2458265.0 Step size [s] is 2.0 Mission Duration [Days] is 0.1 time index,GP index,lat [deg],lon [deg] 5,4303,0.0,76.0 17,4304,0.0,78.0 34,4305,0.0,80.0 51,4306,0.0,82.0 ...
GRID COVERAGE example 2
In the below snippet, the satellite is equipped with two instruments. The second instrument, and second mode is selected for coverage calculation. The
use_field_of_regardflag is set true to indicate that the field-of-regard should be considered for the coverage calculation. Note that in absence of theorientationspecifications for theSpacecraftBusobject, the default is assumed to be aligned to the nadir-pointing frame. In case of the instrument, the default orientation is alignment to the spacecraft bus. Since themethodis not specified, the default methodDirectSphericalPIPis used.from orbitpy.util import OrbitState, Spacecraft, SpacecraftBus from orbitpy.propagator import J2AnalyticalPropagator from orbitpy.coveragecalculator import GridCoverage from orbitpy.grid import Grid from instrupy.base import Instrument import os out_dir = os.path.dirname(os.path.realpath(__file__)) j2_prop = J2AnalyticalPropagator.from_dict({"@type": 'J2 ANALYTICAL PROPAGATOR', 'stepSize':2} ) orbit = OrbitState.from_dict({"date":{"@type":"GREGORIAN_UT1", "year":2018, "month":5, "day":26, "hour":12, "minute":0, "second":0}, "state":{"@type": "KEPLERIAN_EARTH_CENTERED_INERTIAL", "sma": 6378+500, "ecc": 0.001, "inc": 0, "raan": 20, "aop": 0, "ta": 120} }) bus = SpacecraftBus.from_dict({}) instru1= Instrument.from_json({"@type": "Basic Sensor","fieldOfViewGeometry": {"shape": "circular", "diameter":30}, "@id": "A"}) instru2 = Instrument.from_json({"@type": "Basic Sensor","fieldOfViewGeometry": {"shape": "Rectangular", "angleHeight": 10, "angleWidth": 5}, "mode":[{"@id":1, "maneuver":{"maneuverType": "CIRCULAR", "diameter":10}}, {"@id":2, "maneuver":{"maneuverType": "SINGLE_ROLL_ONLY", "A_rollMin":10, "A_rollMax":35}}], "@id": "B"}) # spacecraft with 2 instruments sc = Spacecraft(orbitState=orbit, spacecraftBus=bus, instrument=[instru1, instru2]) state_cart_file = os.path.dirname(os.path.realpath(__file__)) + '/cart_state.csv' # execute the propagator for duration of 0.1 days j2_prop.execute(sc, None, state_cart_file, None, duration=0.1) # make the Grid object grid = Grid.from_dict({"@type": "autogrid", "@id": 1, "latUpper":25, "latLower":-25, "lonUpper":180, "lonLower":-180, "gridRes": 2}) # set output file path out_file_access = out_dir + '/access.csv' # run the coverage calculator cov_cal = GridCoverage(grid=grid, spacecraft=sc, state_cart_file=state_cart_file) out_info = cov_cal.execute(instru_id="B", mode_id=2, use_field_of_regard=True, out_file_access=out_file_access, mid_access_only=True) # select instru B, mode 2 access.csv ----------- GRID COVERAGE Epoch [JDUT1] is 2458265.0 Step size [s] is 2.0 Mission Duration [Days] is 0.1 time index,GP index,lat [deg],lon [deg] 2,3943,2.0,76.0 17,3944,2.0,78.0 34,3945,2.0,80.0 51,3946,2.0,82.0 68,3947,2.0,84.0
POINTING OPTIONS COVERAGE example
In the below snippet, the satellite is equipped with two instruments. The second instrument is associated with pointing-options and is selected for coverage calculation.
from orbitpy.util import OrbitState, Spacecraft, SpacecraftBus from orbitpy.propagator import J2AnalyticalPropagator from orbitpy.coveragecalculator import PointingOptionsCoverage from instrupy.base import Instrument import os out_dir = os.path.dirname(os.path.realpath(__file__)) j2_prop = J2AnalyticalPropagator.from_dict({"@type": 'J2 ANALYTICAL PROPAGATOR', 'stepSize':2} ) orbit = OrbitState.from_dict({"date":{"@type":"GREGORIAN_UT1", "year":2018, "month":5, "day":26, "hour":12, "minute":0, "second":0}, "state":{"@type": "KEPLERIAN_EARTH_CENTERED_INERTIAL", "sma": 6378+500, "ecc": 0.001, "inc": 0, "raan": 20, "aop": 0, "ta": 120} }) bus = SpacecraftBus.from_dict({}) instru1= Instrument.from_json({"@type": "Basic Sensor","fieldOfViewGeometry": {"shape": "circular", "diameter":10}, "@id": "A"}) instru2 = Instrument.from_json({"@type": "Basic Sensor","fieldOfViewGeometry": {"shape": "circular", "diameter":5}, "pointingOption":[{"referenceFrame": "NADIR_POINTING", "convention": "XYZ", "xRotation":0, "yRotation":2.5, "zRotation":0}, {"referenceFrame": "NADIR_POINTING", "convention": "XYZ", "xRotation":0, "yRotation":-2.5, "zRotation":0}], "@id": "B"}) # spacecraft with 2 instruments sc = Spacecraft(orbitState=orbit, spacecraftBus=bus, instrument=[instru1, instru2]) state_cart_file = os.path.dirname(os.path.realpath(__file__)) + '/cart_state.csv' # execute the propagator for duration of 0.1 days j2_prop.execute(sc, None, state_cart_file, None, duration=0.1) # set output file path out_file_access = out_dir + '/access.csv' # run the coverage calculator cov_cal = PointingOptionsCoverage(spacecraft=sc, state_cart_file=state_cart_file) out_info = cov_cal.execute(instru_id="B", mode_id=None, out_file_access=out_file_access, method='ProjectedPIP') # specify instrument "B" access.csv ----------- POINTING OPTIONS COVERAGE Epoch [JDUT1] is 2458265.0 Step size [s] is 2.0 Mission Duration [Days] is 0.1 time index,pnt-opt index,lat [deg],lon [deg] 0,0,0.197,75.989 0,1,-0.197,75.989 1,0,0.197,76.108 1,1,-0.197,76.108 2,0,0.197,76.226 ...
POINTING OPTIONS WITH GRID COVERAGE example
In the below snippet, the satellite is equipped with two instruments. The second instrument with the pointing-options specifications is chosen for coverage calculations. The
mid_access_onlyflag is set toTrueto have only the access-times at the middle of access-intervals. The output csv file shows the grid-points accessed (if any) for each of the pointing-options at every time-step.from orbitpy.util import OrbitState, Spacecraft, SpacecraftBus from orbitpy.propagator import J2AnalyticalPropagator from orbitpy.coveragecalculator import PointingOptionsWithGridCoverage from orbitpy.grid import Grid from instrupy.base import Instrument import os out_dir = os.path.dirname(os.path.realpath(__file__)) # initialize J2 analytical propagator with 2 secs propagation step-size j2_prop = J2AnalyticalPropagator.from_dict({"@type": 'J2 ANALYTICAL PROPAGATOR', 'stepSize':2} ) # initialize orbit (initial state of the satellite) orbit = OrbitState.from_dict({"date":{"@type":"GREGORIAN_UT1", "year":2018, "month":5, "day":26, "hour":12, "minute":0, "second":0}, "state":{"@type": "KEPLERIAN_EARTH_CENTERED_INERTIAL", "sma": 6378+500, "ecc": 0.001, "inc": 0, "raan": 20, "aop": 0, "ta": 120} }) bus = SpacecraftBus.from_dict({"orientation":{"referenceFrame": "NADIR_POINTING", "convention": "REF_FRAME_ALIGNED"}}) # bus is aligned to the NADIR_POINTING frame. instru1= Instrument.from_json({"@type": "Basic Sensor","fieldOfViewGeometry": {"shape": "circular", "diameter":10}, "@id": "A"}) instru2 = Instrument.from_json({"@type": "Basic Sensor","fieldOfViewGeometry": {"shape": "circular", "diameter":20}, "pointingOption":[{"referenceFrame": "NADIR_POINTING", "convention": "XYZ", "xRotation":0, "yRotation":10, "zRotation":0}, {"referenceFrame": "NADIR_POINTING", "convention": "XYZ", "xRotation":0, "yRotation":-10, "zRotation":0}], "@id": "B"}) # spacecraft with 1 instrument sc = Spacecraft(orbitState=orbit, spacecraftBus=bus, instrument=[instru1, instru2]) state_cart_file = os.path.dirname(os.path.realpath(__file__)) + '/cart_state.csv' # execute the propagator for duration of 0.1 days j2_prop.execute(sc, None, state_cart_file, None, duration=0.1) # make the Grid object grid = Grid.from_dict({"@type": "autogrid", "@id": 1, "latUpper":45, "latLower":-45, "lonUpper":180, "lonLower":-180, "gridRes": 1}) # set output file path out_file_access = out_dir + '/access.csv' # run the coverage calculator cov_cal = PointingOptionsWithGridCoverage(grid=grid, spacecraft=sc, state_cart_file=state_cart_file) out_info = cov_cal.execute(instru_id="B", mode_id=None, out_file_access=out_file_access, mid_access_only=True, method='RectangularPIP') access.csv ----------- POINTING OPTIONS WITH GRID COVERAGE Epoch [JDUT1] is 2458265.0 Step size [s] is 2.0 Mission Duration [Days] is 0.1 time index,pnt-opt index,GP index,lat [deg],lon [deg] 2,1,28958,-1.0,75.71 3,0,28599,1.0,76.0 6,1,28959,-1.0,76.713 8,0,28600,1.0,77.0 ... POINTING OPTIONS WITH GRID COVERAGE
API
Classes
Functions