Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to zero-finding algorithm #17

Open
dbookstaber opened this issue Jan 29, 2025 · 0 comments
Open

Improvements to zero-finding algorithm #17

dbookstaber opened this issue Jan 29, 2025 · 0 comments
Labels
enhancement New feature or request

Comments

@dbookstaber
Copy link
Owner

dbookstaber commented Jan 29, 2025

  1. Determine if there is a zero angle before searching. Is there a heuristic that can determine whether zero is reachable? E.g., max range is typically achieved about 40 degrees.

  2. MaxIterations for the proportional iterative search should probably be no more than 10. Especially if, instead of starting from elevation of zero, we take a better initial guess. For example, we could use the vacuum angle to zero as the starting point:

def calculate_drag_free_launch_angles_in_degrees(
    distance: float, height: float, velocity: float, g: float = EARTH_GRAVITY_CONSTANT
) -> Optional[Tuple[float, float]]:
    """
    Calculate both possible launch angles for hitting a target.

    Args:
        distance (float): Horizontal distance to target in meters
        height (float): Vertical height difference to target in meters
        velocity (float): Initial launch velocity in m/s

    Returns:
        Optional[Tuple[float, float]]: Tuple of (low angle, high angle) in degrees,
                                     or None if target is unreachable
    """
    # Check if target is reachable with given velocity
    velocity_squared = velocity**2
    discriminant = velocity_squared**2 - g * (
        g * distance**2 + 2 * height * velocity_squared
    )

    if discriminant < 0:
        return None

    # Calculate the two possible angles using quadratic formula
    term1 = velocity_squared
    term2 = math.sqrt(discriminant)
    term3 = g * distance

    angle1 = math.atan((term1 - term2) / term3)
    angle2 = math.atan((term1 + term2) / term3)

    # Return angles in ascending order (low, high)
    return (math.degrees(min(angle1, angle2)), math.degrees(max(angle1, angle2)))
  1. When first iterative search fails, an "integrative" correction can work. From Serhiy:
    summary_error = 0
    summary_error_corrections = 0
    history = []
    while True:
        while zero_finding_error > _cZeroFindingAccuracy and iterations_count < _cMaxIterations:
            # Check height of trajectory at the zero distance (using current self.barrel_elevation)
            try:
               t = self._integrate(shot_info, zero_distance, zero_distance, TrajFlag.NONE)[0]
            except RangeError as e:
               t = e.incomplete_trajectory[0]
            height = t.height >> Distance.Foot
            height_diff = height - height_at_zero
            zero_finding_error = math.fabs(height_diff)
            summary_error+=height_diff
            if zero_finding_error > _cZeroFindingAccuracy:
                # Adjust barrel elevation to close height at zero distance
                self.barrel_elevation -= (height_diff) / zero_distance
                history.append((self.barrel_elevation, height_diff))
            else:  # last barrel_elevation hit zero!
                return Angular.Radian(self.barrel_elevation)
            iterations_count += 1
        if math.fabs(summary_error)>0 and summary_error_corrections<3:
            print(f'Got to integral correction {summary_error=}')
            print(f'Iterations: {len(history)=} minimum deviation by height {min(history, key=lambda x: abs(x[1]))=} {history=}')
            self.barrel_elevation-=(summary_error/iterations_count)/(zero_distance)
            summary_error = 0
            iterations_count=0
            summary_error_corrections += 1
        else:
            if zero_finding_error > _cZeroFindingAccuracy:
                print(f'The search has not converged  Iteration Count {len(history)} '
                      f'minimum deviation by height {min(history, key=lambda x: abs(x[1]))=} {history=}')
                # ZeroFindingError contains an instance of last barrel elevation; so caller can check how close zero is
                raise ZeroFindingError(zero_finding_error, iterations_count, Angular.Radian(self.barrel_elevation))
  1. When that fails, a bracket search (e.g., https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.brenth.html).
@dbookstaber dbookstaber added the enhancement New feature or request label Jan 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant