PyEphem: проблема преобразования отрицательных углов путем анализа строкового представления - PullRequest
0 голосов
/ 11 сентября 2018

Я использовал PyEphem, чтобы поиграть с движением планет относительно определенной позиции на Земле. Однако иногда я замечаю, что результаты, которые я получаю от PyEphem, кажутся непостоянными для склонения, в то время как прямое вознесение кажется непрерывным. Координаты RA и Dec принимаются на этих графиках каждые 30 минут.

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

Есть идеи, почему это будет? Я могу опубликовать свой сценарий, если это также поможет.

Dec Ma

Код:

from ephem import *
import datetime
import re

def cSTI(number):
    degrees = int(number[0])
    minutes = int(number[1])
    seconds = float(number[2])
    full = degrees + (minutes/60) + (seconds/60/60)

    return round(full,2)

planets = [Sun(),Mercury(),Venus(),Moon(),Mars(),Jupiter(),Saturn(),Uranus(),Neptune(),Pluto()]
masses =  [1.989*10**30,.330*10**24,4.87*10**24,0.073*10**24,0.642*10**24,1898*10**24,568*10**24,86.8*10**24,102*10**24,0.0146*10**24]
earthMass = 5.97*10**24
gravitational_constant = 6.67408 * 10**-11
theYear = '2018'
theMonth = '9'
theDay = '8'
revere = Observer()
revere.lon = '-42.4084'
revere.lat = '71.0120'
currentDate = datetime.datetime(2018,11,30,12,0,0)
lowerDateLimit = datetime.datetime(2018,9,1,12,0,0)
revere.date = currentDate
print('DATE SUN(RA) SUN(DEC)    SUN(AZI)    SUN(GFORCE) MERCURY(RA) MERCURY(DEC)    MERCURY(AZI)    MERCURY(GFORCE) VENUS(RA)   VENUS(DEC)  VENUS(AZI)  VENUS(GFORCE)   MOON(RA)    MOON(DEC)   MOON(AZI)   MOON(GFORCE)    MARS(RA)    MARS(DEC)   MARS(AZI)   MARS(GFORCE)    JUPITER(RA) JUPITER(DEC)    JUPITER(AZI)    JUPITER(GFORCE) SATURN(RA)  SATURN(DEC) SATURN(AZI) SATURN(GFORCE)  URANUS(RA)  URANUS(DEC) URANUS(AZI) URANUS(GFORCE)  NEPTUNE(RA) NEPTUNE(DEC)    NEPTUNE(AZI)    NEPTUNE(GFORCE) PLUTO(RA)   PLUTO(DEC)  PLUTO(AZI)  PLUTO(GFORCE)   ')

while (currentDate> lowerDateLimit):
    print('%s   ' % (revere.date),end = ' ')
    planetIndex = 0;
    for planet in planets:
        planet.compute(revere)

        rightascension = str(planet.ra)
        declination = str(planet.dec)
        azimuth = str(planet.az)

        rightascension = re.split('[:]',rightascension)
        declination = re.split('[:]',declination)
        azimuth = re.split('[:]',azimuth )

        rightascension = cSTI(rightascension);
        declination = cSTI(declination);
        azimuth = cSTI(azimuth);
        GFORCE = gravitational_constant * ((masses[planetIndex]*earthMass)/(planet.earth_distance**2))
        print('%s   %s  %s  %s  ' % (rightascension,declination,azimuth,GFORCE),end = ' ')
        planetIndex+=1
    print()
    currentDate += datetime.timedelta(minutes=-30)
    revere.date = currentDate

1 Ответ

0 голосов
/ 12 сентября 2018

Я полагаю, проблема в том, что вы вручную конвертируете из ephem.Angle() (который является объектом ra, azi и т. Д.) В float.


EDIT

В частности, проблема возникает из-за того, что в вашей функции cSTI(), когда значение отрицательное, вы должны вычитать (а не добавлять) разные значения.

Исправленная реализация будет выглядеть следующим образом:

import math

def cSTI(number):
    degrees = int(number[0])
    minutes = int(number[1])
    seconds = float(number[2])
    full = degrees + \
        math.copysign((minutes / 60), degrees) + \
        math.copysign((seconds / 60 / 60), degrees)
    return round(full, 2)

Обратите внимание, что это своего рода минимальная модификация вашего кода, чтобы заставить его работать.Я бы предложил вам написать несколько документов о том, как написать лучший код, начиная с PEP8 .Кроме того, вам следует избегать магических чисел , подобных диким 60 в вашем коде.

