Exemplo n.º 1
0
def datetime2Lgm(*args):
    '''Convenience function to convert Python datetime to Lgm date and utc format
    
    Parameters
    ----------
    time_in: datetime.datetime
        the input time to convert to a different system
    
    Outputs
    -------
    datelong: long
        LanlGeoMag long integer date representation (YYYYMMDD)
    utc: float
        LanlGeoMag floating point time-of-day (UTC) representation
    '''
    if len(args) == 1:
        time_in = args[0]
    else:
        raise ValueError('Invalid arguments supplied in function call') 
    
    if _spacepy:
        if isinstance(time_in, spt.Ticktock):
            raise NotImplementedError('SpacePy compatibility not yet enabled')
    try:
        datelong = Lgm_CTrans.dateToDateLong(time_in)
        utc = Lgm_CTrans.dateToFPHours(time_in)
    except AttributeError:
        raise TypeError("Date must be a datetime object")
    
    return datelong, utc
Exemplo n.º 2
0
    def test_Lgm_LoadLeapSeconds(self):
        """Lgm_LoadLeapSeconds should work and not change"""
        self.assertTrue(Lgm_LoadLeapSeconds(ctypes.pointer(self.c)))
        # The LeapSecondDates values are the dates that the given corrections came into effect. But the actual leap second
        # date is the day before -- thats when the extra second was tacked on. So subtract a day from each of the dates.
        LeapSecondDates = [19720101, 19720701, 19730101, 19740101, 19750101,
                           19760101, 19770101, 19780101, 19790101, 19800101,
                           19810701, 19820701, 19830701, 19850701, 19880101,
                           19900101, 19910101, 19920701, 19930701, 19940701,
                           19960101, 19970701, 19990101, 20060101, 20090101,]
        LeapSecondJDs = [2441317.5, 2441499.5, 2441683.5, 2442048.5, 2442413.5,
                         2442778.5, 2443144.5, 2443509.5, 2443874.5, 2444239.5,
                         2444786.5, 2445151.5, 2445516.5, 2446247.5, 2447161.5,
                         2447892.5, 2448257.5, 2448804.5, 2449169.5, 2449534.5,
                         2450083.5, 2450630.5, 2451179.5, 2453736.5, 2454832.5,]
        LeapSeconds = [10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0,
                       19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0,
                       28.0, 29.0, 30.0, 31.0, 32.0, 33.0, 34.0, ]
        LeapSecondDates = Lgm_CTrans.dateLongToDate(LeapSecondDates)
        LeapSecondDates = [val - datetime.timedelta(days=1) for val in LeapSecondDates]
        LeapSecondDates = Lgm_CTrans.dateToDateLong(LeapSecondDates)

        for i, (v1, v2, v3) in enumerate(zip(LeapSecondDates, LeapSecondJDs, LeapSeconds)):
            self.assertEqual(v1, self.c.l.LeapSecondDates[i])
            self.assertEqual(v2, self.c.l.LeapSecondJDs[i])
            self.assertEqual(v3, self.c.l.LeapSeconds[i])
Exemplo n.º 3
0
def datetime2Lgm(*args):
    '''Convenience function to convert Python datetime to Lgm date and utc format
    
    Parameters
    ----------
    time_in: datetime.datetime
        the input time to convert to a different system
    
    Outputs
    -------
    datelong: long
        LanlGeoMag long integer date representation (YYYYMMDD)
    utc: float
        LanlGeoMag floating point time-of-day (UTC) representation
    '''
    if len(args) == 1:
        time_in = args[0]
    else:
        raise ValueError('Invalid arguments supplied in function call')

    if _spacepy:
        if isinstance(time_in, spt.Ticktock):
            raise NotImplementedError('SpacePy compatibility not yet enabled')
    try:
        datelong = Lgm_CTrans.dateToDateLong(time_in)
        utc = Lgm_CTrans.dateToFPHours(time_in)
    except AttributeError:
        raise TypeError("Date must be a datetime object")

    return datelong, utc
