geopositioning

Implementation of position determination function


I'm attempting to implement position determination function of an aircraft to get "latitude/longitude azimuth" I attached 3 images for summerized formula as you may see this is 5-step trigonometric equation (Steps 0/4) which is my aim to program. image1; image2 image3

To find aircraft coordinates there are defined 9 input parameters (image1): Station U and S latitude,longitude,altitude; Aircraft altitude and 2 slant ranges. At the end of problem (image3) we will find 3 outputs: Aircraft latitude/longitude azimuth.


Solution

  • This code implements the solution explained for: How can I triangulate a position using two DMEs? on aviation.se. The code is in Python, which I happen to use instead of C, it's easy to convert into another language as required. I've broken down the calculation in smaller units to make code more legible and to ease your understanding.

    The problem involves 3 points in 3D space: U and S are the DMEs, A is the aircraft.

    enter image description here

    As we just need the coordinates of U and S, to determinate A coordinates, I'm using coordinates of 3 well known DME stations. This will allow to check whether the result is correct. View based on the Low Altitude Enroute Chart:

    enter image description here

    When the program is run, the output is:

    CAN: lat 49.17319, lon -0.4552778, alt 82
    EVX: lat 49.03169, lon 1.220861, alt 152
    P north: lat 49.386910325692874, lon 0.646650777948733, alt 296
    P south: lat 48.78949175956114, lon 0.5265322105880027, alt 296
    

    First are the coordinates of points U (CAN DME) and S (EVX DME) we entered, and then two lines for the two possible location of the aircraft.

    I made another test with DME at longer distance (1241 km for ARE and 557.1 km for GLA) which worked pretty good too:

    ARE: lat 48.33264, lon -3.602472, alt 50
    GLA: lat 46.40861, lon 6.244222, alt 1000
    P north: lat 48.082101174246304, lon 13.210754399535269, alt 10
    P south: lat 41.958725412109445, lon 9.470999690780628, alt 10
    

    The actual location of the aircraft is supposed to be SZA navaid, in south of France: Lat 41.937°, lon 9.399°.


    from math import asin, sqrt, cos, sin, atan2, acos, pi, radians, degrees
    
    # Earth radius in meters (https://rechneronline.de/earth-radius/)
    E_RADIUS = 6367 * 1000  # at 45°N - Adjust as required
    
    
    def step_0(r_e, h_u, h_s, h_a, d_ua, d_sa):
        # Return angular distance between each station U/S and aircraft
        # Triangle UCA and SCA: The three sides are known,
    
        a = (d_ua - h_a + h_u) * (d_ua + h_a - h_u)
        b = (r_e + h_u) * (r_e + h_a)
        theta_ua = 2 * asin(.5 * sqrt(a / b))
    
        a = (d_sa - h_a + h_s) * (d_sa + h_a - h_s)
        b = (r_e + h_s) * (r_e + h_a)
        theta_sa = 2 * asin(.5 * sqrt(a / b))
    
        # Return angular distances between stations and aircraft
        return theta_ua, theta_sa
    
    
    def step_1(lat_u, lon_u, lat_s, lon_s):
        # Determine angular distance between the two stations
        # and the relative azimuth of one to the other.
    
        a = sin(.5 * (lat_s - lat_u))
        b = sin(.5 * (lon_s - lon_u))
        c = sqrt(a * a + cos(lat_s) * cos(lat_u) * b * b)
        theta_us = 2 * asin(c)
    
        a = lon_s - lon_u
        b = cos(lat_s) * sin(a)
        c = sin(lat_s) * cos(lat_u)
        d = cos(lat_s) * sin(lat_u) * cos(a)
        psi_su = atan2(b, c - d)
    
        return theta_us, psi_su
    
    
    def step_2(theta_us, theta_ua, theta_sa):
        # Determine whether DME spheres intersect
    
        if (theta_ua + theta_sa) < theta_us:
            # Spheres are too remote to intersect
            return False
    
        if abs(theta_ua - theta_sa) > theta_us:
            # Spheres are concentric and don't intersect
            return False
    
        # Spheres intersect
        return True
    
    
    def step_3(theta_us, theta_ua, theta_sa):
        # Determine one angle of the USA triangle
    
        a = cos(theta_sa) - cos(theta_us) * cos(theta_ua)
        b = sin(theta_us) * sin(theta_ua)
        beta_u = acos(a / b)
    
        return beta_u
    
    
    def step_4(ac_south, lat_u, lon_u, beta_u, psi_su, theta_ua):
        # Determine aircraft coordinates
    
        psi_au = psi_su
        if ac_south:
            psi_au += beta_u
        else:
            psi_au -= beta_u
    
        # Determine aircraft latitude
        a = sin(lat_u) * cos(theta_ua)
        b = cos(lat_u) * sin(theta_ua) * cos(psi_au)
        lat_a = asin(a + b)
    
        # Determine aircraft longitude
        a = sin(psi_au) * sin(theta_ua)
        b = cos(lat_u) * cos(theta_ua)
        c = sin(lat_u) * sin(theta_ua) * cos(psi_au)
        lon_a = atan2(a, b - c) + lon_u
    
        return lat_a, lon_a
    
    
    def main():
        # Program entry point
        # -------------------
    
        # For this test, I'm using three locations in France:
        # VOR Caen, VOR Evreux and VOR L'Aigle.
        # The angles and horizontal distances between them are known
        # by looking at the low-altitude enroute chart which I've posted
        # here: https://i.sstatic.net/m8Wmw.png
        # We know there coordinates and altitude by looking at the AIP France too.
        # For DMS <--> Decimal degrees, this tool is handy:
        # https://www.rapidtables.com/convert/number/degrees-minutes-seconds-to-degrees.html
    
        # Let's pretend the aircraft is at LGL
        # lat = 48.79061, lon = 0.5302778
    
        # Stations U and S are:
        u = {'label': 'CAN', 'lat': 49.17319, 'lon': -0.4552778, 'alt': 82}
        s = {'label': 'EVX', 'lat': 49.03169, 'lon': 1.220861, 'alt': 152}
    
        # We know the aircraft altitude
        a_alt = 296  # meters
    
        # We know the approximate slant ranges to stations U and S
        au_range = 45 * 1852  # 1 NM = 1,852 m
        as_range = 31 * 1852
    
        # Compute angles station - earth center - aircraft for U and S
        # Expected values UA: 0.0130890288 rad
        #                 SA: 0.0090168045 rad
        theta_ua, theta_sa = step_0(
            r_e=E_RADIUS,  # Earth
            h_u=u['alt'],  # Station U altitude
            h_s=s['alt'],  # Station S altitude
            h_a=a_alt, d_ua=au_range, d_sa=as_range  # aircraft data
        )
    
        # Compute angle between station, and their relative azimuth
        # We need to convert angles into radians
        theta_us, psi_su = step_1(
            lat_u=radians(u['lat']), lon_u=radians(u['lon']),  # Station U coordinates
            lat_s=radians(s['lat']), lon_s=radians(s['lon']))   # Station S coordinates
    
        # Check validity of ranges
        if not step_2(
                theta_us=theta_us,
                theta_ua=theta_ua,
                theta_sa=theta_sa):
            # Cannot compute, spheres don't intersect
            print('Cannot compute, ranges are not consistant')
            return 1
    
        # Solve one angle of the USA triangle
        beta_u = step_3(
            theta_us=theta_us,
            theta_ua=theta_ua,
            theta_sa=theta_sa)
    
        # Compute aircraft coordinates.
        # The first parameter is whether the aircraft is south of the line
        # between U and S. If you don't know, then you need to compute
        # both, once with ac_south = False, once with ac_south = True.
        # You will get the two possible positions, one must be eliminated.
    
        # North position
        lat_n, lon_n = step_4(
            ac_south=False,  # See comment above
            lat_u=radians(u['lat']), lon_u=radians(u['lon']),  # Station U
            beta_u=beta_u, psi_su=psi_su, theta_ua=theta_ua  # previously computed
        )
        pn = {'label': 'P north', 'lat': degrees(lat_n), 'lon': degrees(lon_n), 'alt': a_alt}
    
        # South position
        lat_s, lon_s = step_4(
            ac_south=True,
            lat_u=radians(u['lat']), lon_u=radians(u['lon']),
            beta_u=beta_u, psi_su=psi_su, theta_ua=theta_ua)
        ps = {'label': 'P south', 'lat': degrees(lat_s), 'lon': degrees(lon_s), 'alt': a_alt}
    
        # Print results
        fmt = '{}: lat {}, lon {}, alt {}'
        for p in u, s, pn, ps:
            print(fmt.format(p['label'], p['lat'], p['lon'], p['alt']))
    
        # The expected result is about:
        #   CAN: lat 49.17319, lon -0.4552778, alt 82
        #   EVX: lat 49.03169, lon 1.220861, alt 152
        #   P north: lat ??????, lon ??????, alt 296
        #   P south: lat 48.79061, lon 0.5302778, alt 296
    
    
    if __name__ == '__main__':
        main()