Conversation
Modified: orbitpy/coveragecalculator.py: Modified a single line to ensure that the RCG factor output is kept in memory over the entire simulation interval. Previously, it was set to retain only the buff size in memory, so that accesses at the end of the simulation to return the rcg factor to the user were out of bounds are returning garbage values. After this update, RCG factor should always be positive (LOS available) or NaN (no LOS).
…xplicit that the input is a stateseries in ITRF. Modified: orbitpy/specular.py: Changed input names `transmitter` and `receiver` to `transmitter_state_itrf` and `receiver_state_itrf`. examples/specular_example.py: Updated example for new interface.
…ecularCoverage class. Modified: orbitpy/coveragecalculator.py: Modified SpecularCoverage class to support rectangular and spherical polygon FOVs in addition to the previously supported conical FOVs. Follows same logic as the PointCoverage class. May be helpful in the future to consolodate some of this using function calls.
…. Modified specular.py to return RCG factor also.
New:
examples/specular_rcg_example.py: Computes and plots RCG factor for single transmitter/receiver pair.
Modified:
orbitpy/specular.py: Modified to compute and return RCG factor in addition to specular point location.
examples/specular_example.py: Modified to use new specular.py API and also to plot RCG factor.
…sing RCG factor. Validated with CYGNSS data. Modified: orbitpy/specular.py: Added get_best_trajectory function and get_topk_trajectories function. orbitpy/coverage.py: Added _getitem_ and _setitem_ functions to DiscreteCoverageTP. orbitpy/coveragecalculator.py: Added get_best_coverage function which returns the coverage associated with the highest-rcg specular point at each time point. examples/specular_coverage/specular_coverage_example.py: Added a few lines at the end to demonstrate use of the get_best_coverage function. Added: examples/specular_validation.py: Script to validate specular point switching algorithm against CYGNSS data. examples/cygnss_data/: Data used in specular_validation.
There was a problem hiding this comment.
Pull request overview
This PR expands specular-point tooling to compute/use range-corrected gain (RCG) factors, adds utilities to select best/top‑k specular trajectories/coverage over time, and extends specular coverage to additional FOV geometries.
Changes:
get_specular_trajectorynow computes/returns RCG alongside specular point trajectories; adds trajectory ranking helpers (get_best_trajectory,get_topk_trajectories).SpecularCoverageaddsget_best_coverageand extends FOV handling to rectangular and spherical polygon shapes.- Adds/updates example scripts for RCG plotting and CYGNSS validation.
Reviewed changes
Copilot reviewed 7 out of 44 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| orbitpy/specular.py | Adds best/top‑k trajectory selection utilities and returns RCG from specular trajectory computation. |
| orbitpy/coveragecalculator.py | Adds “best coverage” selection by RCG and supports rectangular/polygon FOV paths. |
| orbitpy/coverage.py | Adds indexing helpers for DiscreteCoverageTP. |
| examples/specular_validation.py | New CYGNSS validation example using top‑k by RCG. |
| examples/specular_rcg_example.py | New example to compute/plot RCG for one Tx/Rx pair. |
| examples/specular_example.py | Updates example to new specular API and plots RCG. |
| examples/specular_coverage/specular_coverage_example.py | Demonstrates get_best_coverage usage. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| def get_specular_trajectory( | ||
| transmitter: StateSeries, | ||
| receiver: StateSeries, | ||
| transmitter_states_itrf: StateSeries, | ||
| receiver_states_itrf: StateSeries, | ||
| times: AbsoluteDateArray, | ||
| surface: SurfaceType = SurfaceType.WGS84, | ||
| ) -> PositionSeries: |
| result = PositionSeries( | ||
| data=specular_positions, time=times, frame=transmitter.frame | ||
| data=specular_positions, time=times, frame=transmitter_states_itrf.frame | ||
| ) | ||
|
|
||
| return result | ||
| return result, rcg_factor |
| def get_best_trajectory( | ||
| traj_list: List[Tuple[PositionSeries, List[float]]], | ||
| ) -> Tuple[PositionSeries, List[float]]: |
| output_traj, output_rcg = copy.deepcopy(traj_list[0]) | ||
| num_timepoints = len(output_traj.time) | ||
| num_lists = len(traj_list) | ||
|
|
||
| for i in range(num_timepoints): | ||
| max_rcg = -np.inf | ||
| max_rcg_idx = 0 | ||
| for j in range(0, num_lists): | ||
| rcg_j = traj_list[j][1][i] # rcg at timepoint i for transmitter j | ||
| if np.isfinite(rcg_j) and rcg_j > max_rcg: | ||
| max_rcg = rcg_j | ||
| max_rcg_idx = j | ||
|
|
||
| output_traj.data[0][i] = traj_list[max_rcg_idx][0].data[0][i] | ||
| output_rcg[i] = max_rcg |
| output_traj, output_rcg = copy.deepcopy(traj_list[0]) | ||
| num_timepoints = len(output_traj.time) | ||
| num_lists = len(traj_list) | ||
|
|
||
| for i in range(num_timepoints): | ||
| max_rcg = -np.inf | ||
| max_rcg_idx = 0 | ||
| for j in range(0, num_lists): | ||
| rcg_j = traj_list[j][1][i] # rcg at timepoint i for transmitter j | ||
| if np.isfinite(rcg_j) and rcg_j > max_rcg: | ||
| max_rcg = rcg_j | ||
| max_rcg_idx = j | ||
|
|
||
| output_traj.data[0][i] = traj_list[max_rcg_idx][0].data[0][i] | ||
| output_rcg[i] = max_rcg |
| def get_best_coverage(cls, | ||
| coverage_list: List[Tuple[DiscreteCoverageTP, List[float]]] | ||
| ) -> Tuple[DiscreteCoverageTP, List[float]]: |
| output_coverage, output_rcg = copy.deepcopy(coverage_list[0]) | ||
| num_timepoints = len(coverage_list[0][0].time) | ||
| num_lists = len(coverage_list) | ||
| for i in range(num_timepoints): | ||
| max_rcg = 0.0 | ||
| max_rcg_idx = 0 | ||
| for j in range(0, num_lists): | ||
| rcg_j = coverage_list[j][1][i] # rcg at timepoint i for transmitter j | ||
| if rcg_j > max_rcg: | ||
| max_rcg = rcg_j | ||
| max_rcg_idx = j | ||
|
|
||
| output_coverage.coverage[i] = coverage_list[max_rcg_idx][0].coverage[i] | ||
| output_rcg[i] = max_rcg |
| @@ -0,0 +1,197 @@ | |||
| """ | |||
| Example script demonstrating specular point computation and switching algorithm. | |||
| Uses saved data for CYGNSS position and GPS tramnsmitter positions, and computes the | |||
|
|
||
| best_cov, best_rcg = SpecularCoverage.get_best_coverage(coverage) | ||
|
|
||
| print(f"Total best coverage time summed accross target grid points is {best_cov.coverage_time()}") |
| import os | ||
| import matplotlib.pyplot as plt | ||
|
|
||
| from eosimutils.time import AbsoluteDateArray, AbsoluteDate, AbsoluteDateArray |
| from the entry with the r-th highest RCG at that time. | ||
|
|
||
| Also returns, for each timepoint, the k ids selected (in rank order: best -> k-th best). | ||
|
|
There was a problem hiding this comment.
It would be good to include a small example with say just 5 time-points, number of input trajectories as 4, and k-2.
| import kcl | ||
| import GeometricTools as gte | ||
|
|
||
| def get_best_trajectory( |
There was a problem hiding this comment.
Alternative to using this function, one could call get_topk_trajectories with k=1.
Mention it in the docstring.
|
|
||
| Args: | ||
| coverage_list (List[Tuple[DiscreteCoverageTP, List[float]]]): A list of | ||
| tuples containing (1) the coverage for each GPS transmitter and (2) the radar's |
There was a problem hiding this comment.
replace "A list of tuples containing (1) the coverage" with "A list of tuples, with each tuple containing (1) the coverage..."
| return cls() | ||
|
|
||
| @classmethod | ||
| def get_best_coverage(cls, |
There was a problem hiding this comment.
The code within this function seems to be similar to the get_best_trajectory function in the specular module. Can you modify this to use the function inside the specular module, to avoid duplication of code.
Could you also add a get_topk_coverage which leverages the get_topk_trajectories in the specular module?
| sp_posseries = get_specular_trajectory( | ||
| transmitter=tx_stateseries, | ||
| receiver=sc_stateseries, | ||
| sp_posseries, rcg_factor_ellipsoidal = get_specular_trajectory( |
There was a problem hiding this comment.
Rename sp_posseries to sp_posseries_ellipsoidal
| @@ -0,0 +1,197 @@ | |||
| """ | |||
| Example script demonstrating specular point computation and switching algorithm. | |||
There was a problem hiding this comment.
Explain switching algorithm
| Uses saved data for CYGNSS position and GPS tramnsmitter positions, and computes the | ||
| specular point generated by each GPS/CYGNSS pair. | ||
|
|
||
| Prints the percentage of time points where the top 4 GPS satellites selected by the switching |
There was a problem hiding this comment.
Clarify what happens in the cases when at a time-point there is partial match. For example if 3 out of 4 GPS satellites match, is that time-point treated as a complete failure in the overall metric calculation?
| algorithm match the top 4 GPS satellites from the reference data. The top 4 are selected by taking | ||
| the specular point trajectories with the highest RCG factor at each time point. | ||
|
|
||
| Then, at each time point, selects the specular point with the lowest error norm vs the reference |
There was a problem hiding this comment.
I think this part should be modified so that the GPS indices are matched before calculating the error norm.
The results will likely remain similar or identical. However, there is a possibility that the specular point from one GPS satellite (selected by the switching algorithm) could produce a lower error norm when compared with the specular point of a different GPS satellite in the reference dataset.
Code related to specular coverage calculation. Fixed a bug in RCG factor computation causing negative outputs. The get_specular_trajectory function was expanded to compute and return RCG factors alongside specular point trajectories. SpecularCoverage now supports multiple FOV geometries (conical, rectangular, and spherical polygon). Added function to select the highest-RCG (top or top-k) specular trajectories and coverage over time, and validated against CYGNSS data. Added new and updated examples.
Added
examples/specular_rcg_example.py
Computes and plots the RCG factor for a single transmitter/receiver pair.
examples/specular_validation.py
Validates the specular point switching algorithm against CYGNSS data.
examples/cygnss_data/
CYGNSS datasets used for specular validation.
orbitpy/specular.py
get_best_trajectoryfor selecting the highest-RCG specular point trajectory.get_topk_trajectoriesfor retrieving the top-K specular trajectories by RCG.orbitpy/coveragecalculator.py
get_best_coverageto return coverage associated with the highest-RCG specular point at each time step.Modified
orbitpy/coveragecalculator.py
NaN(no LOS).SpecularCoverageto support rectangular and spherical polygon FOVs in addition to conical FOVs.orbitpy/specular.py
transmitterandreceivertotransmitter_state_itrfandreceiver_state_itrffor clarity.orbitpy/coverage.py
__getitem__and__setitem__methods toDiscreteCoverageTP.examples/specular_example.py
examples/specular_coverage/specular_coverage_example.py
get_best_coverage.