Exemplo n.º 4
0
 def test_dateToDateLong(self):
     """dateToDateLong should give known results for known input"""
     d1 = datetime.datetime(2000, 12, 12)
     self.assertEqual(20001212L, Lgm_CTrans.dateToDateLong(d1))
     self.assertEqual(20001212L, Lgm_CTrans.dateToDateLong([d1]))
     self.assertEqual([20001212L, 20001212L], Lgm_CTrans.dateToDateLong([d1, d1]))
     self.assertEqual([20001212L, 20001212L],
         Lgm_CTrans.dateToDateLong(numpy.array([d1, d1])).tolist())
     self.assertEqual(20001212L, Lgm_CTrans.dateToDateLong(d1.date()))
    def test_Lgm_LoadLeapSeconds(self):
        """Lgm_LoadLeapSeconds should work and not change"""
        self.assertTrue(Lgm_LoadLeapSeconds(ctypes.pointer(self.c)))
        # The LeapSecondDates values are the dates that the given corrections came into effect. But the actual leap second
        # date is the day before -- thats when the extra second was tacked on. So subtract a day from each of the dates.
        LeapSecondDates = [
            19720101,
            19720701,
            19730101,
            19740101,
            19750101,
            19760101,
            19770101,
            19780101,
            19790101,
            19800101,
            19810701,
            19820701,
            19830701,
            19850701,
            19880101,
            19900101,
            19910101,
            19920701,
            19930701,
            19940701,
            19960101,
            19970701,
            19990101,
            20060101,
            20090101,
        ]
        LeapSecondJDs = [
            2441317.5,
            2441499.5,
            2441683.5,
            2442048.5,
            2442413.5,
            2442778.5,
            2443144.5,
            2443509.5,
            2443874.5,
            2444239.5,
            2444786.5,
            2445151.5,
            2445516.5,
            2446247.5,
            2447161.5,
            2447892.5,
            2448257.5,
            2448804.5,
            2449169.5,
            2449534.5,
            2450083.5,
            2450630.5,
            2451179.5,
            2453736.5,
            2454832.5,
        ]
        LeapSeconds = [
            10.0,
            11.0,
            12.0,
            13.0,
            14.0,
            15.0,
            16.0,
            17.0,
            18.0,
            19.0,
            20.0,
            21.0,
            22.0,
            23.0,
            24.0,
            25.0,
            26.0,
            27.0,
            28.0,
            29.0,
            30.0,
            31.0,
            32.0,
            33.0,
            34.0,
        ]
        LeapSecondDates = Lgm_CTrans.dateLongToDate(LeapSecondDates)
        LeapSecondDates = [
            val - datetime.timedelta(days=1) for val in LeapSecondDates
        ]
        LeapSecondDates = Lgm_CTrans.dateToDateLong(LeapSecondDates)

        for i, (v1, v2, v3) in enumerate(
                zip(LeapSecondDates, LeapSecondJDs, LeapSeconds)):
            self.assertEqual(v1, self.c.l.LeapSecondDates[i])
            self.assertEqual(v2, self.c.l.LeapSecondJDs[i])
            self.assertEqual(v3, self.c.l.LeapSeconds[i])
