Положение Солнца (азимут) в Луа

В LUA есть только одна функция, которую я смог найти в Интернете, но она дает неправильные значения (измерено с помощью профессиональных онлайн-инструментов).

Оказывается, что от восхода солнца до некоторого времени после полудня математика работает, но после этого угол Солнца возвращается к положению восхода солнца. Должен быть от 106° до 253°, сейчас от 106° до ~180° и до 106°.

Функция, которую я использую:

-- solar altitude, azimuth (degrees)
function sunposition(latitude, longitude, time)
    time = time or os.time()
    if type(time) == 'table' then time = os.time(time) end

    local date = os.date('*t', time)
    local timezone = (os.time(date) - os.time(os.date('!*t', time))) / 3600
    if date.isdst then timezone = timezone + 1 end

    local utcdate = os.date('*t', time - timezone * 3600)
    local latrad = math.rad(latitude)
    local fd = (utcdate.hour + utcdate.min / 60 + utcdate.sec / 3600) / 24
    local g = (2 * math.pi / 365.25) * (utcdate.yday + fd)
    local d = math.rad(0.396372 - 22.91327 * math.cos(g) + 4.02543 * math.sin(g) - 0.387205 * math.cos(2 * g)
      + 0.051967 * math.sin(2 * g) - 0.154527 * math.cos(3 * g) + 0.084798 * math.sin(3 * g))
    local t = math.rad(0.004297 + 0.107029 * math.cos(g) - 1.837877 * math.sin(g)
      - 0.837378 * math.cos(2 * g) - 2.340475 * math.sin(2 * g))
    local sha = 2 * math.pi * (fd - 0.5) + t + math.rad(longitude)

    local sza = math.acos(math.sin(latrad) * math.sin(d) + math.cos(latrad) * math.cos(d) * math.cos(sha))
    local saa = math.acos((math.sin(d) - math.sin(latrad) * math.cos(sza)) / (math.cos(latrad) * math.sin(sza)))

    return 90 - math.deg(sza), math.deg(saa)
end

Пример запроса:

lat, long = 45.327063, 14.442176 -- Rijeka, Croatia
time = {year=2016, month=2, day=17, hour=17, min=30} -- end of the day
altitude, azimuth = sunposition(lat, long, time)

Результат:

  • -0,1 градуса по высоте
  • 106 градусов по азимуту.

Результат должен быть:

  • -0,1 градуса по высоте
  • 253 градуса по азимуту.

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

Я использую его для своего приложения Corona SDK, которое будет показывать положение Солнца относительно устройства. Единственное решение, которое в настоящее время работает, — это скрипт PHP или Javascript, который мое приложение может запрашивать через вызов API через Интернет, но мне бы очень хотелось этого избежать.

Я очень благодарен за любую помощь от сообщества. Спасибо и люблю вас, люди! :)


person Edi Budimilic    schedule 17.02.2016    source источник


Ответы (2)


Я нашел способ/хак, чтобы решить эту проблему.

Функция осталась прежней:

-- solar altitude, azimuth (degrees)
function sunposition(latitude, longitude, time)
    time = time or os.time()
    if type(time) == 'table' then time = os.time(time) end

    local date = os.date('*t', time)
    local timezone = (os.time(date) - os.time(os.date('!*t', time))) / 3600
    if date.isdst then timezone = timezone + 1 end

    local utcdate = os.date('*t', time - timezone * 3600)
    local latrad = math.rad(latitude)
    local fd = (utcdate.hour + utcdate.min / 60 + utcdate.sec / 3600) / 24
    local g = (2 * math.pi / 365.25) * (utcdate.yday + fd)
    local d = math.rad(0.396372 - 22.91327 * math.cos(g) + 4.02543 * math.sin(g) - 0.387205 * math.cos(2 * g)
      + 0.051967 * math.sin(2 * g) - 0.154527 * math.cos(3 * g) + 0.084798 * math.sin(3 * g))
    local t = math.rad(0.004297 + 0.107029 * math.cos(g) - 1.837877 * math.sin(g)
      - 0.837378 * math.cos(2 * g) - 2.340475 * math.sin(2 * g))
    local sha = 2 * math.pi * (fd - 0.5) + t + math.rad(longitude)

    local sza = math.acos(math.sin(latrad) * math.sin(d) + math.cos(latrad) * math.cos(d) * math.cos(sha))
    local saa = math.acos((math.sin(d) - math.sin(latrad) * math.cos(sza)) / (math.cos(latrad) * math.sin(sza)))

    return 90 - math.deg(sza), math.deg(saa)
end

Добавленный код, устраняющий проблему:

function getSunPos(lat, long, time)
    findTime = {}
    findTime.hour, findTime.min = time.hour, time.min
    fixedAzimuthLast, fixedAzimuth = 0, 0
    for i=0,23 do
        for j=0,59 do
            time.hour, time.min = i, j
            local altitude, azimuth = sunposition(lat, long, time)
            -- fix azimuth
            if fixedAzimuthLast < azimuth then 
                fixedAzimuthLast = azimuth
                fixedAzimuth = fixedAzimuthLast
            else
                fixedAzimuth = fixedAzimuthLast + (180 - azimuth)
            end
            -- find azimuth at target time
            if findTime.hour == i and findTime.min == j then
                -- final result
                return altitude, fixedAzimuth
            end
        end
    end
end

И, наконец, чтобы получить правильный результат:

lat, long = 45.327063, 14.442176
altitude, azimuth = getSunPos(lat, long, os.date('*t', os.time()))

Вот и все. Я был бы более доволен полной функцией, которая правильно выполняет математику, но этого будет достаточно. Он работает и был протестирован в 3 местах по всему миру.

person Edi Budimilic    schedule 26.02.2016

Arccos угла равен углу 360, так как значения их косинусов равны. Вы можете просто вернуть 360-угол.

person Amir    schedule 19.02.2016
comment
Это работает только с алгоритмом, который вручную находит положение заката, глядя на высоту Солнца, и, поскольку высота работает нормально, это помогло мне найти решение в ответе, который я отправил. - person Edi Budimilic; 26.02.2016