ФОРУМ КУПИТЬ

Последние статьи

ВСЕ СТАТЬИ

Программный расчет прогноза погоды

20/02/2011 15:34:13

В тот год осенняя погода
Стояла долго на дворе,
Зимы ждала, ждала природа.
Снег выпал только в январе...

(Пушкин А.С. "Евгений Онегин")

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

Zambretti Forecaster
Zambretti Forecaster дает правильный результат в 91% случаев

Наиболее часто в бытовых приборах и метеостанциях используется алгоритм, который называется "предсказатель Zambretti". Этот алгоритм в простейшем виде использует всего три параметра: текущее атмосферное давление, тенденция изменения давления и направление ветра. Алгоритм был разработан в начале 20 века в Европе на основе большого количества эмпирических наблюдений и данных. Тем не менее, несмотря на свою "бородатость" и простоту, методика Zambretti дает правильный результат более чем в 90% случаях (у буржуев, разумеется).

Привожу реализацию алгоритма Zambretti на языке PHP

<?
//$z_where  Северное = 1 (это наше, если что) или Южное = 2 полушарие
//$z_baro_top  верхний предел 'погодного окна' (1050.0 гПа для Великобритании)
//$z_baro_bottom     нижний предел 'погодного окна' (950.0 гПа для Великобритании)

// usage:   forecast = betel_cast( $z_hpa, $z_month, $z_wind, $z_trend [, $z_where] [, $z_baro_top] [, $z_baro_bottom]);
// $z_hpa - Атмосферное давление в гПа
// $z_month текущий месяц, от 1 до 12
// $z_wind текущее направление ветра в английской системе координат типа N, NNW, NW и т.д.
// $z_trend изменения в атмосферном давлении: 0 = не меняется, 1 = растет, 2 = снижается