Exemplo n.º 6
0
def getLatLonRadfromTLE(epochs, TLEpath, options):
    """ Reads Latitude and Longitude from TLE.

    Parameters
    ==========
    epochs : 
        List of Ticktock objects.
    TLEpath : 
        Path to TLE file.
    options : optparse.Values
        Organized options from the command line.

    Returns
    =======
    testlat, testlon, testrad : list, list, list
        Latitude, Longitude, and Radius, respectively, each being
        a list of float values.
    """
    # now do Mike's setup for getting coords from TLE using SGP4
    pos_in = [0, 0, 0]
    s = _SgpInfo()
    TLEs = _SgpTLE()

    # loop over all times
    testlat = np.asarray(epochs).copy()
    testlat.fill(0)
    testlon = testlat.copy()
    testrad = testlat.copy()
    testtdiff = testlat.copy()
    print('Fetching TLEs & converting for range {0} to {1}'.format(
        epochs[0].isoformat(), epochs[-1].isoformat()))
    for idx, c_date in enumerate(epochs):
        #print('Doing {0}'.format(c_date))
        # put into JD as SGP4 needs serial time
        c = Lgm_init_ctrans(0)

        # now do Mike's setup for getting coords from TLE using SGP4
        dstr = int(c_date.strftime('%Y%j')) + c_date.hour / 24.0 + \
            c_date.minute / 1440.0 + c_date.second / 86400.0
        globstat = os.path.join(TLEpath, '*.txt')
        TLEfiles = glob.glob(globstat)
        if not TLEfiles:
            raise IOError(
                'No TLE files found in {0}. Aborting...'.format(TLEpath))
        Line0, Line1, Line2 = fTLE.findTLEinfiles(
            TLEfiles,
            ParseMethod='UseSatelliteNumber',
            TargetEpoch=dstr,
            SatelliteNumber=options.SatNum,
            Verbose=False,
            PurgeDuplicates=True)

        # print("{0}\n{1}\n{2}\n\n".format(Line0,Line1,Line2))
        nTLEs = c_int(0)
        LgmSgp_ReadTlesFromStrings(Line0, Line1, Line2, pointer(nTLEs),
                                   pointer(TLEs), 1)
        LgmSgp_SGP4_Init(pointer(s), pointer(TLEs))
        date = Lgm_CTrans.dateToDateLong(c_date)
        utc = Lgm_CTrans.dateToFPHours(c_date)
        JD = Lgm_JD(c_date.year, c_date.month, c_date.day, utc,
                    LGM_TIME_SYS_UTC, c)

        # Set up the trans matrices
        Lgm_Set_Coord_Transforms(date, utc, c)
        # get SGP4 output, needs minutes-since-TLE-epoch
        tsince = (JD - TLEs.JD) * 1440.0
        LgmSgp_SGP4(tsince, pointer(s))

        pos_in[0] = s.X
        pos_in[1] = s.Y
        pos_in[2] = s.Z

        Pin = Lgm_Vector.Lgm_Vector(*pos_in)
        Pout = Lgm_Vector.Lgm_Vector()
        Lgm_Convert_Coords(pointer(Pin), pointer(Pout), TEME_TO_GEO, c)
        PoutPy = Pout.tolist()
        PoutPy[0] /= WGS84_A
        PoutPy[1] /= WGS84_A
        PoutPy[2] /= WGS84_A
        nlat, nlon, nrad = Lgm_Vector.CartToSph(*PoutPy)
        testlat[idx] = nlat
        testlon[idx] = nlon
        testrad[idx] = nrad
        testtdiff[idx] = tsince / 1440.0
    return testlat, testlon, testrad
Exemplo n.º 7
0
def getLatLonRadfromTLE(epochs, TLEpath, options):
    """ Reads Latitude and Longitude from TLE.

    Parameters
    ==========
    epochs : 
        List of Ticktock objects.
    TLEpath : 
        Path to TLE file.
    options : optparse.Values
        Organized options from the command line.

    Returns
    =======
    testlat, testlon, testrad : list, list, list
        Latitude, Longitude, and Radius, respectively, each being
        a list of float values.
    """
    # now do Mike's setup for getting coords from TLE using SGP4
    pos_in = [0, 0, 0]
    s = _SgpInfo()
    TLEs = _SgpTLE()

    # loop over all times
    testlat = np.asarray(epochs).copy()
    testlat.fill(0)
    testlon = testlat.copy()
    testrad = testlat.copy()
    testtdiff = testlat.copy()
    print('Fetching TLEs & converting for range {0} to {1}'.format(
        epochs[0].isoformat(), epochs[-1].isoformat()))
    for idx, c_date in enumerate(epochs):
        #print('Doing {0}'.format(c_date))
        # put into JD as SGP4 needs serial time
        c = Lgm_init_ctrans(0)

        # now do Mike's setup for getting coords from TLE using SGP4
        dstr = int(c_date.strftime('%Y%j')) + c_date.hour / 24.0 + \
            c_date.minute / 1440.0 + c_date.second / 86400.0
        globstat = os.path.join(TLEpath, '*.txt')
        TLEfiles = glob.glob(globstat)
        if not TLEfiles:
            raise IOError(
                'No TLE files found in {0}. Aborting...'.format(TLEpath))
        Line0, Line1, Line2 = fTLE.findTLEinfiles(
                TLEfiles,
                ParseMethod='UseSatelliteNumber',
                TargetEpoch=dstr,
                SatelliteNumber=options.SatNum,
                Verbose=False,
                PurgeDuplicates=True)

        # print("{0}\n{1}\n{2}\n\n".format(Line0,Line1,Line2))
        nTLEs = c_int(0)
        LgmSgp_ReadTlesFromStrings(
            Line0,
            Line1,
            Line2,
            pointer(nTLEs),
            pointer(TLEs),
            1)
        LgmSgp_SGP4_Init(pointer(s), pointer(TLEs))
        date = Lgm_CTrans.dateToDateLong(c_date)
        utc = Lgm_CTrans.dateToFPHours(c_date)
        JD = Lgm_JD(
            c_date.year,
            c_date.month,
            c_date.day,
            utc,
            LGM_TIME_SYS_UTC,
            c)

        # Set up the trans matrices
        Lgm_Set_Coord_Transforms(date, utc, c)
        # get SGP4 output, needs minutes-since-TLE-epoch
        tsince = (JD - TLEs.JD) * 1440.0
        LgmSgp_SGP4(tsince, pointer(s))

        pos_in[0] = s.X
        pos_in[1] = s.Y
        pos_in[2] = s.Z

        Pin = Lgm_Vector.Lgm_Vector(*pos_in)
        Pout = Lgm_Vector.Lgm_Vector()
        Lgm_Convert_Coords(pointer(Pin), pointer(Pout), TEME_TO_GEO, c)
        PoutPy = Pout.tolist()
        PoutPy[0] /= WGS84_A
        PoutPy[1] /= WGS84_A
        PoutPy[2] /= WGS84_A
        nlat, nlon, nrad = Lgm_Vector.CartToSph(*PoutPy)
        testlat[idx] = nlat
        testlon[idx] = nlon
        testrad[idx] = nrad
        testtdiff[idx] = tsince / 1440.0
    return testlat, testlon, testrad
