Esempio n. 1
0
def unit_test_twobody():

    # Orbit Parameter Setup
    params = {}
    params['GM'] = GME
    params['J2'] = J2E
    params['wE'] = wE  # rad/s
    params['Re'] = Re  # km
    params['Cd'] = 2.2
    params['A_m'] = 1e-8  # km^2/kg

    # Integration times
    #    tin = np.array([0., 86400.*2.])
    tin = np.arange(0., 86400. * 2 + 1., 10.)

    # Initial orbit - Molniya
    Xo = np.array([
        2.88824880e3, -7.73903934e2, -5.97116199e3, 2.64414431, 9.86808092, 0.0
    ])

    # Integration function and additional settings
    int_params = {}
    int_params['integrator'] = 'ode'
    int_params['ode_integrator'] = 'dop853'
    int_params['intfcn'] = ode_twobody
    int_params['step'] = 10.
    int_params['rtol'] = 1e-12
    int_params['atol'] = 1e-12
    int_params['local_extrap'] = True

    # Run integrator
    tout, Xout = general_dynamics(Xo, tin, params, int_params)

    print(len(tout))
    print(tout[-1])

    # Analytic solution
    elem = cart2kep(Xo, GM=params['GM'])
    a = elem[0]
    e = elem[1]
    i = elem[2]
    RAAN = elem[3]
    w = elem[4]
    theta0 = elem[5] * pi / 180.
    E0 = true2ecc(theta0, e)
    M0 = ecc2mean(E0, e)
    n = np.sqrt(params['GM'] / a**3.)
    kk = 0
    a_diff = []
    e_diff = []
    i_diff = []
    RAAN_diff = []
    w_diff = []
    theta_diff = []
    energy_diff = []
    pos_diff = []
    vel_diff = []
    for t in tout:

        # Compute new mean anomaly [rad]
        M = M0 + n * (t - tout[0])
        while M > 2 * pi:
            M -= 2 * pi

        # Convert to true anomaly [rad]
        E = mean2ecc(M, e)
        theta = ecc2true(E, e)

        # Convert anomaly angles to deg
        M *= 180. / pi
        E *= 180. / pi
        theta *= 180. / pi

        X_true = kep2cart([a, e, i, RAAN, w, theta], GM=params['GM'])
        elem_true = [a, e, i, RAAN, w, theta]

        # Convert numeric to elements
        elem_num = cart2kep(Xout[kk, :], GM=params['GM'])

        a_diff.append(elem_num[0] - elem_true[0])
        e_diff.append(elem_num[1] - elem_true[1])
        i_diff.append(elem_num[2] - elem_true[2])
        RAAN_diff.append(elem_num[3] - elem_true[3])
        w_diff.append(elem_num[4] - elem_true[4])
        theta_diff.append(elem_num[5] - elem_true[5])
        pos_diff.append(
            np.linalg.norm(X_true[0:3].flatten() - Xout[kk, 0:3].flatten()))
        vel_diff.append(
            np.linalg.norm(X_true[3:6].flatten() - Xout[kk, 3:6].flatten()))

        if RAAN_diff[kk] < 0:
            RAAN_diff[kk] += 360.
        if RAAN_diff[kk] > 180:
            RAAN_diff[kk] -= 360.

        energy_diff.append(params['GM'] / (2 * elem_true[0]) - params['GM'] /
                           (2 * elem_num[0]))

        kk += 1

    plt.figure()
    plt.subplot(3, 1, 1)
    plt.plot(tout / 3600., energy_diff, 'k.')
    plt.ylabel('Energy [km^2/s^2]')
    plt.title('Size and Shape Parameters')
    plt.subplot(3, 1, 2)
    plt.plot(tout / 3600., np.asarray(a_diff), 'k.')
    plt.ylabel('SMA [km]')
    plt.subplot(3, 1, 3)
    plt.plot(tout / 3600., e_diff, 'k.')
    plt.ylabel('Eccentricity')
    plt.xlabel('Time [hours]')

    plt.figure()
    plt.subplot(3, 1, 1)
    plt.plot(tout / 3600., i_diff, 'k.')
    plt.ylabel('Inclination [deg]')
    plt.title('Orientation Parameters')
    plt.subplot(3, 1, 2)
    plt.plot(tout / 3600., RAAN_diff, 'k.')
    plt.ylabel('RAAN [deg]')
    plt.subplot(3, 1, 3)
    plt.plot(tout / 3600., w_diff, 'k.')
    plt.ylabel('AoP [deg]')
    plt.xlabel('Time [hours]')

    plt.figure()
    plt.subplot(2, 1, 1)
    plt.plot(tout / 3600., np.asarray(pos_diff), 'k.')
    plt.ylabel('3D Pos [km]')
    plt.title('Position and Velocity')
    plt.subplot(2, 1, 2)
    plt.plot(tout / 3600., np.asarray(vel_diff), 'k.')
    plt.ylabel('3D Vel [km/s]')
    plt.xlabel('Time [hours]')

    plt.show()

    return
