DoubleDihedraFieldOfView.java

  1. /* Copyright 2002-2025 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.geometry.fov;

  18. import org.hipparchus.exception.LocalizedCoreFormats;
  19. import org.hipparchus.geometry.euclidean.threed.Rotation;
  20. import org.hipparchus.geometry.euclidean.threed.RotationConvention;
  21. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  22. import org.hipparchus.geometry.partitioning.Region;
  23. import org.hipparchus.geometry.partitioning.RegionFactory;
  24. import org.hipparchus.geometry.spherical.twod.Circle;
  25. import org.hipparchus.geometry.spherical.twod.S2Point;
  26. import org.hipparchus.geometry.spherical.twod.Sphere2D;
  27. import org.hipparchus.geometry.spherical.twod.SphericalPolygonsSet;
  28. import org.hipparchus.geometry.spherical.twod.SubCircle;
  29. import org.hipparchus.util.FastMath;
  30. import org.orekit.errors.OrekitException;

  31. /** Class representing a spacecraft sensor Field Of View with dihedral shape (i.e. rectangular shape).
  32.  * @author Luc Maisonobe
  33.  * @since 10.1
  34.  */
  35. public class DoubleDihedraFieldOfView extends PolygonalFieldOfView {

  36.     /** Build a Field Of View with dihedral shape (i.e. rectangular shape).
  37.      * @param center Direction of the FOV center, in spacecraft frame
  38.      * @param axis1 FOV dihedral axis 1, in spacecraft frame
  39.      * @param halfAperture1 FOV dihedral half aperture angle 1,
  40.      * must be less than π/2, i.e. full dihedra must be smaller then
  41.      * an hemisphere
  42.      * @param axis2 FOV dihedral axis 2, in spacecraft frame
  43.      * @param halfAperture2 FOV dihedral half aperture angle 2,
  44.      * must be less than π/2, i.e. full dihedra must be smaller then
  45.      * an hemisphere
  46.      * @param margin angular margin to apply to the zone (if positive,
  47.      * points outside of the raw FoV but close enough to the boundary are
  48.      * considered visible; if negative, points inside of the raw FoV
  49.      * but close enough to the boundary are considered not visible)
  50.      */
  51.     public DoubleDihedraFieldOfView(final Vector3D center,
  52.                                     final Vector3D axis1, final double halfAperture1,
  53.                                     final Vector3D axis2, final double halfAperture2,
  54.                                     final double margin) {
  55.         super(createPolygon(center, axis1, halfAperture1, axis2, halfAperture2), margin);
  56.     }

  57.     /** Create polygon.
  58.      * @param center Direction of the FOV center, in spacecraft frame
  59.      * @param axis1 FOV dihedral axis 1, in spacecraft frame
  60.      * @param halfAperture1 FOV dihedral half aperture angle 1,
  61.      * must be less than π/2, i.e. full dihedra must be smaller then
  62.      * an hemisphere
  63.      * @param axis2 FOV dihedral axis 2, in spacecraft frame
  64.      * @param halfAperture2 FOV dihedral half aperture angle 2,
  65.      * must be less than π/2, i.e. full dihedra must be smaller then
  66.      * an hemisphere
  67.      * @return built polygon
  68.      */
  69.     private static SphericalPolygonsSet createPolygon(final Vector3D center,
  70.                                                       final Vector3D axis1, final double halfAperture1,
  71.                                                       final Vector3D axis2, final double halfAperture2) {
  72.         final RegionFactory<Sphere2D, S2Point, Circle, SubCircle> factory = new RegionFactory<>();
  73.         final double tolerance = FastMath.max(FastMath.ulp(2.0 * FastMath.PI),
  74.                                               1.0e-12 * FastMath.max(halfAperture1, halfAperture2));
  75.         final Region<Sphere2D, S2Point, Circle, SubCircle> dihedra1 = buildDihedra(factory, tolerance, center, axis1, halfAperture1);
  76.         final Region<Sphere2D, S2Point, Circle, SubCircle> dihedra2 = buildDihedra(factory, tolerance, center, axis2, halfAperture2);
  77.         return (SphericalPolygonsSet) factory.intersection(dihedra1, dihedra2);
  78.     }

  79.     /** Build a dihedra.
  80.      * @param factory factory for regions
  81.      * @param tolerance tolerance below which points are considered equal
  82.      * @param center Direction of the FOV center, in spacecraft frame
  83.      * @param axis FOV dihedral axis, in spacecraft frame
  84.      * @param halfAperture FOV dihedral half aperture angle,
  85.      * must be less than π/2, i.e. full dihedra must be smaller then
  86.      * an hemisphere
  87.      * @return dihedra
  88.      */
  89.     private static Region<Sphere2D, S2Point, Circle, SubCircle>
  90.         buildDihedra(final RegionFactory<Sphere2D, S2Point, Circle, SubCircle> factory,
  91.                      final double tolerance, final Vector3D center,
  92.                      final Vector3D axis, final double halfAperture) {
  93.         if (halfAperture > 0.5 * FastMath.PI) {
  94.             throw new OrekitException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE,
  95.                                       halfAperture, 0.0, 0.5 * FastMath.PI);
  96.         }

  97.         final Rotation r = new Rotation(axis, halfAperture, RotationConvention.VECTOR_OPERATOR);
  98.         final Vector3D normalCenterPlane = Vector3D.crossProduct(axis, center);
  99.         final Vector3D normalSidePlus    = r.applyInverseTo(normalCenterPlane);
  100.         final Vector3D normalSideMinus   = r.applyTo(normalCenterPlane.negate());

  101.         return factory.intersection(new SphericalPolygonsSet(normalSidePlus,  tolerance),
  102.                                     new SphericalPolygonsSet(normalSideMinus, tolerance));

  103.     }

  104. }