function betel_cast( $z_hpa, $z_month, $z_wind, $z_trend, $z_where = 1, $z_baro_top = 1050, $z_baro_bottom = 950, $wh_temp_out = 20)
{

$z_forecast_uk = Array("Settled fine", "Fine weather", "Becoming fine", "Fine,
becoming less settled", "Fine, possible showers", "Fairly fine,
improving", "Fairly fine, possible showers early", "Fairly fine,
showery later", "Showery early, improving", "Changeable,
mending", "Fairly fine, showers likely",
"Rather unsettled clearing later", "Unsettled, probably improving",
"Showery, bright intervals", "Showery, becoming less settled",
"Changeable, some rain", "Unsettled, short fine intervals",
"Unsettled, rain later", "Unsettled, some rain",
"Mostly very unsettled", "Occasional rain, worsening",
"Rain at times, very unsettled", "Rain at frequent intervals",
"Rain, very unsettled", "Stormy, may improve", "Stormy,
much rain");

// Если зима, то снег, а не дожди!
if ( $wh_temp_out < 2 )
$z_forecast = Array("Отличная, ясно", "Хорошая, ясно",
"Становление хорошей, ясной", "Хорошая, но ухудшается",
"Хорошая, возможен снегопад", "Достаточно хорошая, улучшается",
"Достаточно хорошая, возможен снегопад",
"Достаточно хорошая, но ожидается снегопад",
"Снегопад, но улучшается", "Переменчивая, но улучшается",
"Достаточно хорошая, вероятен снегопад", "Пасмурно, но проясняется",
"Пасмурно, возможно, улучшение",
"Снегопады, возможны временные прояснения",
"Снегопады, становится менее устойчивой",
"Переменчивая, небольшой снег", "Пасмурная, короткие прояснения",
"Пасмурная, ожидается снег", "Пасмурная, временами снег",
"Преимущественно, очень пасмурная", "Временами снег, ухудшение",
"Временами снег, очень плохая, пасмурно", "Снег очень часто",
"Снег, очень плохая, пасмурно", "Штормовая, но улучшается",
"Штормовая!, снегопад");
else
$z_forecast = Array("Отличная, ясно", "Хорошая, ясно",
"Становление хорошей, ясной", "Хорошая, но ухудшается",
"Хорошая, возможен ливень", "Достаточно хорошая, улучшается",
"Достаточно хорошая, возможен ливень",
"Достаточно хорошая, но ожидается ливень", "Ливень, но улучшается",
"Переменчивая, но улучшается", "Достаточно хорошая, вероятны ливни",
"Пасмурно, но проясняется", "Пасмурно, возможно, улучшение",
"Ливни, возможны временные прояснения",
"Ливни, становится менее устойчивой",
"Переменчивая, небольшие дожди", "Пасмурная, короткие прояснения",
"Пасмурная, ожидаются дожди", "Пасмурная, временами дожди",
"Преимущественно, очень пасмурная", "Временами дожди, ухудшение",
"Временами дожди, очень плохая, пасмурно", "Дожди очень часто",
"Дожди, очень плохая, пасмурно", "Штормовая, но улучшается",
"Штормовая!, дожди");

// equivalents of Zambretti 'dial window' letters A - Z
$rise_options  = Array(25,25,25,24,24,19,16,12,11,9,8,6,5,2,1,1,0,0,0,0,0,0) ;
$steady_options  = Array(25,25,25,25,25,25,23,23,22,18,15,13,10,4,1,1,0,0,0,0,0,0) ;
$fall_options = Array(25,25,25,25,25,25,25,25,23,23,21,20,17,14,7,3,1,1,1,0,0,0) ;

    $z_range = $z_baro_top - $z_baro_bottom;
    $z_constant = round(($z_range / 22), 3);

    $z_season = (($z_month >= 4) && ($z_month <= 9)) ;    // true if 'Summer'

    if ($z_where == 1) {         // North hemisphere
        if ($z_wind == "N") { 
            $z_hpa += 6 / 100 * $z_range ; 
        } else if ($z_wind == "NNE") { 
            $z_hpa += 5 / 100 * $z_range ; 
        } else if ($z_wind == "NE") { 
            $z_hpa += 5 / 100 * $z_range ; 
        } else if ($z_wind == "ENE") { 
            $z_hpa += 2 / 100 * $z_range ; 
        } else if ($z_wind == "E") { 
            $z_hpa -= 0.5 / 100 * $z_range ; 
        } else if ($z_wind == "ESE") { 
            $z_hpa -= 2 / 100 * $z_range ; 
        } else if ($z_wind == "SE") { 
            $z_hpa -= 5 / 100 * $z_range ; 
        } else if ($z_wind == "SSE") { 
            $z_hpa -= 8.5 / 100 * $z_range ; 
        } else if ($z_wind == "S") { 
            $z_hpa -= 12 / 100 * $z_range ; 
        } else if ($z_wind == "SSW") { 
            $z_hpa -= 10 / 100 * $z_range ;  //
        } else if ($z_wind == "SW") { 
            $z_hpa -= 6 / 100 * $z_range ; 
        } else if ($z_wind == "WSW") { 
            $z_hpa -= 4.5 / 100 * $z_range ;  //
        } else if ($z_wind == "W") { 
            $z_hpa -= 3 / 100 * $z_range ; 
        } else if ($z_wind == "WNW") { 
            $z_hpa -= 0.5 / 100 * $z_range ; 
        }else if ($z_wind == "NW") { 
            $z_hpa += 1.5 / 100 * $z_range ; 
        } else if ($z_wind == "NNW") { 
            $z_hpa += 3 / 100 * $z_range ; 
        }
        if ($z_season == TRUE) {     // if Summer
            if ($z_trend == 1) {     // rising
                $z_hpa += 7 / 100 * $z_range; 
            } else if ($z_trend == 2) {  //    falling
                $z_hpa -= 7 / 100 * $z_range;
            }
        }
    } else {     // must be South hemisphere
        if ($z_wind == "S") { 
            $z_hpa += 6 / 100 * $z_range ; 
        } else if ($z_wind == "SSW") { 
            $z_hpa += 5 / 100 * $z_range ; 
        } else if ($z_wind == "SW") { 
            $z_hpa += 5 / 100 * $z_range ; 
        } else if ($z_wind == "WSW") { 
            $z_hpa += 2 / 100 * $z_range ; 
        } else if ($z_wind == "W") { 
            $z_hpa -= 0.5 / 100 * $z_range ; 
        } else if ($z_wind == "WNW") { 
            $z_hpa -= 2 / 100 * $z_range ; 
        } else if ($z_wind == "NW") { 
            $z_hpa -= 5 / 100 * $z_range ; 
        } else if ($z_wind == "NNW") { 
            $z_hpa -= 8.5 / 100 * $z_range ; 
        } else if ($z_wind == "N") { 
            $z_hpa -= 12 / 100 * $z_range ; 
        } else if ($z_wind == "NNE") { 
            $z_hpa -= 10 / 100 * $z_range ;  //
        } else if ($z_wind == "NE") { 
            $z_hpa -= 6 / 100 * $z_range ; 
        } else if ($z_wind == "ENE") { 
            $z_hpa -= 4.5 / 100 * $z_range ;  //
        } else if ($z_wind == "E") { 
            $z_hpa -= 3 / 100 * $z_range ; 
        } else if ($z_wind == "ESE") { 
            $z_hpa -= 0.5 / 100 * $z_range ; 
        }else if ($z_wind == "SE") { 
            $z_hpa += 1.5 / 100 * $z_range ; 
        } else if ($z_wind == "SSE") { 
            $z_hpa += 3 / 100 * $z_range ; 
        }
        if ($z_season == FALSE) {    // if Winter
            if ($z_trend == 1) {  // rising
                $z_hpa += 7 / 100 * $z_range; 
            } else if ($z_trend == 2) {  // falling
                $z_hpa -= 7 / 100 * $z_range;
            }
        }
    }    // END North / South

    if($z_hpa == $z_baro_top) {$z_hpa = $z_baro_top - 1;}
    $z_option = floor(($z_hpa - $z_baro_bottom) / $z_constant);
     $z_output = "";

    if($z_option < 0) {
        $z_option = 0;
        $z_output = "Exceptional Weather, ";
    }
    if($z_option > 21) {
        $z_option = 21;
        $z_output = "Exceptional Weather, ";
    }

    if ($z_trend == 1) {
        $z_output .= $z_forecast[$rise_options[$z_option]]." (".$z_forecast_uk[$rise_options[$z_option]].")" ;
    } else if ($z_trend == 2) {
        $z_output .= $z_forecast[$fall_options[$z_option]]." (".$z_forecast_uk[$fall_options[$z_option]].")" ;
    } else {
        $z_output .= $z_forecast[$steady_options[$z_option]]." (".$z_forecast_uk[$steady_options[$z_option]].")" ;
    }
    return ($z_output) ;
}           
?>

По всей видимости, алгоритм был разработан в западной Европе, поэтому мне пришлось добавить в него такое понятие, как "снег". Для этого я добавил еще один параметр - текущую температуру наружнего воздуха. В зависимости от температуры, используется тот или иной массив выходных значений. В результате мы получаем короткую фразу с прогнозом на ближайшие часы. Существует мнение, что эта методика дает наиболее приемлемый результат на данных, взятых в 9 утра. Переводчик из меня тот еще, поэтому конструктивные предложения по более корректному переводу заложенных в алгоритм Zambretti погодных состояний приветствуется.

PHP-скрипт в системе умного дома по выводу прогноза

<?
$wind_dir_text_uk = array("N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW", "FALSE");

// Переменные $abs_pressure, $abs_pressure_1h, $wind_dir_avg, $wh_temp_out берутся из базы данных.
// Процесс заполнения базы и выборки из нее подробно рассмотрен в предыдущей статье,
// поэтому здесь опускаем эту часть кода

// Бесхитростное определение тенденции в изменении давления
// Здесь и в основной функции значения переведены из мм.рт.ст в гПа
if ( $abs_pressure > $abs_pressure_1h + 0.25)
{
    $pressure_trend = 1;
    $pressure_trend_text = "Рост";
}
elseif ( $abs_pressure_1h > $abs_pressure + 0.25)
{
    $pressure_trend = 2;
    $pressure_trend_text = "Падение";
}
else
{
    $pressure_trend = 0;
    $pressure_trend_text = "Не меняется";
}

$forecast = betel_cast($abs_pressure, date('n'), $wind_dir_text_uk[$wind_dir_avg], $pressure_trend, 1, 1050, 950, $wh_temp_out);

echo "Тенденция давления: $pressure_trend_text<br>";
echo "Прогноз: $forecast";
?>

На выходе получаем что-то вроде этого:

Я постараюсь самым тщательным образом проанализировать точность такого рода прогноза, о чем обещаю доложить в этой статье. Ведь как говаривал Федор Иванович Тютчев "Умом Россию не понять, аршином общим не измерить". То, что применимо под таким чудным русскому слуху именем Zambretti и хорошо зарекомендовало себя в Соединенном Королевстве, может у нас и того... ногу сломать, в лучшем случае. Одним словом, вспомним уроки природоведения.

Автор: Andrey_B
Любое использование материалов сайта возможно только с разрешения автора и с обязательным указанием источника.



Добавить комментарий:

(необязательно, не отображается на сайте)


Сортировка комментариев: Последние сверху | Первые сверху

2020-03-28 15:11:23 | Сергей
Я извиняюсь, могли бы вы подсказать...не програмисту. Код из первого окна это отдельный файл php лежащий в папке. А второй это типа метод...Я хочу его в мажордомо запихать...но вот как пока не понял.
Спасибо.


2011-05-27 22:01:08 | Andrey_B
Спасибо, Олег! Я думал об этом. Но алгоритм Zambretti некоторым образом эти осадки дифференцирует на дождь, ливень и т.д., что увеличивает информативность прогноза. Правда, можно ведь писать сильные осадки или просто осадки.


2011-05-27 21:57:30 | Олег
Да вместо дождь или снег, можно просто осадки, что бы не думалось )