Esempio n. 2
0
def twobody_geo_setup():

    arcsec2rad = pi / (3600. * 180.)

    # Retrieve latest EOP data from celestrak.com
    eop_alldata = get_celestrak_eop_alldata()

    # Retrieve polar motion data from file
    XYs_df = get_XYs2006_alldata()

    # Define state parameters
    state_params = {}
    state_params['GM'] = GME
    state_params['radius_m'] = 1.
    state_params['albedo'] = 0.1

    # Integration function and additional settings
    int_params = {}
    int_params['integrator'] = 'ode'
    int_params['ode_integrator'] = 'dop853'
    int_params['intfcn'] = ode_twobody
    int_params['rtol'] = 1e-12
    int_params['atol'] = 1e-12
    int_params['time_system'] = 'datetime'

    # Time vector
    tk_list = []
    for hr in range(24):
        UTC = datetime(2021, 6, 21, hr, 0, 0)
        tvec = np.arange(0., 601., 60.)
        tk_list.extend([UTC + timedelta(seconds=ti) for ti in tvec])

    # Inital State
    elem = [42164.1, 0.001, 0., 90., 0., 0.]
    X_true = np.reshape(kep2cart(elem), (6, 1))
    P = np.diag([1., 1., 1., 1e-6, 1e-6, 1e-6])
    pert_vect = np.multiply(np.sqrt(np.diag(P)), np.random.randn(6))
    X_init = X_true + np.reshape(pert_vect, (6, 1))

    state_dict = {}
    state_dict[tk_list[0]] = {}
    state_dict[tk_list[0]]['X'] = X_init
    state_dict[tk_list[0]]['P'] = P

    # Sensor and measurement parameters
    sensor_id_list = ['UNSW Falcon']
    sensor_params = define_sensors(sensor_id_list)
    sensor_params['eop_alldata'] = eop_alldata
    sensor_params['XYs_df'] = XYs_df

    for sensor_id in sensor_id_list:
        sensor_params[sensor_id]['meas_types'] = ['ra', 'dec']
        sigma_dict = {}
        sigma_dict['ra'] = 5. * arcsec2rad  # rad
        sigma_dict['dec'] = 5. * arcsec2rad  # rad
        sensor_params[sensor_id]['sigma_dict'] = sigma_dict
    print(sensor_params)

    # Generate truth and measurements
    truth_dict = {}
    meas_fcn = H_radec
    meas_dict = {}
    meas_dict['tk_list'] = []
    meas_dict['Yk_list'] = []
    meas_dict['sensor_id_list'] = []
    X = X_true.copy()
    for kk in range(len(tk_list)):

        if kk > 0:
            tin = [tk_list[kk - 1], tk_list[kk]]
            tout, Xout = general_dynamics(X, tin, state_params, int_params)
            X = Xout[-1, :].reshape(6, 1)

        truth_dict[tk_list[kk]] = X

        # Check visibility conditions and compute measurements
        UTC = tk_list[kk]
        EOP_data = get_eop_data(eop_alldata, UTC)

        for sensor_id in sensor_id_list:
            sensor = sensor_params[sensor_id]
            if check_visibility(X, state_params, sensor, UTC, EOP_data,
                                XYs_df):

                # Compute measurements
                Yk = compute_measurement(X,
                                         state_params,
                                         sensor,
                                         UTC,
                                         EOP_data,
                                         XYs_df,
                                         meas_types=sensor['meas_types'])

                Yk[0] += np.random.randn() * sigma_dict['ra']
                Yk[1] += np.random.randn() * sigma_dict['dec']

                meas_dict['tk_list'].append(UTC)
                meas_dict['Yk_list'].append(Yk)
                meas_dict['sensor_id_list'].append(sensor_id)

    # Plot data
    tplot = [(tk - tk_list[0]).total_seconds() / 3600. for tk in tk_list]
    xplot = []
    yplot = []
    zplot = []
    for tk in tk_list:
        X = truth_dict[tk]
        xplot.append(X[0])
        yplot.append(X[1])
        zplot.append(X[2])

    meas_tk = meas_dict['tk_list']
    meas_tplot = [(tk - tk_list[0]).total_seconds() / 3600. for tk in meas_tk]
    meas_sensor_id = meas_dict['sensor_id_list']
    meas_sensor_index = [
        sensor_id_list.index(sensor_id) for sensor_id in meas_sensor_id
    ]

    plt.figure()
    plt.subplot(3, 1, 1)
    plt.plot(tplot, xplot, 'k.')
    plt.ylabel('X [km]')
    plt.subplot(3, 1, 2)
    plt.plot(tplot, yplot, 'k.')
    plt.ylabel('Y [km]')
    plt.subplot(3, 1, 3)
    plt.plot(tplot, zplot, 'k.')
    plt.ylabel('Z [km]')
    plt.xlabel('Time [hours]')

    plt.figure()
    plt.plot(meas_tplot, meas_sensor_index, 'k.')
    plt.xlabel('Time [hours]')
    plt.ylabel('Sensor ID')

    plt.show()

    print(meas_dict)

    pklFile = open('twobody_geo_setup.pkl', 'wb')
    pickle.dump([
        state_dict, state_params, int_params, meas_fcn, meas_dict,
        sensor_params, truth_dict
    ], pklFile, -1)
    pklFile.close()

    return
