RMS/Astrometry/Conversions.py
def geocentricToApparentRadiantAndVelocity(ra_g, dec_g, vg, lat, lon, elev, jd, include_rotation=True):
    """ Converts the geocentric into apparent meteor radiant and velocity. The conversion is not perfect
        as the zenith attraction correction should be done after the radiant has been derotated for Earth's
        velocity, but it's precise to about 0.1 deg.

    Arguments:
        ra_g: [float] Geocentric right ascension (deg).
        dec_g: [float] Declination (deg).
        vg: [float] Geocentric velocity (m/s).
        lat: [float] State vector latitude (deg)
        lon: [float] State vector longitude (deg).
        ele: [float] State vector elevation (meters).
        jd: [float] Julian date.
    Keyword arguments:
        include_rotation: [bool] Whether the velocity should be corrected for Earth's rotation.
            True by default.
    Return:
        (ra, dec, v_init): Apparent radiant (deg) and velocity (m/s).
    """

    # Compute ECI coordinates of the meteor state vector
    state_vector = geo2Cartesian(lat, lon, elev, jd)

    eci_x, eci_y, eci_z = state_vector

    # Assume that the velocity at infinity corresponds to the initial velocity
    v_init = np.sqrt(vg**2 + (2*6.67408*5.9722)*1e13/vectMag(state_vector))

    # Calculate the geocentric latitude (latitude which considers the Earth as an elipsoid) of the reference
    # trajectory point
    lat_geocentric = np.degrees(math.atan2(eci_z, math.sqrt(eci_x**2 + eci_y**2)))

    ### Uncorrect for zenith attraction ###

    # Compute the radiant in the local coordinates
    azim, elev = raDec2AltAz(ra_g, dec_g, jd, lat_geocentric, lon)

    # Compute the zenith angle
    eta = np.radians(90.0 - elev)

    # Numerically correct for zenith attraction
    diff = 10e-5
    zc = eta
    while diff > 10e-6:
        # Update the zenith distance
        zc -= diff

        # Calculate the zenith attraction correction
        delta_zc = 2*math.atan((v_init - vg)*math.tan(zc/2.0)/(v_init + vg))
        diff = zc + delta_zc - eta

    # Compute the uncorrected geocentric radiant for zenith attraction
    ra, dec = altAz2RADec(azim, 90.0 - np.degrees(zc), jd, lat_geocentric, lon)

    ### ###

    # Apply the rotation correction
    if include_rotation:
        # Calculate the velocity of the Earth rotation at the position of the reference trajectory point (m/s)
        v_e = 2*math.pi*vectMag(state_vector)*math.cos(np.radians(lat_geocentric))/86164.09053

        # Calculate the equatorial coordinates of east from the reference position on the trajectory
        azimuth_east = 90.0
        altitude_east = 0
        ra_east, dec_east = altAz2RADec(azimuth_east, altitude_east, jd, lat, lon)

        # Compute the radiant vector in ECI coordinates of the apparent radiant
        v_ref_vect = v_init*np.array(raDec2Vector(ra, dec))

        v_ref_nocorr = np.zeros(3)

        # Calculate the derotated reference velocity vector/radiant
        v_ref_nocorr[0] = v_ref_vect[0] + v_e*np.cos(np.radians(ra_east))
        v_ref_nocorr[1] = v_ref_vect[1] + v_e*np.sin(np.radians(ra_east))
        v_ref_nocorr[2] = v_ref_vect[2]

        # Compute the radiant without Earth's rotation included
        ra_norot, dec_norot = vector2RaDec(vectNorm(v_ref_nocorr))
        v_init_norot = vectMag(v_ref_nocorr)

        ra = ra_norot
        dec = dec_norot
        v_init = v_init_norot

    return ra, dec, v_init
RMS/Math.py
def cartesianToPolar(x, y, z):
    """ Converts 3D cartesian coordinates to polar coordinates. 

    Arguments:
        x: [float] Px coordinate.
        y: [float] Py coordinate.
        z: [float] Pz coordinate.

    Return:
        (theta, phi): [float] Polar angles in radians (inclination, azimuth).

    """

    theta = np.arccos(z)
    phi = np.arctan2(y, x)

    return theta, phi
RMS/Formats/FFfile.py
def filenameToDatetime(file_name):
    """ Converts FF bin file name to a datetime object.

    Arguments:
        file_name: [str] Name of a FF file.

    Return:
        [datetime object] Date and time of the first frame in the FF file.

    """

    # e.g.  FF499_20170626_020520_353_0005120.bin
    # or FF_CA0001_20170626_020520_353_0005120.fits

    file_name = file_name.split('_')

    # Check the number of list elements, and the new fits format has one more underscore
    i = 0
    if len(file_name[0]) == 2:
        i = 1

    date = file_name[i + 1]
    year = int(date[:4])
    month = int(date[4:6])
    day = int(date[6:8])

    time = file_name[i + 2]
    hour = int(time[:2])
    minute = int(time[2:4])
    seconds = int(time[4:6])

    ms = int(file_name[i + 3])


    return datetime.datetime(year, month, day, hour, minute, seconds, ms*1000)
RMS/Astrometry/Conversions.py
def raDec2Vector(ra, dec):
    """ Convert stellar equatorial coordinates to a vector with X, Y and Z components.
    @param ra: [float] right ascension in degrees
    @param dec: [float] declination in degrees
    @return (x, y, z): [tuple of floats]
    """

    ra_rad = math.radians(ra)
    dec_rad = math.radians(dec)

    xt = math.cos(dec_rad)*math.cos(ra_rad)
    yt = math.cos(dec_rad)*math.sin(ra_rad)
    zt = math.sin(dec_rad)

    return xt, yt, zt
RMS/Astrometry/Conversions.py
def datetime2JD(dt, UT_corr=0.0):
    """ Converts a datetime object to Julian date.
    Arguments:
        dt: [datetime object]
    Keyword arguments:
        UT_corr: [float] UT correction in hours (difference from local time to UT)
    Return:
        jd: [float] Julian date
    """

    return date2JD(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond/1000.0,
                   UT_corr=UT_corr)
RMS/Math.py
def isAngleBetween(left, ang, right):
    """ Checks if ang is between the angle on the left and right. 
    
    Arguments:
        left: [float] Left (counter-clockwise) angle (radians).
        ang: [float] Angle to check (radians),
        right: [float] Right (clockwise) angle (radiant).

    Return:
        [bool] True if the angle is in between, false otherwise.
    """

    if right - left < 0:
        right = right - left + 2*np.pi
    else:
        right = right - left


    if ang - left < 0:
        ang = ang - left + 2*np.pi
    else:
        ang = ang - left


    return ang < right
RMS/Math.py
def angularSeparation(ra1, dec1, ra2, dec2):
    """ Calculates the angle between two points on a sphere. 
    
    Arguments:
        ra1: [float] Right ascension 1 (radians).
        dec1: [float] Declination 1 (radians).
        ra2: [float] Right ascension 2 (radians).
        dec2: [float] Declination 2 (radians).

    Return:
        [float] Angle between two coordinates (radians).
    """

    # Classical method
    return np.arccos(np.sin(dec1)*np.sin(dec2) + np.cos(dec1)*np.cos(dec2)*np.cos(ra2 - ra1))

    # # Compute the angular separation using the haversine formula
    # #   Source: https://idlastro.gsfc.nasa.gov/ftp/pro/astro/gcirc.pro
    # deldec2 = (dec2 - dec1)/2.0
    # delra2 =  (ra2 - ra1)/2.0
    # sindis = np.sqrt(np.sin(deldec2)*np.sin(deldec2) \
    #     + np.cos(dec1)*np.cos(dec2)*np.sin(delra2)*np.sin(delra2))
    # dis = 2.0*np.arcsin(sindis) 

    # return dis
