Local solar time function from UTC and longitude

泄露秘密 提交于 2019-11-29 04:04:34

Or if you want to go even shorter, you could use NOAA's low-accuracy equations:

#!/usr/local/bin/python

import sys
from datetime import datetime, time, timedelta
from math import pi, cos, sin

def solar_time(dt, longit):
    return ha

def main():
    if len(sys.argv) != 4:
        print 'Usage: hour_angle.py [YYYY/MM/DD] [HH:MM:SS] [longitude]'
        sys.exit()
    else:
        dt = datetime.strptime(sys.argv[1] + ' ' + sys.argv[2], '%Y/%m/%d %H:%M:%S')
        longit = float(sys.argv[3])

    gamma = 2 * pi / 365 * (dt.timetuple().tm_yday - 1 + float(dt.hour - 12) / 24)
    eqtime = 229.18 * (0.000075 + 0.001868 * cos(gamma) - 0.032077 * sin(gamma) \
             - 0.014615 * cos(2 * gamma) - 0.040849 * sin(2 * gamma))
    decl = 0.006918 - 0.399912 * cos(gamma) + 0.070257 * sin(gamma) \
           - 0.006758 * cos(2 * gamma) + 0.000907 * sin(2 * gamma) \
           - 0.002697 * cos(3 * gamma) + 0.00148 * sin(3 * gamma)
    time_offset = eqtime + 4 * longit
    tst = dt.hour * 60 + dt.minute + dt.second / 60 + time_offset
    solar_time = datetime.combine(dt.date(), time(0)) + timedelta(minutes=tst)
    print solar_time

if __name__ == '__main__':
    main()

Using ephem's sidereal_time() method:

import ephem # pip install pyephem (on Python 2)
             # pip install ephem   (on Python 3)

def solartime(observer, sun=ephem.Sun()):
    sun.compute(observer)
    # sidereal time == ra (right ascension) is the highest point (noon)
    hour_angle = observer.sidereal_time() - sun.ra
    return ephem.hours(hour_angle + ephem.hours('12:00')).norm  # norm for 24h

Note: ephem.hours is a float number that represents an angle in radians and converts to/from a string as "hh:mm:ss.ff".

For comparison, here's the "utc + longitude" formula:

import math
from datetime import timedelta

def ul_time(observer):
    utc_dt = observer.date.datetime()
    longitude = observer.long
    return utc_dt + timedelta(hours=longitude / math.pi * 12)

Example

from datetime import datetime

# "solar time" for some other cities
for name in ['Los Angeles', 'New York', 'London',
             'Paris', 'Moscow', 'Beijing', 'Tokyo']:
    city = ephem.city(name)
    print("%-11s %11s %s" % (name, solartime(city),
                             ul_time(city).strftime('%T')))

# set date, longitude manually
o = ephem.Observer()
o.date = datetime(2012, 4, 15, 1, 0, 2) # some utc time
o.long = '00:00:00.0' # longitude (you could also use a float (radians) here)
print("%s %s" % (solartime(o), ul_time(o).strftime('%T')))

Output

Los Angeles 14:59:34.11 14:44:30
New York    17:56:31.27 17:41:27
London      22:52:02.04 22:36:58
Paris       23:01:56.56 22:46:53
Moscow       1:23:00.33 01:07:57
Beijing      6:38:09.53 06:23:06
Tokyo        8:11:17.81 07:56:15
1:00:00.10 01:00:01

I took a look at Jean Meeus' Astronomical Algorithms. I think you might be asking for local hour angle, which can be expressed in time (0-24hr), degrees (0-360) or radians (0-2pi).

I'm guessing you can do this with ephem. But just for the heck of it, here's some python:

#!/usr/local/bin/python

import sys
from datetime import datetime, time, timedelta
from math import pi, sin, cos, atan2, asin

# helpful constant
DEG_TO_RAD = pi / 180

# hardcode difference between Dynamical Time and Universal Time
# delta_T = TD - UT
# This comes from IERS Bulletin A
# ftp://maia.usno.navy.mil/ser7/ser7.dat
DELTA = 35.0