Esempio n. 3
0
def ls_batch(state_dict, truth_dict, meas_dict, meas_fcn, state_params,
             sensor_params, int_params):
    '''
    This function implements the linearized batch estimator for the least
    squares cost function.

    Parameters
    ------
    state_dict : dictionary
        initial state and covariance for filter execution
    meas_dict : dictionary
        measurement data over time for the filter and parameters (noise, etc)

    meas_fcn : function handle
        function for measurements


    Returns
    ------
    filter_output : dictionary
        output state, covariance, and post-fit residuals over time
    '''

    # State information
    state_tk = sorted(state_dict.keys())[-1]
    Xo_ref = state_dict[state_tk]['X']
    Po_bar = state_dict[state_tk]['P']

    # Setup
    cholPo = np.linalg.inv(np.linalg.cholesky(Po_bar))
    invPo_bar = np.dot(cholPo.T, cholPo)

    n = len(Xo_ref)

    # Initialize output
    filter_output = {}

    # Measurement times
    tk_list = meas_dict['tk_list']
    Yk_list = meas_dict['Yk_list']
    sensor_id_list = meas_dict['sensor_id_list']

    # Number of epochs
    L = len(tk_list)

    # Initialize
    maxiters = 10
    xo_bar = np.zeros((n, 1))
    xo_hat = np.zeros((n, 1))
    phi0 = np.identity(n)
    phi0_v = np.reshape(phi0, (n**2, 1))

    # Begin Loop
    iters = 0
    xo_hat_mag = 1
    conv_crit = 1e-5
    while xo_hat_mag > conv_crit:

        # Increment loop counter and exit if necessary
        iters += 1
        if iters > maxiters:
            iters -= 1
            print('Solution did not converge in ', iters, ' iterations')
            print('Last xo_hat magnitude: ', xo_hat_mag)
            break

        # Initialze values for this iteration
        Xref_list = []
        phi_list = []
        resids_list = []
        phi_v = phi0_v.copy()
        Xref = Xo_ref.copy()
        Lambda = invPo_bar.copy()
        N = np.dot(Lambda, xo_bar)

        # Loop over times
        for kk in range(L):

            # Current and previous time
            if kk == 0:
                tk_prior = state_tk
            else:
                tk_prior = tk_list[kk - 1]

            tk = tk_list[kk]

            # Read the next observation
            Yk = Yk_list[kk]
            sensor_id = sensor_id_list[kk]

            # Initialize
            Xref_prior = Xref.copy()

            # Initial Conditions for Integration Routine
            int0 = np.concatenate((Xref_prior, phi_v))

            # Integrate Xref and STM
            if tk_prior == tk:
                intout = int0.T
            else:
                int0 = int0.flatten()
                tin = [tk_prior, tk]

                tout, intout = general_dynamics(int0, tin, state_params,
                                                int_params)

            # Extract values for later calculations
            xout = intout[-1, :]
            Xref = xout[0:n].reshape(n, 1)
            phi_v = xout[n:].reshape(n**2, 1)
            phi = np.reshape(phi_v, (n, n))

            # Accumulate the normal equations
            Hk_til, Gk, Rk = meas_fcn(tk, Xref, state_params, sensor_params,
                                      sensor_id)
            yk = Yk - Gk
            Hk = np.dot(Hk_til, phi)
            cholRk = np.linalg.inv(np.linalg.cholesky(Rk))
            invRk = np.dot(cholRk.T, cholRk)

            Lambda += np.dot(Hk.T, np.dot(invRk, Hk))
            N += np.dot(Hk.T, np.dot(invRk, yk))

            # Store output
            resids_list.append(yk)
            Xref_list.append(Xref)
            phi_list.append(phi)

#            print(kk)
#            print(tk)
#            print(int0)
#            print(Xref)
#            print(Yk)
#            print(Gk)
#            print(yk)
#
#            if kk > 0:
#                mistake