RMS/Astrometry/Conversions.py
def jd2Date(jd, UT_corr=0, dt_obj=False):
    """ Converts the given Julian date to (year, month, day, hour, minute, second, millisecond) tuple.
    Arguments:
        jd: [float] Julian date
    Keyword arguments:
        UT_corr: [float] UT correction in hours (difference from local time to UT)
        dt_obj: [bool] returns a datetime object if True. False by default.
    Return:
        (year, month, day, hour, minute, second, millisecond)
    """

    dt = timedelta(days=jd)

    try:
        date = dt + JULIAN_EPOCH - J2000_JD + timedelta(hours=UT_corr)

    # If the date is out of range (i.e. before year 1) use year 1. This is the limitation in the datetime
    # library. Time handling should be switched to astropy.time
    except OverflowError:
        date = datetime(MINYEAR, 1, 1, 0, 0, 0)

    # Return a datetime object if dt_obj == True
    if dt_obj:
        return date

    return date.year, date.month, date.day, date.hour, date.minute, date.second, date.microsecond/1000.0
RMS/Routines/AllskyPlot.py
class AllSkyPlot(object):
	def __init__(self, ax_handle=None):

		self.ra0 = 180.0


		if ax_handle is None:
			
			self.fig = plt.figure()
			self.ax = self.fig.add_subplot(1, 1, 1, facecolor='black')

		else:
			self.ax = ax_handle
			self.fig = plt.gcf()

		# Set background color
		self.fig.patch.set_facecolor('black')

		# Set equal aspect ratio
		self.ax.set_aspect('equal')

		# # Set tick color
		# self.ax.tick_params(axis='x', colors='0.5')
		# self.ax.tick_params(axis='y', colors='0.5')

		# Turn off ticks
		self.ax.tick_params(labeltop=False, labelright=False, labelbottom=False, labelleft=False)

		self.plotGrid()


	def raDec2XY(self, ra, dec):

		# Compute projected coordinates
		x = ((180 - ra)%360 - self.ra0)*np.cos(np.radians(dec))
		y = dec

		return x, y


	def plot(self, ra_array, dec_array, max_break_deg=30, **kwargs):

		# If there are more than one point, check for 0/360 wraparounds in RA
		if isinstance(ra_array, list) or isinstance(ra_array, np.ndarray):

			ra_array = np.array(ra_array)
			ra_array = (180 - ra_array)%360
			dec_array = np.array(dec_array)

			coord_list = []

			# Find large breaks in RA and plot them separately
			ra_diff = np.abs(ra_array[:-1] - ra_array[1:])
			break_indices = np.where(ra_diff > max_break_deg)[0]

			if not len(break_indices):
				coord_list = [[ra_array, dec_array]]

			else:
				prev_break_idx = 0
				for break_idx in break_indices:
					ra_temp = ra_array[prev_break_idx:break_idx + 1]
					dec_temp = dec_array[prev_break_idx:break_idx + 1]

					prev_break_idx = break_idx + 1

					coord_list.append([ra_temp, dec_temp])

				coord_list.append([ra_array[break_idx + 1:], dec_array[break_idx + 1:]])

		else:
			coord_list = [[180 - ra_array, dec_array]]


		# Plot all segments
		for i, (ra_temp, dec_temp) in enumerate(coord_list):
			x, y = self.raDec2XY(180 - ra_temp, dec_temp)

			# Make sure that all plotted lines have the same color
			if i > 0:
				color = plt_handle[0].get_color()

				# Add color to kwargs
				if 'color' not in kwargs:
					kwargs['color'] = color
			

			plt_handle = self.ax.plot(x, y, **kwargs)



	def scatter(self, ra_array, dec_array, **kwargs):

		x, y = self.raDec2XY(ra_array, dec_array)
		self.ax.scatter(x, y, **kwargs)




	def plotGrid(self, step=15):


		# Plot a meridian and parallel grid
		ra_grid = np.sort(np.append(np.arange(0, 360 + step, step), [180.0001]))
		dec_grid = np.arange(-90, 90 + step, step)


		# Plot meridians
		for ra in ra_grid[:-1]:

			# Increase number of points for meridian plot so they are smoother
			step_finer = step/5
			dec_arr = np.arange(-90, 90 + step_finer, step_finer)

			ra_temp = np.zeros_like(dec_arr) + ra

			x_grid, y_grid = self.raDec2XY(ra_temp, dec_arr)

			self.ax.plot(x_grid, y_grid, linestyle='dotted', alpha=0.5, color='silver')


		# Plot parallels
		for dec in dec_grid:

			dec_temp = np.zeros_like(ra_grid) + dec

			self.plot(ra_grid, dec_temp, linestyle='dotted', alpha=0.5, color='silver')


		# Plot dec ticks
		for dec in dec_grid[::2]:

			x, y = self.raDec2XY(0, dec)

			if dec > 0:
				va = 'bottom'

			else:
				va = 'top'

			self.ax.text(x, y, "{:+d}$^\circ$".format(dec), color='0.5', ha='center', va=va, size=7)


		# Plot every other RA tick and skip 0 and 360
		ra_ticks = np.sort(np.append(np.arange(0, 360, 2*step), [180.0001]))
		for ra in ra_ticks:

			# Offset RA so 0 starts in the middle and increases to the left
			#ra_text = (180 - ra)%360

			x, y = self.raDec2XY(ra, 0)
			self.ax.text(x, y, "{:+d}$^\circ$".format(int(ra)), color='0.5', ha='center', va='top', size=7)




	
	def beautify(self):

		self.ax.set_xlim([-180, 180])
		self.ax.set_ylim([-90, 90])

		self.fig.tight_layout()


	def show(self):

		self.beautify()
		plt.show()