Exemplo n.º 8
0
def coordTrans(pos_in, time_in, in_sys, out_sys, de_eph=False):
    '''
    Convert coordinates between almost any system using LanlGeoMag

    Parameters
    ----------
    position : list
        a three element vector of positions in input coord system
    time : datetime
        a datimetime object representing the time at the desired conversion
    system_in : str
        a string giving the acronym for the input coordinate system
    system_out : str
        a string giving the acronym for the desired output coordinate system
    de_eph : bool or int (optional)
        a boolean stating whether JPL DE421 is to be used for Sun, etc.

    Returns
    -------
    out : list
        3-element list of the converted coordinate

    Examples
    --------
    >>> from lgmpy import magcoords
    >>> import datetime
    >>> magcoords.coordTrans([-4,0,0], datetime.datetime(2009,1,1),'SM','GSM')
    [-3.60802691..., 2.5673907444...e-16, -1.72688788616...]
    >>> magcoords.coordTrans([-3.608026916281573, 2.5673907444456745e-16, -1.7268878861662329], datetime.datetime(2009,1,1),'GSM','SM')
    [-3.99999999..., 4.0592529337...e-16, 8.8817841970...3e-16]

    TODO
    ----
    extend interface to get necessary args from a MagModel or cTrans structure
    '''
    # change datetime to Lgm Datelong and UTC
    ct = Lgm_CTrans.Lgm_CTrans(0)
    if de_eph:
        Lgm_Set_CTrans_Options(LGM_EPH_DE, LGM_PN_IAU76, pointer(ct))
    try:
        datelong = Lgm_CTrans.dateToDateLong(time_in)
        utc = Lgm_CTrans.dateToFPHours(time_in)
        Lgm_Set_Coord_Transforms( datelong, utc, pointer(ct)) # don't need pointer as it is one
    except AttributeError:
        raise(TypeError("Date must be a datetime object"))

    try:
        conv_val = trans_dict[in_sys]*100 + trans_dict[out_sys]
    except KeyError:
        raise KeyError('One of the specified coordinate systems is not recognised')

    ## do this as WGS uses Cartesian but needs to be converted from desired spherical input
    if 'WGS84' in in_sys:
        XYZ = Lgm_Vector.SphToCart(*pos_in)
        SPH = Lgm_Vector.Lgm_Vector(XYZ.x,XYZ.y, XYZ.z)
        Pout = _doConversion(SPH, conv_val, cTrans=ct)
    else:
        Pout = _doConversion(pos_in, conv_val, ct)

    if 'WGS84' in out_sys:
        nlat, nlon, nrad = Lgm_Vector.CartToSph(*Pout.tolist())
        Pout = Lgm_Vector.Lgm_Vector(nlat, nlon, nrad)

    return Pout.tolist()