# Solve the normal equations
        cholLam_inv = np.linalg.inv(np.linalg.cholesky(Lambda))
        Po = np.dot(cholLam_inv.T, cholLam_inv)
        xo_hat = np.dot(Po, N)
        xo_hat_mag = np.linalg.norm(xo_hat)

        # Update for next batch iteration
        Xo_ref = Xo_ref + xo_hat
        xo_bar = xo_bar - xo_hat

        print('Iteration Number: ', iters)
        print('xo_hat_mag = ', xo_hat_mag)

    # Form output
    for kk in range(L):
        tk = tk_list[kk]
        X = Xref_list[kk]
        resids = resids_list[kk]
        phi = phi_list[kk]
        P = np.dot(phi, np.dot(Po, phi.T))

        filter_output[tk] = {}
        filter_output[tk]['X'] = X
        filter_output[tk]['P'] = P
        filter_output[tk]['resids'] = resids

    # Integrate over full time
    tk_truth = list(truth_dict.keys())
    phi_v = phi0_v.copy()
    Xref = Xo_ref.copy()
    full_state_output = {}
    for kk in range(len(tk_truth)):

        # Current and previous time
        if kk == 0:
            tk_prior = state_tk
        else:
            tk_prior = tk_truth[kk - 1]

        tk = tk_truth[kk]

        # Initial Conditions for Integration Routine
        Xref_prior = Xref.copy()
        int0 = np.concatenate((Xref_prior, phi_v))

        # Integrate Xref and STM
        if tk_prior == tk:
            intout = int0.T
        else:
            int0 = int0.flatten()
            tin = [tk_prior, tk]

            tout, intout = general_dynamics(int0, tin, state_params,
                                            int_params)

        # Extract values for later calculations
        xout = intout[-1, :]
        Xref = xout[0:n].reshape(n, 1)
        phi_v = xout[n:].reshape(n**2, 1)
        phi = np.reshape(phi_v, (n, n))
        P = np.dot(phi, np.dot(Po, phi.T))

        full_state_output[tk] = {}
        full_state_output[tk]['X'] = Xref
        full_state_output[tk]['P'] = P

    return filter_output, full_state_output
Esempio n. 4
0
def balldrop_setup():

    # Define state parameters
    acc = 9.81  #m/s^2
    state_params = {}
    state_params['acc'] = acc
    state_params['Q'] = np.diag([1e-12, 1e-12])

    # Integration function and additional settings
    int_params = {}
    int_params['integrator'] = 'ode'
    int_params['ode_integrator'] = 'dop853'
    int_params['intfcn'] = ode_balldrop
    int_params['rtol'] = 1e-12
    int_params['atol'] = 1e-12
    int_params['time_system'] = 'seconds'

    # Time vector
    tk_list = np.arange(0., 100.1, 1.)

    # Inital State
    X_true = np.array([[0.], [0.]])
    P = np.array([[4., 0.], [0., 1.]])
    pert_vect = np.multiply(np.sqrt(np.diag(P)), np.random.randn(2))
    X_init = X_true + np.reshape(pert_vect, (2, 1))

    state_dict = {}
    state_dict[tk_list[0]] = {}
    state_dict[tk_list[0]]['X'] = X_init
    state_dict[tk_list[0]]['P'] = P

    # Generate Truth and Measurements
    truth_dict = {}
    meas_dict = {}
    meas_dict['tk_list'] = []
    meas_dict['Yk_list'] = []
    meas_dict['sensor_id_list'] = []
    sensor_params = {}
    sensor_params[1] = {}
    sig_y = 0.01
    sig_dy = 0.001
    sensor_params[1]['sigma_dict'] = {}
    sensor_params[1]['sigma_dict']['y'] = sig_y
    sensor_params[1]['sigma_dict']['dy'] = sig_dy
    sensor_params[1]['meas_types'] = ['y', 'dy']
    meas_fcn = H_balldrop
    outlier_inds = []
    X = X_true.copy()

    for kk in range(len(tk_list)):

        if kk > 0:
            tin = [tk_list[kk - 1], tk_list[kk]]
            tout, Xout = general_dynamics(X, tin, state_params, int_params)
            X = Xout[-1, :].reshape(2, 1)

        truth_dict[tk_list[kk]] = X

        if kk in outlier_inds:
            y_noise = 100. * sig_y * np.random.randn()
        else:
            y_noise = sig_y * np.random.randn()

        dy_noise = sig_dy * np.random.randn()

        y_meas = float(X[0]) + y_noise
        dy_meas = float(X[1]) + dy_noise
        meas_dict['tk_list'].append(tk_list[kk])
        meas_dict['Yk_list'].append(np.array([[y_meas], [dy_meas]]))
        meas_dict['sensor_id_list'].append(1)

    return state_dict, state_params, int_params, meas_fcn, meas_dict, sensor_params, truth_dict