def RAAN_to_LTAN(RAAN, UTC, EOP_data): ''' This function computes the Local Time of Ascending Node for a sunsynchronous orbit. Parameters ------ RAAN : float Right Ascension of Ascending Node [deg] UTC : datetime object time in UTC EOP_data : dictionary EOP data for the given time including pole coordinates and offsets, time offsets, and length of day Returns ------ LTAN : float local time of ascending node, decimal hour in range [0, 24) ''' # Compute TT in JD format TT_JD = utcdt2ttjd(UTC, EOP_data['TAI_UTC']) # Compute TT in centuries since J2000 epoch TT_cent = jd2cent(TT_JD) # Compute apparent right ascension of the sun sun_eci_geom, sun_eci_app = compute_sun_coords(TT_cent) sun_ra = atan2(sun_eci_app[1], sun_eci_app[0]) * 180./pi # deg # Compute LTAN in decimal hours LTAN = ((RAAN - sun_ra)/15. + 12.) % 24. # decimal hours return LTAN
def teme2gcrf(r_TEME, v_TEME, UTC, IAU1980nut, EOP_data): ''' This function converts position and velocity vectors from the True Equator Mean Equinox (TEME) frame used for TLEs to the GCRF inertial frame. Parameters ------ r_TEME : 3x1 numpy array position vector in TEME frame v_TEME : 3x1 numpy array velocity vector in TEME frame UTC : datetime object time in UTC IAU1980nut : 2D numpy array nutation coefficients EOP_data : dictionary EOP data for the given time including pole coordinates and offsets, time offsets, and length of day Returns ------ r_GCRF : 3x1 numpy array position vector in GCRF frame v_GCRF : 3x1 numpy array velocity vector in GCRF frame ''' # Compute TT in JD format TT_JD = utcdt2ttjd(UTC, EOP_data['TAI_UTC']) # Compute TT in centuries since J2000 epoch TT_cent = jd2cent(TT_JD) # IAU 1976 Precession P = compute_precession_IAU1976(TT_cent) # IAU 1980 Nutation N, FA, Eps0, Eps_true, dPsi, dEps = \ compute_nutation_IAU1980(IAU1980nut, TT_cent, EOP_data['ddPsi'], EOP_data['ddEps']) # Equation of the Equinonx 1982 R = eqnequinox_IAU1982_simple(dPsi, Eps0) # Compute transformation matrix and output GCRF_TEME = np.dot(P, np.dot(N, R)) r_GCRF = np.dot(GCRF_TEME, r_TEME) v_GCRF = np.dot(GCRF_TEME, v_TEME) return r_GCRF, v_GCRF
def itrf2gcrf(r_ITRF, v_ITRF, UTC, EOP_data, XYs_df=[]): ''' This function converts a position and velocity vector in the ITRF(ECEF) frame to the GCRF(ECI) frame using the IAU 2006 precession and IAU 2000A_R06 nutation theories. This routine employs a hybrid of the "Full Theory" using Fukushima-Williams angles and the CIO-based method. Specifically, this routine interpolates a table of X,Y,s values and then uses them to construct the BPN matrix directly. The X,Y,s values in the data table were generated using Fukushima-Williams angles and the IAU 2000A_R06 nutation theory. This general scheme is outlined in [3] and [4]. Parameters ------ r_ITRF : 3x1 numpy array position vector in ITRF v_ITRF : 3x1 numpy array velocity vector in ITRF UTC : datetime object time in UTC EOP_data : dictionary EOP data for the given time including pole coordinates and offsets, time offsets, and length of day Returns ------ r_GCRF : 3x1 numpy array position vector in GCRF v_GCRF : 3x1 numpy array velocity vector in GCRF ''' # Form column vectors r_ITRF = np.reshape(r_ITRF, (3, 1)) v_ITRF = np.reshape(v_ITRF, (3, 1)) # Compute UT1 in JD format UT1_JD = utcdt2ut1jd(UTC, EOP_data['UT1_UTC']) # Compute TT in JD format TT_JD = utcdt2ttjd(UTC, EOP_data['TAI_UTC']) # Compute TT in centuries since J2000 epoch TT_cent = jd2cent(TT_JD) # Construct polar motion matrix (ITRS to TIRS) W = compute_polarmotion(EOP_data['xp'], EOP_data['yp'], TT_cent) # Contruct Earth rotaion angle matrix (TIRS to CIRS) R = compute_ERA(UT1_JD) # Construct Bias-Precessino-Nutation matrix (CIRS to GCRS/ICRS) XYs_data = init_XYs2006(UTC, UTC, XYs_df) X, Y, s = get_XYs(XYs_data, TT_JD) # Add in Free Core Nutation (FCN) correction X = EOP_data['dX'] + X # rad Y = EOP_data['dY'] + Y # rad # Compute Bias-Precssion-Nutation (BPN) matrix BPN = compute_BPN(X, Y, s) # Transform position vector ecef2eci = np.dot(BPN, np.dot(R, W)) r_GCRF = np.dot(ecef2eci, r_ITRF) # Transform velocity vector # Calculate Earth rotation rate, rad/s (Vallado p227) wE = 7.29211514670639e-5 * (1 - EOP_data['LOD'] / 86400) r_TIRS = np.dot(W, r_ITRF) v_GCRF = np.dot( BPN, np.dot(R, (np.dot(W, v_ITRF) + np.cross(np.array([[0.], [0.], [wE]]), r_TIRS, axis=0)))) return r_GCRF, v_GCRF
def batch_eop_rotation_matrices(UTC_list, eop_alldata_text, eop_flag='linear', GMST_only_flag=False): ''' This function generates a list of rotation matrices between TEME/GCRF and GCRF/ITRF for use in coordinate transformations. The function can process a large array of times and has flags to control the level of fidelity of the transformation, in order to facilitate good computational performance for a large number of transforms. Parameters ------ UTC_list : list list of datetime object to compute Frame Rotation matrices for eop_alldata_text : string string containing observed and predicted EOP data, no header information increment : float, optional time increment between desired frame rotations [sec] (default=10.) eop_flag : string, optional flag to determine how to determine EOP parameters from input text data 'zeros' = set all EOP values to zero (~2km error) 'nearest' = set EOP values to nearest whole day (~3m error) 'linear' = linearly interpolate EOP values between 2 nearest days (~20cm error) (default = 'linear') GMST_only_flag : boolean, optional flag to determine whether to apply only the Earth rotation angle for the GCRF/ITRF transformation (i.e., no precession, nutation, polar motion) True = apply GMST Earth rotation angle only (no P, N, W) False = apply full transformation including P, N, W (default = False) Returns ------ GCRF_TEME_list : list of 3x3 numpy arrays transformation matrix at each time for GCRF/TEME r_GCRF = GCRF_TEME * r_TEME ITRF_GCRF_list : list of 3x3 numpy arrays transformation matrix at each time for GCRF/TEME r_ITRF = ITRF_GCRF * r_GCRF ''' # Initialize Output GCRF_TEME_list = [] ITRF_GCRF_list = [] # Constants arcsec2rad = (1. / 3600.) * pi / 180. # Retrieve IAU Nutation data from file IAU1980_nut = get_nutation_data() # Retrieve polar motion data from file XYs_df = get_XYs2006_alldata() # Generate MJD list MJD_list = [dt2mjd(UTC) for UTC in UTC_list] # Loop over times for kk in range(len(MJD_list)): MJD_kk = MJD_list[kk] UTC_kk = UTC_list[kk] # UTC_kk = mjd2dt(MJD_kk) # Compute the appropriate EOP values for this time using the input # text data. Per Reference 4 Table 2-3, the following error levels are # expected for different levels of fidelity in the approximation # Set all EOPs to zero # Expected error level ~2km in GEO if eop_flag == 'zeros': xp = 0. yp = 0. UT1_UTC = 0. LOD = 0. ddPsi = 0. ddEps = 0. dX = 0. dY = 0. TAI_UTC = 0. # Read EOP text data for higher level approximations else: nchar = 102 nskip = 1 nlines = 0 MJD_int = int(MJD_kk) # Get closest lines from EOP text data # First index have to search text data if kk == 0: MJD_prior = copy.copy(MJD_int) # Find EOP data lines around time of interest for ii in range(len(eop_alldata_text)): start = ii + nlines * (nchar + nskip) stop = ii + nlines * (nchar + nskip) + nchar line = eop_alldata_text[start:stop] nlines += 1 MJD_line = int(line[11:16]) if MJD_line == MJD_int: line0 = line if MJD_line == MJD_int + 1: line1 = line break # Otherwise, we can use previous knowledge else: # If it's the same day as last time we can just reuse line0 # and line1. Otherwise, we have to increment nlines to get # the new line1 if MJD_int == MJD_prior + 1: print(MJD_int) print(MJD_prior) print(line0) print(line1) # Find EOP data lines around time of interest for ii in range(len(eop_alldata_text)): start = ii + nlines * (nchar + nskip) stop = ii + nlines * (nchar + nskip) + nchar line = eop_alldata_text[start:stop] nlines += 1 MJD_line = int(line[11:16]) if MJD_line == MJD_int: line0 = line if MJD_line == MJD_int + 1: line1 = line break # line0 = copy.copy(line1) # nlines += 1 # start = ii + nlines*(nchar+nskip) # stop = ii + nlines*(nchar+nskip) + nchar # line1 = eop_alldata_text[start:stop] # # MJD_line = int(line[11:16]) # if MJD_line != MJD_int: # print(MJD_line) # print(MJD_int) # sys.exit('Wrong MJD value encountered!') MJD_prior = MJD_int # print('\n') # print(nlines) # print(start) # print(stop) # print(line0) # print(line1) # # mistake # Read the EOP values for each line line0_array = eop_read_line(line0) line1_array = eop_read_line(line1) # Set all EOPs to values for nearest day # Expected error level ~3.6m max (0.8m RMS) in GEO if eop_flag == 'nearest': if (MJD_kk - MJD_int) < 0.5: xp = line0_array[1] * arcsec2rad yp = line0_array[2] * arcsec2rad UT1_UTC = line0_array[3] LOD = line0_array[4] ddPsi = line0_array[5] * arcsec2rad ddEps = line0_array[6] * arcsec2rad dX = line0_array[7] * arcsec2rad dY = line0_array[8] * arcsec2rad TAI_UTC = line0_array[9] else: xp = line1_array[1] * arcsec2rad yp = line1_array[2] * arcsec2rad UT1_UTC = line1_array[3] LOD = line1_array[4] ddPsi = line1_array[5] * arcsec2rad ddEps = line1_array[6] * arcsec2rad dX = line1_array[7] * arcsec2rad dY = line1_array[8] * arcsec2rad TAI_UTC = line1_array[9] # Linear interpolation of values between two nearest days # Expected error level ~22cm max (4cm RMS) in GEO if eop_flag == 'linear': # Adjust UT1-UTC column in case leap second occurs between lines line0_array[3] -= line0_array[9] line1_array[3] -= line1_array[9] # Leap seconds do not interpolate TAI_UTC = line0_array[9] # Linear interpolation dt = MJD_kk - line0_array[0] interp = (line1_array[1:] - line0_array[1:])/ \ (line1_array[0] - line0_array[0]) * dt + line0_array[1:] # Convert final output xp = interp[0] * arcsec2rad yp = interp[1] * arcsec2rad UT1_UTC = interp[2] + TAI_UTC LOD = interp[3] ddPsi = interp[4] * arcsec2rad ddEps = interp[5] * arcsec2rad dX = interp[6] * arcsec2rad dY = interp[7] * arcsec2rad # Compute rotation matrices for transform # Compute current times UT1_JD = utcdt2ut1jd(UTC_kk, UT1_UTC) TT_JD = utcdt2ttjd(UTC_kk, TAI_UTC) TT_cent = jd2cent(TT_JD) # GCRF/ITRF Transformation # Contruct Earth rotaion angle matrix (TIRS to CIRS) R_CIRS = compute_ERA(UT1_JD) if GMST_only_flag: ITRF_GCRF = R_CIRS.T else: # Construct polar motion matrix (ITRS to TIRS) W = compute_polarmotion(xp, yp, TT_cent) # Construct Bias-Precessino-Nutation matrix (CIRS to GCRS/ICRS) XYs_data = init_XYs2006(UTC_kk, UTC_kk, XYs_df) X, Y, s = get_XYs(XYs_data, TT_JD) # Add in Free Core Nutation (FCN) correction X = dX + X # rad Y = dY + Y # rad # Compute Bias-Precssion-Nutation (BPN) matrix BPN = compute_BPN(X, Y, s) # Transform position vector ITRF_GCRF = np.dot(W.T, np.dot(R_CIRS.T, BPN.T)) # TEME/GCRF Transformation # IAU 1976 Precession P = compute_precession_IAU1976(TT_cent) # IAU 1980 Nutation N, FA, Eps0, Eps_true, dPsi, dEps = \ compute_nutation_IAU1980(IAU1980_nut, TT_cent, ddPsi, ddEps) # Equation of the Equinonx 1982 R_1982 = eqnequinox_IAU1982_simple(dPsi, Eps0) # Compute transformation matrix and output GCRF_TEME = np.dot(P, np.dot(N, R_1982)) # Store output GCRF_TEME_list.append(GCRF_TEME) ITRF_GCRF_list.append(ITRF_GCRF) return GCRF_TEME_list, ITRF_GCRF_list