Exemplo n.º 9
0
def Lvalue(*args, **kwargs):
    '''
    Function to return the L-value of a position using either McIlwain or Hilton
    approximation

    Parameters
    ==========
    pos : list
        3-element position int he specified coord_system
    time : datetime
        date and time for the calculation
    alpha : float, optional
        the pitch angle for the L calculation, default=90
    Bfield : str, optional
        the magnetic field model to use, default=Lgm_B_T89
    method : str, optional
        the L-value formula to use, McIlwain or Hilton, default=Hilton
    Kp : int
        Kp index value for the calculation
    coord_system : str
        the input coordinate system, default=GSM
    extended_out : bool
        keyword specifying short or extended output, default=False

    Returns
    =======
    out : dict
        dictionary of the values, see examples for documentation of dictionary

    Examples
    ========
    >>> from lgmpy import magcoords
    >>> import datetime
    >>> magcoords.Lvalue([3, 0, 1], datetime.datetime(2000, 1, 1), extended_out=False)
    {'I': 0.2434969602..., 'L': 3.195481841...}

    The ``extended_out=False`` output is:
        - I : I value at the given point
        - L : L value at the given point

    >>> from lgmpy import magcoords
    >>> import datetime
    >>> magcoords.Lvalue([3, 0, 1], datetime.datetime(2000, 1, 1), extended_out=True)
    {'Blocal': 1024.1142193703838,
    'Bmin': 921.8869150...,
    'Bmirr': 1024.1142193...,
    'I': 0.24349696021...,
    'L': 3.1954818410...,
    'M': 30119.614287...}

    The ``extended_out=True`` output is:
        - I : I value at the given point
        - L : L value at the given point
        - Bmin : minimum B at the input position (nT)
        - Bmirror : mirror B at the input position (nT)
        - M : TODO what exactly and I, magnetic moment?
    '''
    defaults = {'alpha': 90.,
                'Bfield': 'Lgm_B_T89',
                'method': 'Hilton',
                'Kp': 2,
                'coord_system': 'GSM',
                'extended_out': False}

    #replace missing kwargs with defaults
    for dkey in defaults:
        if dkey not in kwargs:
            kwargs[dkey] = defaults[dkey]

    method_dict = {'Hilton': 1, 'McIlwain': 0}

    # change datetime to Lgm Datelong and UTC
    mInfo = Lgm_MagModelInfo.Lgm_MagModelInfo()
    mInfo.Kp = kwargs['Kp']
    try:
        Bfield_dict[kwargs['Bfield']](pointer(mInfo))
    except KeyError:
        raise(NotImplementedError("Only Bfield=%s currently supported" % Bfield_dict.keys()))
    try:
        datelong = Lgm_CTrans.dateToDateLong(args[1])
        utc = Lgm_CTrans.dateToFPHours(args[1])
        Lgm_Set_Coord_Transforms( datelong, utc, mInfo.c) # dont need pointer as it is one
    except AttributeError:
        raise(TypeError("Date must be a datetime object"))
    #else:
        #ans['Epoch'] = datamodel.dmarray([args[1]])

    if kwargs['coord_system'] != 'GSM':
        in_sys = kwargs['coord_system']
        Pout = coordTrans(args[0], args[1], in_sys, 'GSM', de_eph=False)
        Pgsm = Lgm_Vector.Lgm_Vector(*Pout)
    else:
        Pgsm = Lgm_Vector.Lgm_Vector(*args[0])

    Iout = c_double()
    Bm = c_double()
    M = c_double()
    ans = Lgm_McIlwain_L(datelong, utc, pointer(Pgsm), kwargs['alpha'],
                            method_dict[kwargs['method']],
                            pointer(Iout), pointer(Bm), pointer(M),
                            pointer(mInfo))

    #TODO: decide on format for output -- perhaps use datamodel and have method as attribute?
    #maybe only return I for extended_out flag=True
    if kwargs['extended_out']:
        sunPos_GSM = coordTrans(mInfo.c.contents.Sun, args[1], 'MOD', kwargs['coord_system'])
        SunMlon = np.rad2deg(np.arctan2( sunPos_GSM[1], sunPos_GSM[0]))
        SunMlon += 180.0 # flip to midnight

        MLON = np.rad2deg(np.arctan2( Pgsm.y, Pgsm.x))
        if (MLON < 0.0):
            MLON += 360.0

        MLT = np.mod( (MLON-SunMlon)/15.0+24.0, 24.0 )
        return {'L': ans, 'I': Iout.value, 'Bmin': mInfo.Bmin, 'Blocal': mInfo.Blocal,
                'Bmirr': mInfo.Bm, 'M': M.value, 'MLon':MLON, 'MLT':MLT}
    else:
        return {'L': ans, 'I': Iout.value}