def coords(yr, mon, day):
    # @input year (int)
    # @input month (int)
    # @input day (float)
    # @output right ascention, in radians (float)
    # @output declination, in radians (float)

    # get julian day (AA ch7)
    day += DELTA / 60 / 60 / 24 # use dynamical time
    if mon <= 2:
        yr -= 1
        mon += 12
    a = yr / 100
    b = 2 - a + a / 4
    jd = int(365.25 * (yr + 4716)) + int(30.6 * (mon + 1)) + day + b - 1524.5

    # get sidereal time at greenwich (AA ch12)
    t = (jd - 2451545.0) / 36525

    # Calculate mean equinox of date (degrees)
    l = 280.46646 + 36000.76983 * t + 0.0003032 * t**2
    while (l > 360):
        l -= 360
    while (l < 0):
        l += 360

    # Calculate mean anomoly of sun (degrees)
    m = 357.52911 + 35999.05029 * t - 0.0001537 * t**2

    # Calculate eccentricity of Earth's orbit
    e = 0.016708634 - 0.000042037 * t - 0.0000001267 * t**2

    # Calculate sun's equation of center (degrees)
    c = (1.914602 - 0.004817 * t - .000014 * t**2) * sin(m * DEG_TO_RAD) \
        + (0.019993 - .000101 * t) * sin(2 * m * DEG_TO_RAD) \
        + 0.000289 * sin(3 * m * DEG_TO_RAD)

    # Calculate the sun's radius vector (AU)
    o = l + c # sun's true longitude (degrees)
    v = m + c # sun's true anomoly (degrees)

    r = (1.000001018 * (1 - e**2)) / (1 + e * cos(v * DEG_TO_RAD))

    # Calculate right ascension & declination
    seconds = 21.448 - t * (46.8150 + t * (0.00059 - t * 0.001813))
    e0 = 23 + (26 + (seconds / 60)) / 60

    ra = atan2(cos(e0 * DEG_TO_RAD) * sin(o * DEG_TO_RAD), cos(o * DEG_TO_RAD)) # (radians)
    decl = asin(sin(e0 * DEG_TO_RAD) * sin(o * DEG_TO_RAD)) # (radians)

    return ra, decl

def hour_angle(dt, longit):
    # @input UTC time (datetime)
    # @input longitude (float, negative west of Greenwich)
    # @output hour angle, in degrees (float)

    # get gregorian time including fractional day
    y = dt.year
    m = dt.month
    d = dt.day + ((dt.second / 60.0 + dt.minute) / 60 + dt.hour) / 24.0 

    # get right ascention
    ra, _ = coords(y, m, d)

    # get julian day (AA ch7)
    if m <= 2:
        y -= 1
        m += 12
    a = y / 100
    b = 2 - a + a / 4
    jd = int(365.25 * (y + 4716)) + int(30.6 * (m + 1)) + d + b - 1524.5

    # get sidereal time at greenwich (AA ch12)
    t = (jd - 2451545.0) / 36525
    theta = 280.46061837 + 360.98564736629 * (jd - 2451545) \
            + .000387933 * t**2 - t**3 / 38710000

    # hour angle (AA ch13)
    ha = (theta + longit - ra / DEG_TO_RAD) % 360

    return ha

def main():
    if len(sys.argv) != 4:
        print 'Usage: hour_angle.py [YYYY/MM/DD] [HH:MM:SS] [longitude]'
        sys.exit()
    else:
        dt = datetime.strptime(sys.argv[1] + ' ' + sys.argv[2], '%Y/%m/%d %H:%M:%S')
        longit = float(sys.argv[3])
    ha = hour_angle(dt, longit)
    # convert hour angle to timedelta from noon
    days = ha / 360
    if days > 0.5:
        days -= 0.5
    td = timedelta(days=days)
    # make solar time
    solar_time = datetime.combine(dt.date(), time(12)) + td
    print solar_time

if __name__ == '__main__':
    main()

Trying again, with ephem. I included latitude and elevation as arguments, but they are not needed of course. You can just call them 0 for your purposes.

#!/usr/local/bin/python

import sys
from datetime import datetime, time, timedelta
import ephem

def hour_angle(dt, longit, latit, elev):
    obs = ephem.Observer()
    obs.date = dt.strftime('%Y/%m/%d %H:%M:%S')
    obs.lon = longit
    obs.lat = latit
    obs.elevation = elev
    sun = ephem.Sun()
    sun.compute(obs)
    # get right ascention
    ra = ephem.degrees(sun.g_ra) - 2 * ephem.pi

    # get sidereal time at greenwich (AA ch12)
    jd = ephem.julian_date(dt)
    t = (jd - 2451545.0) / 36525
    theta = 280.46061837 + 360.98564736629 * (jd - 2451545) \
            + .000387933 * t**2 - t**3 / 38710000

    # hour angle (AA ch13)
    ha = (theta + longit - ra * 180 / ephem.pi) % 360
    return ha

def main():
    if len(sys.argv) != 6:
        print 'Usage: hour_angle.py [YYYY/MM/DD] [HH:MM:SS] [longitude] [latitude] [elev]'
        sys.exit()
    else:
        dt = datetime.strptime(sys.argv[1] + ' ' + sys.argv[2], '%Y/%m/%d %H:%M:%S')
        longit = float(sys.argv[3])
        latit = float(sys.argv[4])
        elev = float(sys.argv[5])

    # get hour angle
    ha = hour_angle(dt, longit, latit, elev)

    # convert hour angle to timedelta from noon
    days = ha / 360
    if days > 0.5:
        days -= 0.5
    td = timedelta(days=days)

    # make solar time
    solar_time = datetime.combine(dt.date(), time(12)) + td
    print solar_time

if __name__ == '__main__':
    main()

For a very simple function & very very approximated local time: Time variation goes from -12h to +12h and longitude goes from -180 to 180. Then:


import datetime as dt

def localTimeApprox(myDateTime, longitude):
   """Returns local hour approximation"""
   return myDateTime+dt.timedelta(hours=(longitude*12/180))

Sample calling: localTimeApprox(dt.datetime(2014, 7, 9, 20, 00, 00), -75)

Returns: datetime.datetime(2014, 7, 9, 15, 0)

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!