2011-04-07 22:20:29 | Andrey_B
Ставр, в целом тенденцию алгоритм Zambretti угадывает. Причем что интересно, алгоритм почти на 100% совпадает с прогнозом, который дает гисметео на сутки. Видимо, алгоритмы в этой сфере за 100 лет не сильно далеко ушли. ;)
Зимой алгоритм достаточно правдиво предсказывал погоду. Сейчас сложнее. Апрель в континентальной России не тоже самое, что апрель в западной Европе, к которой адаптирован алгоритм. То есть в общем если алгоритм, например, предсказывает дождь, то пасмурная погода скорее всего случается. Если алгоритм предсказывает шторм, то ветер в 7 м/с будет. Хотя для нас тут в степи 7 м/с - обычное дело. ;)
В целом, польза от расчета есть. Ориентироваться на него можно.
А мне вот интересно когда будет настоящий шторм. Раз в год, обычно летом у нас такое бывает. Когда огромные деревья просто лежат на земле под порывами ветра. Как выдержит это крыльчатка анемометра. Посмотрим... посмотрим... ;)
И спасибо за описание применения "точки росы". Очень интересно.


2011-04-07 11:56:26 | Ставр
Кстати, как себя показывает в работе Zambretti, по Вашим наблюдениям? Насколько точны его предсказания на практике?


