Как записать данные в файл .dat Python новая строка несколько списков Satellite Orbit

Я создал функцию для генерации и распространения спутниковой орбиты. Теперь я хочу сохранить все свои данные в файле .dat. Я не уверен, сколько циклов for мне нужно или как это сделать.

Я хочу, чтобы время, широта, долгота и высота были в одной строке для каждого шага распространения.

Код для данных:

myOrbitJ2000Time = [1085.0, 2170.0, 3255.0, 4340.1, 5425.1]

lat = [48.5, 26.5, -28.8, -48.1, 0.1]

lon = [-144.1, -50.4, -1.6, 91.5, 152.8]

alt = [264779.5, 262446.1, 319661.8, 355717.3, 306129.0]

Желаемый результат в файле .dat:

J2000 Время, Широта, Долгота, Альт.

1085.0, 48.6, -144.2, 264779.5

2170.0, 26.5, -50.4, 262446.1

3255.0, -28.7, -1.6, 319661.8

4340.1, -48.0, 91.5, 355717.2

5425.1, 0.1, 152.8, 06129.0

Полный код:

import orbital
from orbital import earth, KeplerianElements, plot
import matplotlib.pyplot as plt
import numpy as np
from astropy import time
from astropy.time import TimeDelta, Time
from astropy import units as u
from astropy import coordinates as coord


#def orbitPropandcoordTrans(orbitPineapple_J2000time, _ecc, _inc, _raan, _arg_pe, _meananom, meanMotion):
def orbitPropandcoordTrans(propNum, orbitPineapple_J2000time, _ecc, _inc, _raan, _arg_pe, _meananom, meanMotion):
        '''
        Create original orbit and run for 100 propagations (in total one whole orbit)
        in order to get xyz and time for each propagation step.
        The end goal is to plot the lat, lon, & alt data to see if it matches ISS groundtrace.
        '''
    import orbital
    from orbital import earth, KeplerianElements, plot
    import matplotlib.pyplot as plt
    import numpy as np
    from astropy import time
    from astropy.time import TimeDelta, Time
    from astropy import units as u
    from astropy import coordinates as coord

    'Calculate Avg. Period from Mean Motion'
    _avgPeriod = 86400 / meanMotion
    #print('_avgPeriod', _avgPeriod)

    ######
    #propNum = int(_avgPeriod)

    'Generate Orbit'
    #orbitPineapple = KeplerianElements.with_period(5560, body=earth, e=0.0004525, i=(np.deg2rad(51.6414)), raan=(np.deg2rad(247.1662)))
    orbitPineapple = KeplerianElements.with_period(_avgPeriod, body=earth, e=_ecc, i=(np.deg2rad(_inc)), raan=(np.deg2rad(_raan)), arg_pe=(np.deg2rad(_arg_pe)), M0=(np.deg2rad(_meananom))) #ref_epoch=   
    #plot(orbitPineapple)
    #plt.show()
    #print('')
    #print('')

    'Propagate Orbit and retrieve xyz'
    myOrbitX = []         #X Coordinate for propagated orbit step
    myOrbitY = []         #Y Coordinate for propagated orbit step
    myOrbitZ = []         #Z Coordinate for propagated orbit step
    myOrbitTime = []      #Time for each propagated orbit step
    myOrbitJ2000Time = [] #J2000 times
    #propNum = 100        #Number of propagations and Mean Anomaly size (one orbit 2pi/propNum)

    for i in range(propNum):
        orbitPineapple.propagate_anomaly_by(M=(2.0*np.pi/propNum)) #Propagate the orbit by the Mean Anomaly
        myOrbitX.append(orbitPineapple.r.x)                        #x vals
        myOrbitY.append(orbitPineapple.r.y)                        #y vals
        myOrbitZ.append(orbitPineapple.r.z)                        #z vals
        myOrbitTime.append(orbitPineapple_J2000time)               #J2000 time vals
        myOrbitJ2000Time.append(orbitPineapple.t)

        #plot(orbitPineapple)
    #print('X:', 'Length:', len(myOrbitX))
    #print(myOrbitX)
    #print('Y:', 'Length:',len(myOrbitY))
    #print(myOrbitY)
    #print('Z:', 'Length:', len(myOrbitZ))
    #print(myOrbitZ)
    #print('J2000 Time:', 'Length:',len(myOrbitTime))
    #print(myOrbitTime)


    '''Because the myOrbitTime is only the time between each step to be the sum of itself plus
    all the previous times. And then I need to convert that time from seconds after J2000 to UTC.'''
    myT = [] #UTC time list

    for i in range(propNum):
        myT.append((Time(2000, format='jyear') + TimeDelta(myOrbitTime[i]*u.s)).iso) #Convert time from J2000 to UTC
    #print('UTC Time List Length:', len(myT))
    #print('UTC Times:', myT)

    '''Now I have xyz and time for each propagation step and need to convert the coordinates from
    ECI to Lat, Lon, & Alt'''
    now = []     #UTC time at each propagation step
    xyz =[]      #Xyz coordinates from OrbitalPy initial orbit propagation
    cartrep = [] #Cartesian Representation
    gcrs = []    #Geocentric Celestial Reference System/Geocentric Equatorial Inertial, the default coord system of OrbitalPy
    itrs =[]     #International Terrestrial Reference System coordinates
    lat = []     #Longitude of the location, for the default ellipsoid
    lon = []     #Longitude of the location, for the default ellipsoid
    alt = []     #Height of the location, for the default ellipsoid


    for i in range(propNum):
        xyz = (myOrbitX[i], myOrbitY[i], myOrbitZ[i])                   #Xyz coord for each prop. step
        now = time.Time(myT[i])                                         #UTC time at each propagation step
        cartrep = coord.CartesianRepresentation(*xyz, unit=u.m)         #Add units of [m] to xyz
        gcrs = coord.GCRS(cartrep, obstime=time.Time(myT[i]))           #Let AstroPy know xyz is in GCRS
        itrs = gcrs.transform_to(coord.ITRS(obstime=time.Time(myT[i]))) #Convert GCRS to ITRS
        loc = coord.EarthLocation(*itrs.cartesian.xyz)                  #Get lat/lon/height from ITRS
        lat.append(loc.lat.value)                                       #Create latitude list
        lon.append(loc.lon.value)                                       #Create longitude list
        alt.append(loc.height.value)           

    #print('Current Time:', now)
    #print('')
    #print('UTC Time:')
    #print(myT)
    print('myOrbitJ2000Time', myOrbitJ2000Time)
    print('')
    print('Lat:')
    print('')
    print(lat)
    print('')
    print('Lon:')
    print(lon)
    print('')
    print('Alt:')
    print(alt)

орбитаPropandcoordTrans(5, -34963095, 0,0073662, 51,5946, 154,8079, 103,6176, 257,3038, 15,92610159)


person Rose    schedule 12.10.2017    source источник
comment
В качестве примечания я собираюсь сделать это для гораздо больших наборов данных, где каждый список будет иметь ~ 6000 значений вместо 5, и поэтому я ищу наиболее эффективный способ.   -  person Rose    schedule 12.10.2017
comment
Чтобы было ясно, вы спрашиваете, как записывать столбцы данных в виде значений, разделенных запятыми? Вы вообще знакомы с Numpy и/или классом Astropy Table?   -  person Iguananaut    schedule 14.10.2017


Ответы (2)


Чтобы ответить на ваш общий вопрос, вместо определения набора списков Python (которые медленно добавляются и работают с ними, особенно при работе с большим количеством значений при увеличении разрешения), вы можете начать с создания вместо этого массивов Numpy списков. При инициализации массивов Numpy обычно необходимо указать размер массива, но в этом случае это легко, поскольку вы знаете, сколько размножений вам нужно. Например:

>>> orbitX = np.empty(propNum, dtype=float)

создает пустой массив Numpy из propNum значений с плавающей запятой (здесь «пустой» означает, что массив просто не инициализирован какими-либо значениями — это самый быстрый способ создать новый массив, поскольку мы собираемся заполнить все значения в нем позже все равно.

Затем в вашем цикле вместо использования orbitX.append(x) вы бы присвоили значение в массиве, соответствующем текущему тику: orbitX[i] = x. То же самое для других случаев.

Тогда есть несколько возможностей вывести ваши данные, но с использованием Astropy Table обеспечивает большую гибкость. Вы можете легко создать таблицу, содержащую нужные столбцы:

>>> from astropy.table import Table
>>> table = Table([J2000, lat, lon, alt], names=('J2000', 'lat', 'lon', 'alt'))

Преимущество объекта Table в том, что существует множество вариантов формата вывода. Например. в приглашении Python:

>>> table
    J2000          lat            lon            alt
   float64       float64        float64        float64
------------- -------------- -------------- -------------
1085.01128806  48.5487129749 -144.175054697 264779.500823
2170.02257613  26.5068122883 -50.3805485685 262446.085716
3255.03386419 -28.7915478311  -1.6090285674 319661.817451
4340.04515225 -48.0536526356  91.5416828221 355717.274021
5425.05644032 0.084422392655  152.811717713  306129.02576

Для вывода в файл сначала вы должны подумать о том, как вы хотите отформатировать данные. Уже существует множество распространенных форматов данных, которые вы можете использовать, но это зависит от того, для чего предназначены данные и кто будет их потреблять («Файл «.dat» ничего не означает с точки зрения форматов файлов; или, скорее, он может означает что угодно). Но в своем вопросе вы указали, что вам нужно то, что называется «значениями, разделенными запятыми» (CSV), где данные отформатированы со столбцами, идущими вниз, причем каждое значение в строке разделено запятой. Класс Table может довольно легко выводить CSV (и любой вариант):

>>> import sys
>>> table.write(sys.stdout, format='ascii.csv')  # Note: I'm just using sys.stdout for demonstration purposes; normally you would give a filename
J2000,lat,lon,alt
1085.011288063746,48.54871297493748,-144.17505469691633,264779.5008225624
2170.022576127492,26.506812288280788,-50.38054856853237,262446.0857159357
3255.0338641912376,-28.79154783108773,-1.6090285674024463,319661.81745081506
4340.045152254984,-48.05365263557444,91.54168282208444,355717.2740210131
5425.05644031873,0.08442239265500713,152.81171771323176,306129.0257600865

Хотя есть много других вариантов. Например, если вы хотите, чтобы данные были красиво отформатированы в выровненных столбцах, вы также можете сделать это. Подробнее об этом можно прочитать здесь< /а>. (Кроме того, я бы посоветовал, если вам нужен формат простого текста, попробовать ascii.ecsv, преимущество которого заключается в том, что он выводит дополнительные метаданные, которые позволяют легко считывать их обратно в таблицу Astropy):

>>> table.write(sys.stdout, format='ascii.ecsv')
# %ECSV 0.9
# ---
# datatype:
# - {name: J2000, datatype: float64}
# - {name: lat, datatype: float64}
# - {name: lon, datatype: float64}
# - {name: alt, datatype: float64}
# schema: astropy-2.0
J2000 lat lon alt
1085.01128806 48.5487129749 -144.175054697 264779.500823
2170.02257613 26.5068122883 -50.3805485685 262446.085716
3255.03386419 -28.7915478311 -1.6090285674 319661.817451
4340.04515225 -48.0536526356 91.5416828221 355717.274021
5425.05644032 0.084422392655 152.811717713 306129.02576

Еще одна несвязанная вещь, которую я отмечу, заключается в том, что многие объекты в Astropy могут работать с целыми массивами в дополнение к отдельным значениям, и это часто может быть намного более эффективным, особенно для многих значений. В частности, у вас есть этот цикл Python:

for i in range(propNum):
    xyz = (myOrbitX[i], myOrbitY[i], myOrbitZ[i])                   #Xyz coord for each prop. step
    now = time.Time(myT[i])                                         #UTC time at each propagation step
    cartrep = coord.CartesianRepresentation(*xyz, unit=u.m)         #Add units of [m] to xyz
    gcrs = coord.GCRS(cartrep, obstime=time.Time(myT[i]))           #Let AstroPy know xyz is in GCRS
    itrs = gcrs.transform_to(coord.ITRS(obstime=time.Time(myT[i]))) #Convert GCRS to ITRS
    loc = coord.EarthLocation(*itrs.cartesian.xyz)                  #Get lat/lon/height from ITRS
    lat.append(loc.lat.value)                                       #Create latitude list
    lon.append(loc.lon.value)                                       #Create longitude list
    alt.append(loc.height.value)

Это можно полностью переписать без явного цикла, рассматривая их как массивы координат. Например:

>>> times = time.Time(myT)  # All the times, not just a single one
>>> cartrep = coord.CartesianRepresentation(orbitX, orbitY, orbitZ, unit=u.m)  # Again, an array of coordinates
>>> gcrs = coord.GCRS(cartrep, obstime=times)
>>> itrs = gcrs.transform_to(coord.ITRS(obstime=ts))
>>> loc = coord.EarthLocation(*itrs.cartesian.xyz)  # I'm not sure this is the most efficient way to do this but I'm not an expert on the coordinates package

При этом мы также можем получить все координаты в виде массивов. Например.:

>>> loc.lat
<Latitude [ 48.54871297, 26.50681229,-28.79154783,-48.05365264,
             0.08442239] deg>

Так что в целом это более эффективный способ работы с большим количеством значений координат. Точно так же при преобразовании myT в исходном коде вместо циклического перебора всех временных смещений вы можете создать один массив TimeDelta и добавить его к исходному времени.

К сожалению, я не эксперт по пакету orbital, но не похоже, что он упрощает получение координат в разных точках орбиты. ISTM вроде должен быть.

person Iguananaut    schedule 14.10.2017
comment
Каким будет самый быстрый способ записать таблицу астропсии в файл? - person Rose; 24.10.2017
comment
Не могли бы Вы уточнить? Самый быстрый по количеству кода или самый быстрый по времени вычислений? - person Iguananaut; 24.10.2017

Вероятно, самый простой способ — использовать zip():

data = zip(myOrbitJ2000Time, lat, lon, alt)

Таким образом, «данные» будут иметь формат, который вы хотите сериализовать. Не забудьте также сериализовать нужный заголовок.

person Austin A    schedule 12.10.2017
comment
Как сериализовать заголовок? / Что это обозначает? - person Rose; 12.10.2017
comment
Файл, на который вы ссылаетесь в качестве выходных данных, представляет собой файл csv, который содержит заголовок с именами столбцов для данных под ним. Это первая строка в файлах csv. Чтобы сериализовать его, вы вручную запишете эти данные в свой файл, используя что-то вроде writeDataToFile("J2000 Time, Lat, Lon, Alt\n"), а затем программно прокрутите список данных, созданный выше. - person Austin A; 12.10.2017