RMS/Math.py
def vectNorm(vect):
    """ Convert a given vector to a unit vector. """

    return vect/vectMag(vect)
RMS/Astrometry/Conversions.py
def vector2RaDec(eci):
    """ Convert Earth-centered intertial vector to right ascension and declination.
    Arguments:
        eci: [3 element ndarray] Vector coordinates in Earth-centered inertial system
    Return:
        (ra, dec): [tuple of floats] right ascension and declinaton (degrees)
    """

    # Normalize the ECI coordinates
    eci = vectNorm(eci)

    # Calculate declination
    dec = np.arcsin(eci[2])

    # Calculate right ascension
    ra = np.arctan2(eci[1], eci[0])%(2*np.pi)

    return np.degrees(ra), np.degrees(dec)
RMS/Math.py
def angularSeparationVect(vect1, vect2):
    """ Calculates angle between vectors in radians. """

    return np.abs(np.arccos(np.dot(vect1, vect2)))
RMS/Routines/SolarLongitude.py
def jd2SolLonSteyaert(jd):
    """ Convert the given Julian date to solar longitude, J2000.0 epoch. Chris Steyaert method.

    Reference: Steyaert, C. (1991). Calculating the solar longitude 2000.0. WGN, Journal of the International 
        Meteor Organization, 19, 31-34.

    Arguments:
        jd: [float] julian date

    Return:
        [float] solar longitude in radians, J2000.0 epoch

    """

    # Define time constants
    A0 = [334166, 3489, 350, 342, 314, 268, 234, 132, 127, 120, 99, 90, 86, 78, 75, 51, 49, 36, 32, 28, 27, 
        24, 21, 21, 20, 16, 13, 13]

    B0 = [4.669257, 4.6261, 2.744, 2.829, 3.628, 4.418, 6.135, 0.742, 2.037, 1.110, 5.233, 2.045, 3.508, 
        1.179, 2.533, 4.58, 4.21, 2.92, 5.85, 1.90, 0.31, 0.34, 4.81, 1.87, 2.46, 0.83, 3.41, 1.08]

    C0 = [6283.07585, 12566.1517, 5753.385, 3.523, 77713.771, 7860.419, 3930.210, 11506.77, 529.691, 1577.344, 
        5884.927, 26.298, 398.149, 5223.694, 5507.553, 18849.23, 775.52, 0.07, 11790.63, 796.3, 10977.08, 
        5486.78, 2544.31, 5573.14, 6069.78, 213.3, 2942.46, 20.78]

    A1 = [20606, 430, 43]
    B1 = [2.67823, 2.635, 1.59]
    C1 = [6283.07585, 12566.152, 3.52]

    A2 = [872, 29]
    B2 = [1.073, 0.44]
    C2 = [6283.07585, 12566.15]

    A3 = 29
    B3 = 5.84
    C3 = 6283.07585

    # Number of millennia since 2000
    T = (jd - 2451545.0)/365250.0

    # Mean solar longitude
    L0 = 4.8950627 + 6283.07585*T - 0.0000099*T**2

    # Wrap L0 to [0, 2pi] range
    L0 = L0%(2*np.pi)

    # Periodical terms
    S0 = np.sum([A0[i]*np.cos((B0[i] + C0[i]*T)%(2*np.pi)) for i in range(28)])
    S1 = np.sum([A1[i]*np.cos((B1[i] + C1[i]*T)%(2*np.pi)) for i in range(3)])
    S2 = np.sum([A2[i]*np.cos((B2[i] + C2[i]*T)%(2*np.pi)) for i in range(2)])
    S3 = A3*np.cos((B3 + C3*T)%(2*np.pi))

    # Solar longitude of J2000.0
    L = L0 + (S0 + S1*T + S2*T**2 + S3*T**3)*1e-7

    # Bound to solar longitude to the [0, 2pi] range
    L = L%(2*np.pi)

    return L