2011-04-07 10:31:39 | Ставр
Доброе утро!
Если посмотреть на точку росы с позиции дачника, то как я уже писал, опираясь на интернет-источники: "Поэтому воздух у поверхности почвы при образовании росы не будет охлаждаться ниже точки росы и вероятность наступления заморозка уменьшится. " Значит точку росы можно также использовать как дополнительную проверку угрозы заморозка.

Еще один вариант - для гурманов. Можно рассчитать примерное время выпадения росы (по скорости снижения температуры) и предупреждать дачника, что после например, 22:37 выходя в сад не забудь надеть галоши ))).

Роса также является одним из признаков того, что погода должна быть хорошая.
В августе, когда начинаются холодные росы - на томатах появляется фитофтороз. Это тоже может быть полезно для огородника.

Чем полезна точка росы для городского жителя, гламурного завсегдатая бутиков и кафе. :-))))


2011-04-06 23:26:47 | Andrey_B
Ставр, добавил у себя в скрипте расчет точки росы. Есть ли у вас мысли по поводу практического использования этого параметра?
Первое,что приходит в голову - использование расчета точки росы для определения степени комфортности восприятия окружающего воздуха. А как еще ее можно использовать?


2011-04-06 17:25:11 | Ставр
Спасибо, Андрей! Разобрался.
Кстати у WMR88 значения в базу пишутся в градусах, т.е. от 0 до 360, по-этому мне пришлось поделить на 22,5, чтоб подогнать под ваши скрипты, а в скрипте расчета среднего направления ветра наоборот - не делить на 22,5 :-)

Еще один момент. WV если скорости ветра нет, то в базу пишется NULL, однако ваш скрипт, почему-то в этом случае рисует Северный ветер (типа направление 0 и это нулевой элемент массива с названиями ветров)... До конца не понял в каком случае назначится 17-й элемент "нет данных".
у поэтому вставил в код:

if(empty ($data)) {
$wind_dir_currrent = "Ветра нет";

по крайней мере, сейчас пока станция дома валяется, и ветер кроме как феном не сделать, отображает, что ветра нет, а не "Северный".
Потом проверю, как оно будет...

Еще раз спасибо!


2011-04-05 18:16:14 | Andrey_B
Ставр, алгоритм Zambretti - это просто функция. Она у меня лежит в отдельном файле и подключается с помощью include()
Сам алгоритм и не должен делать никакие выборки. Выборки мы делаем сами, а потом передаем полученные результаты как параметры функции. Вот так.
$forecast = betel_cast($abs_pressure, date('n'), $wind_dir_text_uk[$wind_dir_avg], $pressure_trend, 1, 1050, 950, $wh_temp_out);
В частности тенденция давления у нас $pressure_trend. А как эта переменная называется внутри алгоритма нам вообще не важно.


2011-04-05 17:57:32 | Ставр
Доброго дня, Андрей!
Я прикрутил свою погодную станцию WMR88.
WeatherView прекрасно считывает с нее данные и экспортирует их в mysql.
Взял за основу ваши скрипты, написал запросы с базу и тут возник вопрос. Как скрипт вывода погоды связан со скриптом алгоритма?
В скрипте алгоритма используется переменная $z_trend, а в скрипте вывода погоды - $pressure_trend.
Я так понимаю, что в скрипте алгоритма SQL-запросы выборки ветра, z-тренда убраны?

Есть одно замечание по поводу WV - локаль должна быть EN, иначе десятичные данные передаются из станции с "запятой", а WV считает запятую разделителем чисел. Поэтому в случае русской локали получим например, не одно значение 0,15, а два 0 и 15 (это для тех, кто тоже будет использовать WMR88)


2011-04-03 22:37:03 | Andrey_B
Ставр, спасибо за ссылку, за идею и за ваш алгоритм.
Опубликовал свою версию программы в статье "Расчет заморозков"


2011-04-03 16:33:55 | gtsx4444
Ставр, похоже комментарий со всякими спецсимволами отфильтровался пройдя через форму. Все SQL запросы потерлись, думаю он был в более рабочем виде))


2011-04-03 00:40:23 | Ставр
Нюра и на сайте приусадебном у меня модуль просила. ))
И кстати о нем.
К слову о прогнозе погоды, а точнее предсказания заморозка - хочется поделиться.
Есть алгоритм предсказания заморозка он описан здесь:
www.holodilshchik.ru/index_holodilshchik_issue_5_2006_Zamorozki.htm

я для себя накидал программку на перле и ПХП.
Просьба строго не судить, ибо не программер я
[Исходный код вырезан автором сайта и опубликован в статье "Расчет заморозков"]


2011-03-26 14:16:02 | Andrey_B
Нюра, этот сайт посвящен Умному Дому своими руками. То, что вы просите, это неумный дом несвоими руками, поэтому, извините. ;)
Единственное, чем я могу помочь - сделать Он-лайн сервис на базе алгоритма. Ввели данные - получили прогноз.


2011-03-26 12:21:09 | Нюра
Не программисты мы :(((
Нам бы exe модуль готовый, а?