Если бы я делал это вручную, я бы также избегал использования ненужных регулярных выражений и фактически начинал быиз ephem.Angle() объекта, подобного:

import math

MIN_IN_DEG = SEC_IN_MIN = 60

def ephem_angle_to_float(angle):
    degrees, minutes, seconds = [float(s) for s in str(angle).split(':')]
    value = abs(degrees) + \
        minutes / MIN_IN_DEG + \
        seconds / SEC_IN_MIN / MIN_IN_DEG
    return math.copysign(value, degrees)

, и это может быть вызвано непосредственно к planet.ra или planet.dec в вашем коде.

Но я бы не стал делать это вручную.См. Ниже.


Но хорошая новость заключается в том, что нет необходимости вручную вычислять это самостоятельно, вы можете просто привести его к float для значения в радианах, как указано в официальная документация .

Следующий код является вашей отработанной версией, которая создает табличные данные в виде списка списков (которые можно легко преобразовать в CSV с помощью csv из стандартной библиотеки Python).Был сделан ряд модификаций:

  • небесные тела и массы теперь более явны
  • объекты ephem создаются динамически
  • постоянная G теперьвзято из scipy.constants (которое извлекается из последних CODATA)
  • метки и данные создаются динамически
  • по возможности избегалась явная индексация
  • даты начала и окончаниябыли несколько перевернуты, поскольку это не имеет значения для проблемы

Обратите внимание, что здесь не выполняется преобразование, поскольку это может привести к потере информации.

Вот код:

import numpy as np
import scipy as sp

import ephem
import scipy.constants
import datetime

celestial_masses = {
    'sun': 1.989e30,
    'mercury': 0.330e24,
    'venus': 4.870e24,
    'earth': 5.970e24,
    'moon': 0.073e24,
    'mars': 0.642e24,
    'jupiter': 1898e24,
    'saturn': 568e24,
    'uranus': 86.8e24,
    'neptune': 102e24,
    'pluto': 0.0146e24, }
celestials = {
    name: (eval('ephem.{}()'.format(name.title())), mass)
    for name, mass in celestial_masses.items() if name != 'earth'}
gg = sp.constants.gravitational_constant

observer = ephem.Observer()
# Revere, Massachusetts, USA
observer.lon = '-42.4084'
observer.lat = '71.0120'

start_datetime = datetime.datetime(2018, 9, 1, 12, 0, 0)
end_datetime = datetime.datetime(2018, 11, 30, 12, 0, 0)

values = 'ra', 'dec', 'azi', 'gforce'
labels = ('date',) + tuple(
    '{}({})'.format(name, value)
    for name in celestials.keys()
    for value in values)
data = []
observer.date = start_datetime
delta_datetime = datetime.timedelta(minutes=30)
while (observer.date.datetime() < end_datetime):
    row = [observer.date]
    for name, (body, mass) in celestials.items():
        body.compute(observer)
        row.extend([
            body.ra, body.dec, body.az,
            gg * ((mass * celestial_masses['earth']) / (body.earth_distance ** 2))])
    data.append(row)
    observer.date = observer.date.datetime() + delta_datetime

Для преобразования в CSV (со значениями с плавающей запятой для ra, dec, az и gforce) это можно сделать следующим образом:

import csv

filepath = 'celestial_bodies_revere_MA.csv'
with open(filepath, 'w') as file_obj:
    csv_obj = csv.writer(file_obj)
    csv_obj.writerow(labels)
    for row in data:
        # : use default string conversion
        # csv_obj.writerow(row)

        # : a possible conversion to float for all but the `date`
        csv_obj.writerow([
            float(x) if i != labels.index('date') else x
            for i, x in enumerate(row)])

Дополнительно,Вот некоторый код для графиков, показывающий, что проблема с отрицательными значениями исчезла:

import matplotlib as mpl
import matplotlib.pyplot as plt

x = [row[labels.index('date')].datetime() for row in data]
fig, axs = plt.subplots(4, 1, figsize=(16, 26))
for i, value in enumerate(values):
    pos = i
    for name in celestials.keys():
        y = [row[labels.index('{}({})'.format(name, value))] for row in data]
        axs[pos].plot(x, y, label=name)
        axs[pos].set_title(value)
        axs[pos].legend(loc='center left', bbox_to_anchor=(1, 0.5))
fig.tight_layout()

, что приводит к:

Plots

...