Exemplo n.º 10
0
def coordTrans(pos_in, time_in, in_sys, out_sys, de_eph=False):
    '''
    Convert coordinates between almost any system using LanlGeoMag

    Parameters
    ----------
    position : list
        a three element vector of positions in input coord system
    time : datetime
        a datimetime object representing the time at the desired conversion
    system_in : str
        a string giving the acronym for the input coordinate system
    system_out : str
        a string giving the acronym for the desired output coordinate system
    de_eph : bool or int (optional)
        a boolean stating whether JPL DE421 is to be used for Sun, etc.

    Returns
    -------
    out : list
        3-element list of the converted coordinate

    Examples
    --------
    >>> from lgmpy import magcoords
    >>> import datetime
    >>> magcoords.coordTrans([-4,0,0], datetime.datetime(2009,1,1),'SM','GSM')
    [-3.60802691..., 2.5673907444...e-16, -1.72688788616...]
    >>> magcoords.coordTrans([-3.608026916281573, 2.5673907444456745e-16, -1.7268878861662329], datetime.datetime(2009,1,1),'GSM','SM')
    [-3.99999999..., 4.0592529337...e-16, 8.8817841970...3e-16]

    TODO
    ----
    extend interface to get necessary args from a MagModel or cTrans structure
    '''
    # change datetime to Lgm Datelong and UTC
    ct = Lgm_CTrans.Lgm_CTrans(0)
    if de_eph:
        Lgm_Set_CTrans_Options(LGM_EPH_DE, LGM_PN_IAU76, pointer(ct))
    try:
        datelong = Lgm_CTrans.dateToDateLong(time_in)
        utc = Lgm_CTrans.dateToFPHours(time_in)
        Lgm_Set_Coord_Transforms(
            datelong, utc, pointer(ct))  # don't need pointer as it is one
    except AttributeError:
        raise (TypeError("Date must be a datetime object"))

    try:
        conv_val = trans_dict[in_sys] * 100 + trans_dict[out_sys]
    except KeyError:
        raise KeyError(
            'One of the specified coordinate systems is not recognised')

    ## do this as WGS uses Cartesian but needs to be converted from desired spherical input
    if 'WGS84' in in_sys:
        XYZ = Lgm_Vector.SphToCart(*pos_in)
        SPH = Lgm_Vector.Lgm_Vector(XYZ.x, XYZ.y, XYZ.z)
        Pout = _doConversion(SPH, conv_val, cTrans=ct)
    else:
        Pout = _doConversion(pos_in, conv_val, ct)

    if 'WGS84' in out_sys:
        nlat, nlon, nrad = Lgm_Vector.CartToSph(*Pout.tolist())
        Pout = Lgm_Vector.Lgm_Vector(nlat, nlon, nrad)

    return Pout.tolist()
Exemplo n.º 11
0
def Lvalue(*args, **kwargs):
    '''
    Function to return the L-value of a position using either McIlwain or Hilton
    approximation

    Parameters
    ==========
    pos : list
        3-element position int he specified coord_system
    time : datetime
        date and time for the calculation
    alpha : float, optional
        the pitch angle for the L calculation, default=90
    Bfield : str, optional
        the magnetic field model to use, default=Lgm_B_T89
    method : str, optional
        the L-value formula to use, McIlwain or Hilton, default=Hilton
    Kp : int
        Kp index value for the calculation
    coord_system : str
        the input coordinate system, default=GSM
    extended_out : bool
        keyword specifying short or extended output, default=False

    Returns
    =======
    out : dict
        dictionary of the values, see examples for documentation of dictionary

    Examples
    ========
    >>> from lgmpy import magcoords
    >>> import datetime
    >>> magcoords.Lvalue([3, 0, 1], datetime.datetime(2000, 1, 1), extended_out=False)
    {'I': 0.2434969602..., 'L': 3.195481841...}

    The ``extended_out=False`` output is:
        - I : I value at the given point
        - L : L value at the given point

    >>> from lgmpy import magcoords
    >>> import datetime
    >>> magcoords.Lvalue([3, 0, 1], datetime.datetime(2000, 1, 1), extended_out=True)
    {'Blocal': 1024.1142193703838,
    'Bmin': 921.8869150...,
    'Bmirr': 1024.1142193...,
    'I': 0.24349696021...,
    'L': 3.1954818410...,
    'M': 30119.614287...}

    The ``extended_out=True`` output is:
        - I : I value at the given point
        - L : L value at the given point
        - Bmin : minimum B at the input position (nT)
        - Bmirror : mirror B at the input position (nT)
        - M : TODO what exactly and I, magnetic moment?
    '''
    defaults = {
        'alpha': 90.,
        'Bfield': 'Lgm_B_T89',
        'method': 'Hilton',
        'Kp': 2,
        'coord_system': 'GSM',
        'extended_out': False
    }

    #replace missing kwargs with defaults
    for dkey in defaults:
        if dkey not in kwargs:
            kwargs[dkey] = defaults[dkey]

    method_dict = {'Hilton': 1, 'McIlwain': 0}

    # change datetime to Lgm Datelong and UTC
    mInfo = Lgm_MagModelInfo.Lgm_MagModelInfo()
    mInfo.Kp = kwargs['Kp']
    try:
        Bfield_dict[kwargs['Bfield']](pointer(mInfo))
    except KeyError:
        raise (NotImplementedError("Only Bfield=%s currently supported" %
                                   Bfield_dict.keys()))
    try:
        datelong = Lgm_CTrans.dateToDateLong(args[1])
        utc = Lgm_CTrans.dateToFPHours(args[1])
        Lgm_Set_Coord_Transforms(datelong, utc,
                                 mInfo.c)  # dont need pointer as it is one
    except AttributeError:
        raise (TypeError("Date must be a datetime object"))
    #else:
    #ans['Epoch'] = datamodel.dmarray([args[1]])

    if kwargs['coord_system'] != 'GSM':
        in_sys = kwargs['coord_system']
        Pout = coordTrans(args[0], args[1], in_sys, 'GSM', de_eph=False)
        Pgsm = Lgm_Vector.Lgm_Vector(*Pout)
    else:
        Pgsm = Lgm_Vector.Lgm_Vector(*args[0])

    Iout = c_double()
    Bm = c_double()
    M = c_double()
    ans = Lgm_McIlwain_L(datelong, utc, pointer(Pgsm), kwargs['alpha'],
                         method_dict[kwargs['method']], pointer(Iout),
                         pointer(Bm), pointer(M), pointer(mInfo))

    #TODO: decide on format for output -- perhaps use datamodel and have method as attribute?
    #maybe only return I for extended_out flag=True
    if kwargs['extended_out']:
        sunPos_GSM = coordTrans(mInfo.c.contents.Sun, args[1], 'MOD',
                                kwargs['coord_system'])
        SunMlon = np.rad2deg(np.arctan2(sunPos_GSM[1], sunPos_GSM[0]))
        SunMlon += 180.0  # flip to midnight

        MLON = np.rad2deg(np.arctan2(Pgsm.y, Pgsm.x))
        if (MLON < 0.0):
            MLON += 360.0

        MLT = np.mod((MLON - SunMlon) / 15.0 + 24.0, 24.0)
        return {
            'L': ans,
            'I': Iout.value,
            'Bmin': mInfo.Bmin,
            'Blocal': mInfo.Blocal,
            'Bmirr': mInfo.Bm,
            'M': M.value,
            'MLon': MLON,
            'MLT': MLT
        }
    else:
        return {'L': ans, 'I': Iout.value}