Последние статьи из блога
- MegaD-485-TTL - изолированный конвертер индустриального класса
- MegaD-4VC - подключаем сигналы 0-10В и 4-20мА к контроллеру
- MegaD-4CM - высокоточный 4-х канальный измеритель тока
- MegaD-4US - 4-х канальный датчик напряжения на DIN-рейку
- Как я боролся с собаками с помощью нейросети
- Интеграция ультразвукового датчика скорости и направления ветра
- Установка WiFi-камеры на робота-газонокосилку
- Твердотельный датчик направления и скорости ветра. Эксперимент
- iPhone на стене в качестве панели управления домом
- MegaD-16M-XT - подсветка выключателей и не только
- Переделка выключателей в кнопки и мини-обзор текущего рынка
- RadSens - модульный счетчик Гейгера с интерфейсом I2C
- "U" - значит универсальный. Обзор модуля MegaD-16U-XT
- SCD4x - современная альтернатива для измерения концентрации CO2
- HTU31D - новый датчик температуры и влажности с нагревательным элементом
- Измерение коэффициента пульсации ламп с помощью MegaD-2561
Расчет восхода и захода солнца. Автоматизация освещения
20/01/2011 23:14:11
Возможно, данная статья устарела.
Все новые статьи
В системах домашней автоматизации может потребоваться информация о восходе и заходе нашего светила. В отсутствие дополнительных датчиков можно автоматически включать и выключать уличное, садовое освещение, ночники, системы автополива и прочее.
Я уже писал о том, как я решил задачу управления уличным освещением с помощью камеры видеонаблюдения. Минус данного решения заключается в том, что чувствительность современных камер достаточно высока, а значит уловить порог снижения яркости изображения можно только тогда, когда на улице уже совсем стемнеет. Особенно актуальна проблема зимой, когда дополнительное влияние на общую яркость изображения оказывает снег.
Поэтому я решил рассчитывать ежедневно восход и заход солнца, дабы управлять уличным освещением более точно. Но, как это часто бывает, задача оказалась достаточно сложной. Образовалась масса сложных формул, понятий, таблиц. Одним словом, самостоятельно этот орех разгрызть я не смог, а точнее не захотел, так как нашел уже готовый класс для PHP.
<?php
class sun
{
var $latitude; #szerokosc geograficzna
var $longitude; #dlugosc geograficzna
var $timezone; #strefa czasowa
function sun ($latitude, $longitude, $timezone)
{
$this->latitude = $latitude;
$this->longitude = $longitude;
$this->timezone = $timezone;
$this->yday = date("z");
$this->mon = date("n");
$this->mday = date("j");
$this->year = date("Y");
#---------------------
$this->DST=$this->is_daylight_time(date("U"));
if ($this->DST)
{
$this->timezone = ($this->timezone + 1);
}
if ($this->timezone == "13")
{
$this->timezone = "-11";
}
#---------------------
$this->A = 1.5708;
$this->B = 3.14159;
$this->C = 4.71239;
$this->D = 6.28319;
$this->E = 0.0174533 * $this->latitude;
$this->F = 0.0174533 * $this->longitude;
$this->G = 0.261799 * $this->timezone;
#---------------------
# For astronomical twilight, use
#$this->R = -.309017;
# For nautical twilight, use
#$this->R = -.207912;
# For civil twilight, use
#$this->R = -.104528;
# For sunrise or sunset, use
$this->R = -.0145439;
#---------------------
}
function is_daylight_time($time)
{
list($dom, $dow, $month, $hour, $min) = explode(":", date("d:w:m:H:i", $time));
if ($month > 4 && $month < 10)
{
$this->retval = 1; # May thru September
}
elseif ($month == 4 && $dom > 7)
{
$this->retval = 1; # After first week in April
}
elseif ($month == 4 && $dom <= 7 && $dow == 0 && $hour >= 2)
{
$this->retval = 1; # After 2am on first Sunday ($dow=0) in April
}
elseif ($month == 4 && $dom <= 7 && $dow != 0 && ($dom-$dow > 0))
{
$this->retval = 1; # After Sunday of first week in April
}
elseif ($month == 10 && $dom < 25)
{
$this->retval = 1; # Before last week of October
}
elseif ($month == 10 && $dom >= 25 && $dow == 0 && $hour < 2)
{
$this->retval = 1; # Before 2am on last Sunday in October
}
elseif ($month == 10 && $dom >= 25 && $dow != 0 && ($dom-24-$dow < 1) )
{
$this->retval = 1; # Before Sunday of last week in October
}
else
{
$this->retval = 0;
}
return $this->retval;
}
function sunrise()
{
$J = $this->A;
$K = $this->yday + (($J - $this->F) / $this->D);
$L = ($K * .017202) - .0574039; # Solar Mean Anomoly
$M = $L + .0334405 * sin($L); # Solar True Longitude
$M += 4.93289 + (3.49066E-04) * sin(2 * $L);
if ($this->D == 0)
{
echo "Trying to normalize with zero offset..."; exit;
}
while ($M < 0)
{
$M = ($M + $this->D);
}
while ($M >= $this->D)
{
$M = ($M - $this->D);
}
if (($M / $this->A) - intval($M / $this->A) == 0)
{
$M += 4.84814E-06;
}
$P = sin($M) / cos($M); # Solar Right Ascension
$P = atan2(.91746 * $P, 1);
# Quadrant Adjustment
if ($M > $this->C)
{
$P += $this->D;
}
else
{
if ($M > $this->A)
{
$P += $this->B;
}
}
$Q = .39782 * sin($M); # Solar Declination
$Q = $Q / sqrt(-$Q * $Q + 1); # This is how the original author wrote it!
$Q = atan2($Q, 1);
$S = $this->R - (sin($Q) * sin($this->E));
$S = $S / (cos($Q) * cos($this->E));
if (abs($S) > 1)
{
echo 'none';
} # Null phenomenon
$S = $S / sqrt(-$S * $S + 1);
$S = $this->A - atan2($S, 1);
$S = $this->D - $S ;
$T = $S + $P - 0.0172028 * $K - 1.73364; # Local apparent time
$U = $T - $this->F; # Universal timer
$V = $U + $this->G; # Wall clock time
# Quadrant Determination
if ($this->D == 0)
{
echo "Trying to normalize with zero offset..."; exit;
}
while ($V < 0)
{
$V = ($V + $this->D);
}
while ($V >= $this->D)
{
$V = ($V - $this->D);
}
$V = $V * 3.81972;
$hour = intval($V);
$min = intval((($V - $hour) * 60) + 0.5);
// return date( "G:i ", mktime($hour,$min,0,$this->mon,$this->mday,$this->year) - 1800 );
return mktime($hour,$min,0,$this->mon,$this->mday,$this->year) - 1800;
}
function sunset()
{
$J = $this->C;
$K = $this->yday + (($J - $this->F) / $this->D);
$L = ($K * .017202) - .0574039; # Solar Mean Anomoly
$M = $L + .0334405 * sin($L); # Solar True Longitude
$M += 4.93289 + (3.49066E-04) * sin(2 * $L);
if ($this->D == 0)
{
echo "Trying to normalize with zero offset..."; exit;
}
while ($M < 0)
{
$M = ($M + $this->D);
}
while ($M >= $this->D)
{
$M = ($M - $this->D);
}
if (($M / $this->A) - intval($M / $this->A) == 0)
{
$M += 4.84814E-06;
}
$P = sin($M) / cos($M); # Solar Right Ascension
$P = atan2(.91746 * $P, 1);
# Quadrant Adjustment
if ($M > $this->C)
{
$P += $this->D;
}
else
{
if ($M > $this->A)
{
$P += $this->B;
}
}
$Q = .39782 * sin($M); # Solar Declination
$Q = $Q / sqrt(-$Q * $Q + 1); # This is how the original author wrote it!
$Q = atan2($Q, 1);
$S = $this->R - (sin($Q) * sin($this->E));
$S = $S / (cos($Q) * cos($this->E));
if (abs($S) > 1)
{
echo 'none';
} # Null phenomenon
$S = $S / sqrt(-$S * $S + 1);
$S = $this->A - atan2($S, 1);
#$S = $this->D - $S ;
$T = $S + $P - 0.0172028 * $K - 1.73364; # Local apparent time
$U = $T - $this->F; # Universal timer
$V = $U + $this->G; # Wall clock time
# Quadrant Determination
if ($this->D == 0)
{
echo "Trying to normalize with zero offset..."; exit;
}
while ($V < 0)
{
$V = ($V + $this->D);
}
while ($V >= $this->D)
{
$V = ($V - $this->D);
}
$V = $V * 3.81972;
$hour = intval($V);
$min = intval((($V - $hour) * 60) + 0.5);
//return date( "G:i ", mktime($hour,$min,0,$this->mon,$this->mday,$this->year) + 1800 );
return mktime($hour,$min,0,$this->mon,$this->mday,$this->year) + 1800;
}
}
//$ext_light = show_list($keys_id, "#key_pio#", "", 1, "key_label='ext_light'", 1);
$sun = new sun('55.72', '37.63', '3');
$my_sunrise = $sun->sunrise();
$my_sunset = $sun->sunset();
if ( time() >= $my_sunrise && $ext_light == 1 && time() < $my_sunset )
echo "Выключить свет";
if ( time() >= $my_sunset && $ext_light == 0 )
echo "Включить свет";
?>
Класс я нашел на каком-то польском сайте.
Пользоваться классом достаточно просто. Необходимо только знать широту, долготу и часовой пояс. Широту и долготу конкретной местности можно подсмотреть с помощью Google API. Если кому-то надо - расскажу как.
Например, для Москвы это:
$sun = new sun('55.72', '37.63', '3');
Я только изменил функции sunset() и sunrise() таким образом, чтобы свет выключался на 1800 секунд (30 минут) раньше и включался на 30 минут позже астрономического восхода и захода солнца. Это выяснилоась экспериментальным путем. Поэтому в соответствующих функциях измените или вовсе уберите коррекцию.
Если вам необходимо, чтобы функции возвращали значения в человеческом виде, закомментируйте последний return и уберите комментарий с предпоследнего.
Автор: Andrey_B
Любое использование материалов сайта возможно только с разрешения автора и с обязательным указанием источника.
Добавить комментарий:
Сортировка комментариев: Последние сверху | Первые сверху
2011-02-21 11:59:55 | Олег
Велосипед? :)
/ru.php.net/manual/en/function.date-sun-info.php
Возвращает:
sunrise: 05:52:11
sunset: 15:41:21
transit: 10:46:46
civil_twilight_begin: 05:24:08
civil_twilight_end: 16:09:24
nautical_twilight_begin: 04:52:25
nautical_twilight_end: 16:41:06
astronomical_twilight_begin: 04:21:32
astronomical_twilight_end: 17:12:00
Гражданские сумерки (civil_twilight) скорее всего нужны для включения-выключения освещения.
2011-02-21 12:25:55 | Andrey_B
Олег, а ведь вы правы. Действительно в PHP5 есть такая функция.
Только вот расчет времени отличается от моего. На 2-4 минуты. В целях автоматизации освещения несущественно, но интересно почему.
Я проверял свою функцию на сайтах, посвященных астрономии и расчет совпадал. Может быть кто-то возьмется определить? Какая из функций - та что представлена в статье или та, что заложена в PHP5 дает более правильный результат?
А вот civil_twilight действительно вещь полезная. Как раз наш случай ;)
Спасибо за полезный комментарий.
2011-03-11 21:08:35 | Александр
На сколько я помню - долгота москвы 37градусов 37 минут, следовательно - в формулу надо подставлять 37.63 - вероятно у Вас опечатка.
2011-10-16 22:08:55 | САНЧО
Может помочь программа RA4NCN
2016-02-19 11:18:26 | Шерзод
Хотел дополнить для тех кто живет не в России. У меня функция date_sun_info() показывал совсем другие значения. Оказалось при установки веб-сервера в php.ini значение date.timezone по умолчанию были "Europe/Moscow". Надо изменить на свой часовой пояс, в моем случае "Asia/Tashkent". Те кто не хочет копаться в файле настройки php.ini (так как для разных сборок они могут находится в разных местах), могут прямо в начало скрипта добавить ini_set('date.timezone', "Asia/Tashkent");
2016-02-19 11:47:11 | Шерзод
В мануалах пишут такой код:
date_sun_info(strtotime("2006-12-12"), 31.7667, 35.2333);
Если надо вывести восход/заход текущего дня, то не надо писать strtotime(date("Y-m-d")). Можно заменить функцией time(), так как strtotime("2006-12-12") все равно вставляет туда временную метку.
date_sun_info(time(), 31.7667, 35.2333); - думаю самый короткий код.
Если нужна инфа о завтрашнем дне, то time() + 86400 секунд в сутке (=606024):
date_sun_info(time() + 86400, 31.7667, 35.2333);
2016-03-27 14:53:53 | Александр Т.
Подскажите, как этот функционал добавить в дэмо интерфейс?
2018-06-30 02:49:55 | nihil
Добрый день. Тоже начал копать в этом направлении. Но прочитав Вашу статью про датчик освещенности и понял что с него считывать проще. Дальше пара эксперементов и готово ;)