diff --git a/apps/api/src/hyp3_api/validation.py b/apps/api/src/hyp3_api/validation.py index 6d9ff9646..4355f71f6 100644 --- a/apps/api/src/hyp3_api/validation.py +++ b/apps/api/src/hyp3_api/validation.py @@ -179,6 +179,23 @@ def check_granules_intersecting_bounds(job, granule_metadata): ) +def check_same_relative_orbits(job, granule_metadata): + previous_relative_orbit = None + for granule in granule_metadata: + name_split = granule['name'].split('_') + absolute_orbit = name_split[7] + # "Relationship between relative and absolute orbit numbers": https://sentiwiki.copernicus.eu/web/s1-products + offset = 73 if name_split[0] == 'S1A' else 27 + relative_orbit = ((int(absolute_orbit) - offset) % 175) + 1 + if not previous_relative_orbit: + previous_relative_orbit = relative_orbit + if relative_orbit != previous_relative_orbit: + raise GranuleValidationError( + f'Relative orbit number for {granule["name"]} does not match that of the previous granules: ' + f'{relative_orbit} is not {previous_relative_orbit}.' + ) + + def convert_single_burst_jobs(jobs: list[dict]) -> list[dict]: jobs = deepcopy(jobs) for job in jobs: diff --git a/job_spec/SRG_GSLC.yml b/job_spec/SRG_GSLC.yml index 26609f537..ccff7c84e 100644 --- a/job_spec/SRG_GSLC.yml +++ b/job_spec/SRG_GSLC.yml @@ -38,7 +38,8 @@ SRG_GSLC: example: -116.583 validators: [ check_bounds_formatting, - check_granules_intersecting_bounds + check_granules_intersecting_bounds, + check_same_relative_orbits ] cost_profiles: DEFAULT: diff --git a/job_spec/SRG_TIME_SERIES.yml b/job_spec/SRG_TIME_SERIES.yml index 564abda2f..38e9e5456 100644 --- a/job_spec/SRG_TIME_SERIES.yml +++ b/job_spec/SRG_TIME_SERIES.yml @@ -39,7 +39,8 @@ SRG_TIME_SERIES: example: -124.41473278572731 validators: [ check_bounds_formatting, - check_granules_intersecting_bounds + check_granules_intersecting_bounds, + check_same_relative_orbits ] cost_profiles: DEFAULT: diff --git a/tests/test_api/test_validation.py b/tests/test_api/test_validation.py index 2bf251e7f..2a85600a1 100644 --- a/tests/test_api/test_validation.py +++ b/tests/test_api/test_validation.py @@ -536,3 +536,20 @@ def test_check_granules_intersecting_bounds(): validation.check_granules_intersecting_bounds(job_with_specified_bounds, invalid_granule_metadata) with raises(validation.GranuleValidationError, match=error_pattern): validation.check_granules_intersecting_bounds(job_with_default_bounds, invalid_granule_metadata) + + +def check_same_relative_orbits(): + valid_granule_metadata = [ + {'name': 'S1A_IW_RAW__0SDV_20201015T161622_20201015T161654_034809_040E95_AF3C'}, + {'name': 'S1A_IW_RAW__0SDV_20200816T161620_20200816T161652_033934_03EFCE_5730'}, + {'name': 'S1B_IW_RAW__0SDV_20200810T161537_20200810T161610_022863_02B66A_F7D7'}, + {'name': 'S1B_IW_RAW__0SDV_20200623T161535_20200623T161607_022163_02A10F_7FD6'} + ] + invalid_granule_metadata = valid_granule_metadata + invalid_granule_metadata.append( + {'name': 'S1B_IW_RAW__0SDV_20200623T161535_20200623T161607_012345_02A10F_7FD6'} + ) + validation.check_same_relative_orbits({}, valid_granule_metadata) + error_pattern = r'.*23 is not 87.*' + with raises(validation.GranuleValidationError, match=error_pattern): + validation.check_same_relative_orbits({}, invalid_granule_metadata)