RMS/Astrometry/Conversions.py
class EARTH_CONSTANTS(object):
    """ Holds Earth's shape and physical parameters. """

    def __init__(self):

        # Earth elipsoid parameters in meters (source: WGS84, the GPS standard)
        self.EQUATORIAL_RADIUS = 6378137.0
        self.POLAR_RADIUS = 6356752.314245
        self.E = math.sqrt(1.0 - self.POLAR_RADIUS**2/self.EQUATORIAL_RADIUS**2)
        self.RATIO = self.EQUATORIAL_RADIUS/self.POLAR_RADIUS
        self.SQR_DIFF = self.EQUATORIAL_RADIUS**2 - self.POLAR_RADIUS**2
RMS/Astrometry/Conversions.py
def raDec2AltAz(ra, dec, jd, lat, lon):
    """ Calculate the reference azimuth and altitude of the centre of the FOV from the given RA/Dec.
    Arguments:
        ra:  [float] Right ascension in degrees.
        dec: [float] Declination in degrees.
        jd: [float] Reference Julian date.
        lat: [float] Latitude +N in degrees.
        lon: [float] Longitude +E in degrees.
    Return:
        (azim, elev): [tuple of float]: Azimuth and elevation (degrees).
    """
    ra = np.radians(ra)
    dec = np.radians(dec)
    lat = np.radians(lat)
    lon = np.radians(lon)

    # Compute azim and elev using a fast cython function
    if isinstance(ra, float) or isinstance(ra, int) or isinstance(ra, np.float64):
        azim, elev = cyraDec2AltAz(ra, dec, jd, lat, lon)

    elif isinstance(ra, np.ndarray):
        # Compute it for numpy arrays
        azim, elev = cyraDec2AltAz_vect(ra, dec, jd, lat, lon)

    else:
        raise TypeError("ra must be a number or np.ndarray, given: {}".format(type(ra)))

    return np.degrees(azim), np.degrees(elev)
RMS/Math.py
@np.vectorize
def sphericalPointFromHeadingAndDistance(ra1, dec1, heading, distance):
    """ Given RA and Dec, a heading and angular distance, compute coordinates of the point.

    Arguments:
        ra1: [float] Right Ascension (deg).
        dec1: [float] Declination (deg).
        heading: [float] Heading +E of due N in degrees (deg).
        distance: [float] Distance (deg).

    Return:
        ra, dec: [float] Coordinates of the new point (deg)

    """

    ra1 = np.radians(ra1)
    dec1 = np.radians(dec1)
    heading = np.radians(heading)
    distance = np.radians(distance)

    # Compute the new declination
    dec = np.arcsin(np.sin(dec1)*np.cos(distance) + np.cos(dec1)*np.sin(distance)*np.cos(heading))

    # Compute the new RA
    dra = np.arctan2(np.sin(heading)*np.sin(distance)*np.cos(dec1), np.cos(distance) \
        - np.sin(dec1)*np.sin(dec))
    ra = (ra1 - dra + np.pi)%(2*np.pi) - np.pi


    return np.degrees(ra)%360, np.degrees(dec)


Question: import sys
import os
import glob
import datetime
import copy
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
    import argparse
    import RMS.ConfigReader as cr
from RMS.Astrometry.Conversions import raDec2Vector, vector2RaDec, datetime2JD, jd2Date, raDec2AltAz, \
    geocentricToApparentRadiantAndVelocity, EARTH_CONSTANTS
from RMS.Formats.FFfile import filenameToDatetime
from RMS.Formats.FTPdetectinfo import readFTPdetectinfo
from RMS.Formats.Showers import loadShowers, generateActivityDiagram, makeShowerColors
from RMS.Math import vectNorm, angularSeparation, angularSeparationVect, isAngleBetween, \
    sphericalPointFromHeadingAndDistance, cartesianToPolar
from RMS.Routines.GreatCircle import fitGreatCircle, greatCircle, greatCirclePhase
from RMS.Routines.SolarLongitude import jd2SolLonSteyaert
from RMS.Routines.AllskyPlot import AllSkyPlot
""" Single station shower association. """

from __future__ import print_function, division, absolute_import






EARTH = EARTH_CONSTANTS()


class MeteorSingleStation(object):
    def __init__(self, station_id, lat, lon, ff_name):
        """ Container for single station observations which enables great circle fitting. 

        Arguments:
            station_id: [str]
            lat: [float] +N latitude (deg).
            lon: [float] +E longitude (deg).
            ff_name: [str] Name of the FF file on which the meteor was recorded.
        """

        self.station_id = station_id
        self.lat = lat
        self.lon = lon
        self.ff_name = ff_name

        self.jd_array = []
        self.ra_array = []
        self.dec_array = []
        self.mag_array = []

        self.cartesian_points = None

        self.normal = None

        self.meteor_begin_cartesian = None
        self.meteor_end_cartesian = None

        self.duration = None

        self.jdt_ref = None

        # Solar longitude of the beginning (degrees)
        self.lasun = None

        # Phases on the great circle of the beginning and the end
        self.gc_beg_phase = None
        self.gc_end_phase = None

        # Approx apparent shower radiant (only for associated meteors)
        self.radiant_ra = None
        self.radiant_dec = None



    def addPoint(self, jd, ra, dec, mag):

        self.jd_array.append(jd)
        self.ra_array.append(ra)
        self.dec_array.append(dec)
        self.mag_array.append(mag)




    def fitGC(self):
        """ Fits great circle to observations. """

        self.cartesian_points = []

        self.ra_array = np.array(self.ra_array)
        self.dec_array = np.array(self.dec_array)

        for ra, dec in zip(self.ra_array, self.dec_array):

            vect = vectNorm(raDec2Vector(ra, dec))

            self.cartesian_points.append(vect)


        self.cartesian_points = np.array(self.cartesian_points)

        # Set begin and end pointing vectors
        self.beg_vect = self.cartesian_points[0]
        self.end_vect = self.cartesian_points[-1]

        # Compute alt of the begining and the last point

Answer:         self.beg_azim, self.beg_alt = raDec2AltAz(self.ra_array[0], self.dec_array[0], self.jd_array[0], \