mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-02-01 01:09:47 +00:00
3fe10e2e59
git-svn-id: https://svn.fhem.de/fhem/trunk@25198 2b470e98-0d58-463d-a4d8-8e2adae1ed80
3268 lines
123 KiB
Perl
3268 lines
123 KiB
Perl
########################################################################################
|
||
#
|
||
# 95_Astro.pm
|
||
#
|
||
# Collection of various routines for astronomical data
|
||
# Prof. Dr. Peter A. Henning
|
||
#
|
||
# Equations from "Practical Astronomy with your Calculator" by Peter Duffett-Smith
|
||
# Program skeleton (with some errors) by Arnold Barmettler
|
||
# http://lexikon.astronomie.info/java/sunmoon/
|
||
#
|
||
# $Id$
|
||
#
|
||
########################################################################################
|
||
#
|
||
# This programm is free software; you can redistribute it and/or modify
|
||
# it under the terms of the GNU General Public License as published by
|
||
# the Free Software Foundation; either version 2 of the License, or
|
||
# (at your option) any later version.
|
||
#
|
||
# The GNU General Public License can be found at
|
||
# http://www.gnu.org/copyleft/gpl.html.
|
||
# A copy is found in the textfile GPL.txt and important notices to the license
|
||
# from the author is found in LICENSE.txt distributed with these scripts.
|
||
#
|
||
# This script is distributed in the hope that it will be useful,
|
||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
# GNU General Public License for more details.
|
||
#
|
||
########################################################################################
|
||
|
||
package FHEM::Astro;
|
||
use strict;
|
||
use warnings;
|
||
use POSIX;
|
||
use utf8;
|
||
|
||
use Encode;
|
||
use FHEM::Meta;
|
||
use GPUtils qw(GP_Import GP_Export);
|
||
use Math::Trig;
|
||
use Time::HiRes qw(gettimeofday);
|
||
use Time::Local;
|
||
use UConv;
|
||
#use Data::Dumper;
|
||
|
||
my $DEG = pi/180.0;
|
||
my $RAD = 180./pi;
|
||
|
||
my $deltaT = 65; # Correction time in s
|
||
|
||
my %Astro;
|
||
my %Date;
|
||
|
||
#-- These we may set on request
|
||
my %sets = (
|
||
"update" => "noArg",
|
||
);
|
||
|
||
#-- These we may get on request
|
||
my %gets = (
|
||
"json" => undef,
|
||
"text" => undef,
|
||
"version" => "noArg",
|
||
);
|
||
|
||
#-- These we may configure
|
||
my %attrs = (
|
||
"altitude" => undef,
|
||
"disable" => "1,0",
|
||
"horizon" => undef,
|
||
"interval" => undef,
|
||
"language" => "EN,DE,ES,FR,IT,NL,PL",
|
||
"latitude" => undef,
|
||
"lc_numeric" => "en_EN.UTF-8,de_DE.UTF-8,es_ES.UTF-8,fr_FR.UTF-8,it_IT.UTF-8,nl_NL.UTF-8,pl_PL.UTF-8",
|
||
"lc_time" => "en_EN.UTF-8,de_DE.UTF-8,es_ES.UTF-8,fr_FR.UTF-8,it_IT.UTF-8,nl_NL.UTF-8,pl_PL.UTF-8",
|
||
"longitude" => undef,
|
||
"recomputeAt" => "multiple-strict,MoonRise,MoonSet,MoonTransit,NewDay,SunRise,SunSet,SunTransit,AstroTwilightEvening,AstroTwilightMorning,CivilTwilightEvening,CivilTwilightMorning,CustomTwilightEvening,CustomTwilightMorning",
|
||
"timezone" => undef,
|
||
);
|
||
|
||
my $json;
|
||
my $tt;
|
||
|
||
#-- Export variables to other programs
|
||
our %transtable = (
|
||
EN => {
|
||
"coord" => "Coordinates",
|
||
"position" => "Position",
|
||
"longitude" => "Longitude",
|
||
"latitude" => "Latitude",
|
||
"altitude" => "Height a.s.l.",
|
||
"lonecl" => "Ecliptical longitude",
|
||
"latecl" => "Ecliptical latitude",
|
||
"ra" => "Right ascension",
|
||
"dec" => "Declination",
|
||
"az" => "Azimuth",
|
||
"alt" => "Horizontal altitude",
|
||
"age" => "Age",
|
||
"rise" => "Rise",
|
||
"set" => "Set",
|
||
"transit" => "Transit",
|
||
"distance" => "Distance",
|
||
"diameter" => "Diameter",
|
||
"toobs" => "to observer",
|
||
"toce" => "to Earth center",
|
||
"progress" => "Progress",
|
||
"twilightcivil" => "Civil twilight",
|
||
"twilightnautic" => "Nautical twilight",
|
||
"twilightastro" => "Astronomical twilight",
|
||
"twilightcustom" => "Custom twilight",
|
||
"sign" => "Zodiac sign",
|
||
"dst" => "daylight saving time",
|
||
"nt" => "standard time",
|
||
"switchtodst" => "Time change ahead to daylight saving time",
|
||
"switchtont" => "Time change back to standard time",
|
||
"hoursofsunlight" => "Hours of sunlight",
|
||
"hoursofnight" => "Hours of night",
|
||
"hoursofvisibility" => "Visibility",
|
||
|
||
#--
|
||
"time" => "Time",
|
||
"date" => "Date",
|
||
"jdate" => "Julian date",
|
||
"dayofyear" => "day of year",
|
||
"days" => "days",
|
||
"timezone" => "Time Zone",
|
||
"lmst" => "Local Sidereal Time",
|
||
|
||
#--
|
||
"season" => "Astronomical Season",
|
||
"spring" => "Spring",
|
||
"summer" => "Summer",
|
||
"fall" => "Fall",
|
||
"winter" => "Winter",
|
||
|
||
#--
|
||
"aries" => "Ram",
|
||
"taurus" => "Bull",
|
||
"gemini" => "Twins",
|
||
"cancer" => "Crab",
|
||
"leo" => "Lion",
|
||
"virgo" => "Maiden",
|
||
"libra" => "Scales",
|
||
"scorpio" => "Scorpion",
|
||
"sagittarius" => "Archer",
|
||
"capricorn" => "Goat",
|
||
"aquarius" => "Water Bearer",
|
||
"pisces" => "Fish",
|
||
|
||
#--
|
||
"sun" => "Sun",
|
||
|
||
#--
|
||
"moon" => "Moon",
|
||
"phase" => "Phase",
|
||
"newmoon" => "New Moon",
|
||
"waxingcrescent" => "Waxing Crescent",
|
||
"firstquarter" => "First Quarter",
|
||
"waxingmoon" => "Waxing Moon",
|
||
"fullmoon" => "Full Moon",
|
||
"waningmoon" => "Waning Moon",
|
||
"lastquarter" => "Last Quarter",
|
||
"waningcrescent" => "Waning Crescent",
|
||
},
|
||
|
||
DE => {
|
||
"coord" => "Koordinaten",
|
||
"position" => "Position",
|
||
"longitude" => "Länge",
|
||
"latitude" => "Breite",
|
||
"altitude" => "Höhe ü. NHN",
|
||
"lonecl" => "Eklipt. Länge",
|
||
"latecl" => "Eklipt. Breite",
|
||
"ra" => "Rektaszension",
|
||
"dec" => "Deklination",
|
||
"az" => "Azimut",
|
||
"alt" => "Horizontwinkel",
|
||
"age" => "Alter",
|
||
"phase" => "Phase",
|
||
"rise" => "Aufgang",
|
||
"set" => "Untergang",
|
||
"transit" => "Kulmination",
|
||
"distance" => "Entfernung",
|
||
"diameter" => "Durchmesser",
|
||
"toobs" => "z. Beobachter",
|
||
"toce" => "z. Erdmittelpunkt",
|
||
"progress" => "Fortschritt",
|
||
"twilightcivil" => "Bürgerliche Dämmerung",
|
||
"twilightnautic" => "Nautische Dämmerung",
|
||
"twilightastro" => "Astronomische Dämmerung",
|
||
"twilightcustom" => "Konfigurierte Dämmerung",
|
||
"sign" => "Tierkreiszeichen",
|
||
"dst" => "Sommerzeit",
|
||
"nt" => "Normalzeit",
|
||
"switchtodst" => "Umstellung vorwärts auf Sommerzeit",
|
||
"switchtont" => "Umstellung zurück auf Normalzeit",
|
||
"hoursofsunlight" => "Tagesstunden",
|
||
"hoursofnight" => "Nachtstunden",
|
||
"hoursofvisibility" => "Sichtbarkeit",
|
||
|
||
#--
|
||
"time" => "Zeit",
|
||
"date" => "Datum",
|
||
"jdate" => "Julianisches Datum",
|
||
"dayofyear" => "Tag d. Jahres",
|
||
"days" => "Tage",
|
||
"timezone" => "Zeitzone",
|
||
"lmst" => "Lokale Sternzeit",
|
||
|
||
#--
|
||
"season" => "Astronomische Jahreszeit",
|
||
"spring" => "Frühling",
|
||
"summer" => "Sommer",
|
||
"fall" => "Herbst",
|
||
"winter" => "Winter",
|
||
|
||
#--
|
||
"aries" => "Widder",
|
||
"taurus" => "Stier",
|
||
"gemini" => "Zwillinge",
|
||
"cancer" => "Krebs",
|
||
"leo" => "Löwe",
|
||
"virgo" => "Jungfrau",
|
||
"libra" => "Waage",
|
||
"scorpio" => "Skorpion",
|
||
"sagittarius" => "Schütze",
|
||
"capricorn" => "Steinbock",
|
||
"aquarius" => "Wassermann",
|
||
"pisces" => "Fische",
|
||
|
||
#--
|
||
"sun" => "Sonne",
|
||
|
||
#--
|
||
"moon" => "Mond",
|
||
"phase" => "Phase",
|
||
"newmoon" => "Neumond",
|
||
"waxingcrescent" => "Zunehmende Sichel",
|
||
"firstquarter" => "Erstes Viertel",
|
||
"waxingmoon" => "Zunehmender Mond",
|
||
"fullmoon" => "Vollmond",
|
||
"waningmoon" => "Abnehmender Mond",
|
||
"lastquarter" => "Letztes Viertel",
|
||
"waningcrescent" => "Abnehmende Sichel",
|
||
},
|
||
|
||
ES => {
|
||
"coord" => "Coordenadas",
|
||
"position" => "Posición",
|
||
"longitude" => "Longitud",
|
||
"latitude" => "Latitud",
|
||
"altitude" => "Altura sobre el mar",
|
||
"lonecl" => "Longitud eclíptica",
|
||
"latecl" => "Latitud eclíptica",
|
||
"ra" => "Ascensión recta",
|
||
"dec" => "Declinación",
|
||
"az" => "Azimut",
|
||
"alt" => "Ángulo horizonte",
|
||
"age" => "Años",
|
||
"rise" => "Salida",
|
||
"set" => "Puesta",
|
||
"transit" => "Culminación",
|
||
"distance" => "Distancia",
|
||
"diameter" => "Diámetro",
|
||
"toobs" => "al observar",
|
||
"toce" => "al centro de la tierra",
|
||
"progress" => "Progreso",
|
||
"twilightcivil" => "Crepúsculo civil",
|
||
"twilightnautic" => "Crepúsculo náutico",
|
||
"twilightastro" => "Crepúsculo astronómico",
|
||
"twilightcustom" => "Crepúsculo personalizado",
|
||
"sign" => "Signo del zodiaco",
|
||
"dst" => "horario de verano",
|
||
"nt" => "hora estándar",
|
||
"switchtodst" => "Conversión adelante a la hora de verano",
|
||
"switchtont" => "Conversión hacia atrás a la hora estándar",
|
||
"hoursofsunlight" => "Horas de luz solar",
|
||
"hoursofnight" => "Horas de la noche",
|
||
"hoursofvisibility" => "Visibilidad",
|
||
|
||
#--
|
||
"time" => "Tiempo",
|
||
"date" => "Fecha",
|
||
"jdate" => "Fecha de Julian",
|
||
"dayofyear" => "Día del año",
|
||
"days" => "Días",
|
||
"timezone" => "Zona horaria",
|
||
"lmst" => "Hora sideral local",
|
||
|
||
#--
|
||
"season" => "Temporada Astronomica",
|
||
"spring" => "Primavera",
|
||
"summer" => "Verano",
|
||
"fall" => "Otoño",
|
||
"winter" => "Invierno",
|
||
|
||
#--
|
||
"aries" => "Aries",
|
||
"taurus" => "Tauro",
|
||
"gemini" => "Geminis",
|
||
"cancer" => "Cáncer",
|
||
"leo" => "León",
|
||
"virgo" => "Virgo",
|
||
"libra" => "Libra",
|
||
"scorpio" => "Escorpión",
|
||
"sagittarius" => "Sagitario",
|
||
"capricorn" => "Capricornio",
|
||
"aquarius" => "Acuario",
|
||
"pisces" => "Piscis",
|
||
|
||
#--
|
||
"sun" => "Sol",
|
||
|
||
#--
|
||
"moon" => "Luna",
|
||
"phase" => "Fase",
|
||
"newmoon" => "Luna nueva",
|
||
"waxingcrescent" => "Luna creciente",
|
||
"firstquarter" => "Primer cuarto",
|
||
"waxingmoon" => "Luna creciente",
|
||
"fullmoon" => "Luna llena",
|
||
"waningmoon" => "Luna menguante",
|
||
"lastquarter" => "Último cuarto",
|
||
"waningcrescent" => "Creciente menguante",
|
||
},
|
||
|
||
FR => {
|
||
"coord" => "Coordonnées",
|
||
"position" => "Position",
|
||
"longitude" => "Longitude",
|
||
"latitude" => "Latitude",
|
||
"altitude" => "Hauteur au dessus de la mer",
|
||
"lonecl" => "Longitude écliptique",
|
||
"latecl" => "Latitude écliptique",
|
||
"ra" => "Ascension droite",
|
||
"dec" => "Déclinaison",
|
||
"az" => "Azimut",
|
||
"alt" => "Angle horizon",
|
||
"age" => "Âge",
|
||
"rise" => "Lever",
|
||
"set" => "Coucher",
|
||
"transit" => "Culmination",
|
||
"distance" => "Distance",
|
||
"diameter" => "Diamètre",
|
||
"toobs" => "à l'observateur",
|
||
"toce" => "au centre de la terre",
|
||
"progress" => "Progrès",
|
||
"twilightcivil" => "Crépuscule civil",
|
||
"twilightnautic" => "Crépuscule nautique",
|
||
"twilightastro" => "Crépuscule astronomique",
|
||
"twilightcustom" => "Crépuscule personnalisé",
|
||
"sign" => "Signe du zodiaque",
|
||
"dst" => "heure d'été",
|
||
"nt" => "heure normale",
|
||
"switchtodst" => "Conversion en avant à l'heure d'été",
|
||
"switchtont" => "Conversion de retour à l'heure normale",
|
||
"hoursofsunlight" => "Heures de soleil",
|
||
"hoursofnight" => "Heures de la nuit",
|
||
"hoursofvisibility" => "Visibilité",
|
||
|
||
#--
|
||
"time" => "Temps",
|
||
"date" => "Date",
|
||
"jdate" => "Date de Julien",
|
||
"dayofyear" => "jour de l'année",
|
||
"days" => "jours",
|
||
"timezone" => "Fuseau horaire",
|
||
"lmst" => "Heure sidérale locale",
|
||
|
||
#--
|
||
"season" => "Saison Astronomique",
|
||
"spring" => "Printemps",
|
||
"summer" => "Été",
|
||
"fall" => "Automne",
|
||
"winter" => "Hiver",
|
||
|
||
#--
|
||
"aries" => "bélier",
|
||
"taurus" => "Taureau",
|
||
"gemini" => "Gémeaux",
|
||
"cancer" => "Cancer",
|
||
"leo" => "Lion",
|
||
"virgo" => "Jeune fille",
|
||
"libra" => "Balance",
|
||
"scorpio" => "Scorpion",
|
||
"sagittarius" => "Sagittaire",
|
||
"capricorn" => "Capricorne",
|
||
"aquarius" => "Verseau",
|
||
"pisces" => "Poissons",
|
||
|
||
#--
|
||
"sun" => "Soleil",
|
||
|
||
#--
|
||
"moon" => "Lune",
|
||
"phase" => "Phase",
|
||
"newmoon" => "Nouvelle lune",
|
||
"waxingcrescent" => "Croissant croissant",
|
||
"firstquarter" => "Premier quart",
|
||
"waxingmoon" => "Lune croissante",
|
||
"fullmoon" => "Pleine lune",
|
||
"waningmoon" => "Lune décroissante",
|
||
"lastquarter" => "Le dernier quart",
|
||
"waningcrescent" => "Croissant décroissant",
|
||
},
|
||
|
||
IT => {
|
||
"coord" => "Coordinate",
|
||
"position" => "Posizione",
|
||
"longitude" => "Longitudine",
|
||
"latitude" => "Latitudine",
|
||
"altitude" => "Altezza sopra il mare",
|
||
"lonecl" => "Longitudine ellittica",
|
||
"latecl" => "Latitudine eclittica",
|
||
"ra" => "Giusta ascensione",
|
||
"dec" => "Declinazione",
|
||
"az" => "Azimut",
|
||
"alt" => "Angolo di orizzonte",
|
||
"age" => "Età",
|
||
"rise" => "Crescente",
|
||
"set" => "Affondamento",
|
||
"transit" => "Culmine",
|
||
"distance" => "Distanza",
|
||
"diameter" => "Diametro",
|
||
"toobs" => "verso l'osservatore",
|
||
"toce" => "verso centro della terra",
|
||
"progress" => "Progresso",
|
||
"twilightcivil" => "Crepuscolo civile",
|
||
"twilightnautic" => "Crepuscolo nautico",
|
||
"twilightastro" => "Crepuscolo astronomico",
|
||
"twilightcustom" => "Crepuscolo personalizzato",
|
||
"sign" => "Segno zodiacale",
|
||
"dst" => "ora legale",
|
||
"nt" => "ora standard",
|
||
"switchtodst" => "Conversione in avanti a ora legale",
|
||
"switchtont" => "Conversione a ritroso in ora standard",
|
||
"hoursofsunlight" => "Ore di luce solare",
|
||
"hoursofnight" => "Ore della notte",
|
||
"hoursofvisibility" => "Visibilità",
|
||
|
||
#--
|
||
"time" => "Tempo",
|
||
"date" => "Data",
|
||
"jdate" => "Data giuliana",
|
||
"dayofyear" => "giorno dell'anno",
|
||
"days" => "giorni",
|
||
"timezone" => "Fuso orario",
|
||
"lmst" => "Tempo siderale locale",
|
||
|
||
#--
|
||
"season" => "Stagione Astronomica",
|
||
"spring" => "Stagione primaverile",
|
||
"summer" => "Estate",
|
||
"fall" => "Autunno",
|
||
"winter" => "Inverno",
|
||
|
||
#--
|
||
"aries" => "Ariete",
|
||
"taurus" => "Toro",
|
||
"gemini" => "Gemelli",
|
||
"cancer" => "Cancro",
|
||
"leo" => "Leone",
|
||
"virgo" => "Vergine",
|
||
"libra" => "Libra",
|
||
"scorpio" => "Scorpione",
|
||
"sagittarius" => "Arciere",
|
||
"capricorn" => "Capricorno",
|
||
"aquarius" => "Acquario",
|
||
"pisces" => "Pesci",
|
||
|
||
#--
|
||
"sun" => "Sole",
|
||
|
||
#--
|
||
"moon" => "Luna",
|
||
"phase" => "Fase",
|
||
"newmoon" => "Nuova luna",
|
||
"waxingcrescent" => "Luna crescente",
|
||
"firstquarter" => "Primo quarto",
|
||
"waxingmoon" => "Luna crescente",
|
||
"fullmoon" => "Luna piena",
|
||
"waningmoon" => "Luna calante",
|
||
"lastquarter" => "Ultimo quarto",
|
||
"waningcrescent" => "Pericolo crescente",
|
||
},
|
||
|
||
NL => {
|
||
"coord" => "Coördinaten",
|
||
"position" => "Positie",
|
||
"longitude" => "Lengtegraad",
|
||
"latitude" => "Breedtegraad",
|
||
"altitude" => "Hoogte b. Zee",
|
||
"lonecl" => "Eclipticale Lengtegraad",
|
||
"latecl" => "Eclipticale Breedtegraad",
|
||
"ra" => "Juiste klimming",
|
||
"dec" => "Declinatie",
|
||
"az" => "Azimuth",
|
||
"alt" => "Horizon Angle",
|
||
"age" => "Leeftijd",
|
||
"rise" => "Opkomst",
|
||
"set" => "Ondergang",
|
||
"transit" => "Culminatie",
|
||
"distance" => "Afstand",
|
||
"diameter" => "Diameter",
|
||
"toobs" => "voor de Waarnemer",
|
||
"toce" => "naar het Middelpunt van de Aarde",
|
||
"progress" => "Vooruitgang",
|
||
"twilightcivil" => "Burgerlijke Schemering",
|
||
"twilightnautic" => "Nautische Schemering",
|
||
"twilightastro" => "Astronomische Schemering",
|
||
"twilightcustom" => "Aangepaste Schemering",
|
||
"sign" => "Sterrenbeeld",
|
||
"dst" => "Zomertijd",
|
||
"nt" => "Standaardtijd",
|
||
"switchtodst" => "Voorwaartse omschakeling naar de zomertijd",
|
||
"switchtont" => "Omzetting terug naar de normale tijd",
|
||
"hoursofsunlight" => "Dagen Uur",
|
||
"hoursofnight" => "Uren van de Nacht",
|
||
"hoursofvisibility" => "Zichtbaarheid",
|
||
|
||
#--
|
||
"time" => "Tijd",
|
||
"date" => "Datum",
|
||
"jdate" => "Juliaanse Datum",
|
||
"dayofyear" => "Dag van het Jaar",
|
||
"days" => "Dagen",
|
||
"timezone" => "Tijdzone",
|
||
"lmst" => "Lokale Sterrentijd",
|
||
|
||
#--
|
||
"season" => "Astronomisch Seizoen",
|
||
"spring" => "De lente",
|
||
"summer" => "Zomer",
|
||
"fall" => "Herfst",
|
||
"winter" => "Winter",
|
||
|
||
#--
|
||
"aries" => "Ram",
|
||
"taurus" => "Stier",
|
||
"gemini" => "Tweelingen",
|
||
"cancer" => "Kanker",
|
||
"leo" => "Leeuw",
|
||
"virgo" => "Maagd",
|
||
"libra" => "Weegschaal",
|
||
"scorpio" => "Schorpioen",
|
||
"sagittarius" => "Boogschutter",
|
||
"capricorn" => "Steenbok",
|
||
"aquarius" => "Waterman",
|
||
"pisces" => "Vis",
|
||
|
||
#--
|
||
"sun" => "Zon",
|
||
|
||
#--
|
||
"moon" => "Maan",
|
||
"phase" => "Fase",
|
||
"newmoon" => "Nieuwe Maan",
|
||
"waxingcrescent" => "Wassende halve Maan",
|
||
"firstquarter" => "Eerste Kwartier",
|
||
"waxingmoon" => "Wassende Maan",
|
||
"fullmoon" => "Volle Maan",
|
||
"waningmoon" => "Afnemende Maan",
|
||
"lastquarter" => "Het laatste Kwartier",
|
||
"waningcrescent" => "Afnemende halve Maan",
|
||
},
|
||
|
||
PL => {
|
||
"coord" => "Współrzędne",
|
||
"position" => "Pozycja",
|
||
"longitude" => "Długość",
|
||
"latitude" => "Szerokość",
|
||
"altitude" => "Wysokość nad morzem",
|
||
"lonecl" => "Długość ekliptyczna",
|
||
"latecl" => "Szerokość ekliptyczna",
|
||
"ra" => "Rektascencja",
|
||
"dec" => "Deklinacja",
|
||
"az" => "Azymut",
|
||
"alt" => "Kąt horyzont",
|
||
"age" => "Wiek",
|
||
"rise" => "Wschód",
|
||
"set" => "Zachód",
|
||
"transit" => "Kulminacja",
|
||
"distance" => "Dystans",
|
||
"diameter" => "Średnica",
|
||
"toobs" => "w kierunku obserwatora",
|
||
"toce" => "w kierunku środka ziemi",
|
||
"progress" => "Postęp",
|
||
"twilightcivil" => "Zmierzch cywilny",
|
||
"twilightnautic" => "Zmierzch morski",
|
||
"twilightastro" => "Zmierzch astronomiczny",
|
||
"twilightcustom" => "Zmierzch niestandardowy",
|
||
"sign" => "Znak zodiaku",
|
||
"dst" => "czas letni",
|
||
"nt" => "standardowy czas",
|
||
"switchtodst" => "Przeliczenie napastnicy na czas letni",
|
||
"switchtont" => "Przeliczanie wstecz do czasu normalnego",
|
||
"hoursofsunlight" => "Godziny światła słonecznego",
|
||
"hoursofnight" => "Godziny nocy",
|
||
"hoursofvisibility" => "Widoczność",
|
||
|
||
#--
|
||
"time" => "Czas",
|
||
"date" => "Data",
|
||
"jdate" => "Juliańska data",
|
||
"dayofyear" => "dzień roku",
|
||
"days" => "dni",
|
||
"timezone" => "Strefa czasowa",
|
||
"lmst" => "Lokalny czas gwiazdowy",
|
||
|
||
#--
|
||
"season" => "Sezon Astronomiczny",
|
||
"spring" => "Wiosna",
|
||
"summer" => "Lato",
|
||
"fall" => "Jesień",
|
||
"winter" => "Zima",
|
||
|
||
#--
|
||
"aries" => "Baran",
|
||
"taurus" => "Byk",
|
||
"gemini" => "Bliźnięta",
|
||
"cancer" => "Rak",
|
||
"leo" => "Lew",
|
||
"virgo" => "Panna",
|
||
"libra" => "Libra",
|
||
"scorpio" => "Skorpion",
|
||
"sagittarius" => "Strzelec",
|
||
"capricorn" => "Koziorożec",
|
||
"aquarius" => "Wodnik",
|
||
"pisces" => "Ryby",
|
||
|
||
#--
|
||
"sun" => "Słońce",
|
||
|
||
#--
|
||
"moon" => "Księżyc",
|
||
"phase" => "Faza",
|
||
"newmoon" => "Nów",
|
||
"waxingcrescent" => "Półksiężyc woskowy",
|
||
"firstquarter" => "Pierwszym kwartale",
|
||
"waxingmoon" => "Księżyc przybywający",
|
||
"fullmoon" => "Pełnia księżyca",
|
||
"waningmoon" => "Zmniejszający się księżyc",
|
||
"lastquarter" => "Ostatni kwartał",
|
||
"waningcrescent" => "Zwiększający się księżyc",
|
||
}
|
||
);
|
||
|
||
our @zodiac = ("aries","taurus","gemini","cancer","leo","virgo",
|
||
"libra","scorpio","sagittarius","capricorn","aquarius","pisces");
|
||
|
||
our @phases = ("newmoon","waxingcrescent", "firstquarter", "waxingmoon",
|
||
"fullmoon", "waningmoon", "lastquarter", "waningcrescent");
|
||
|
||
our %seasons = (
|
||
N => [ "winter", "spring", "summer", "fall" ],
|
||
S => [ "summer", "fall", "winter", "spring" ]
|
||
);
|
||
|
||
#-- Run before package compilation
|
||
BEGIN {
|
||
|
||
# Import from main context
|
||
GP_Import(
|
||
qw(
|
||
attr
|
||
AttrVal
|
||
data
|
||
Debug
|
||
defs
|
||
deviceEvents
|
||
FmtDateTime
|
||
FW_pO
|
||
FW_RET
|
||
FW_RETTYPE
|
||
FW_webArgs
|
||
GetType
|
||
init_done
|
||
InternalTimer
|
||
IsDisabled
|
||
Log
|
||
Log3
|
||
maxNum
|
||
minNum
|
||
modules
|
||
readingFnAttributes
|
||
readingsBeginUpdate
|
||
readingsBulkUpdateIfChanged
|
||
readingsEndUpdate
|
||
readingsSingleUpdate
|
||
RemoveInternalTimer
|
||
time_str2num
|
||
toJSON
|
||
)
|
||
);
|
||
|
||
# Export to main context
|
||
GP_Export(
|
||
qw(
|
||
Get
|
||
Initialize
|
||
)
|
||
);
|
||
|
||
# Export to main context with different name
|
||
no strict qw/refs/;
|
||
*{'main::asunrise_rel'} = *{ 'FHEM::Astro::SUNRISE_EL_sunrise_rel' };
|
||
*{'main::asunset_rel'} = *{ 'FHEM::Astro::SUNRISE_EL_sunset_rel' };
|
||
*{'main::asunrise_abs'} = *{ 'FHEM::Astro::SUNRISE_EL_sunrise_abs' };
|
||
*{'main::asunset_abs'} = *{ 'FHEM::Astro::SUNRISE_EL_sunset_abs' };
|
||
*{'main::asunrise'} = *{ 'FHEM::Astro::SUNRISE_EL_sunrise' };
|
||
*{'main::asunset'} = *{ 'FHEM::Astro::SUNRISE_EL_sunset' };
|
||
*{'main::aisday'} = *{ 'FHEM::Astro::SUNRISE_EL_isday' };
|
||
*{'main::asunrise_abs_dat'} = *{ 'FHEM::Astro::SUNRISE_EL_sunrise_abs_dat' };
|
||
*{'main::asunset_abs_dat'} = *{ 'FHEM::Astro::SUNRISE_EL_sunset_abs_dat' };
|
||
use strict qw/refs/;
|
||
}
|
||
|
||
_LoadOptionalPackages();
|
||
|
||
sub SunRise($$$$$$$$);
|
||
sub MoonRise($$$$$$$);
|
||
sub SetTime(;$$$);
|
||
sub Compute($;$);
|
||
|
||
########################################################################################################
|
||
#
|
||
# Initialize
|
||
#
|
||
# Parameter hash = hash of device addressed
|
||
#
|
||
########################################################################################################
|
||
|
||
sub Initialize ($) {
|
||
my ($hash) = @_;
|
||
|
||
$hash->{DefFn} = "FHEM::Astro::Define";
|
||
$hash->{SetFn} = "FHEM::Astro::Set";
|
||
$hash->{GetFn} = "FHEM::Astro::Get";
|
||
$hash->{UndefFn} = "FHEM::Astro::Undef";
|
||
$hash->{AttrFn} = "FHEM::Astro::Attr";
|
||
$hash->{NotifyFn} = "FHEM::Astro::Notify";
|
||
$hash->{AttrList} = join (" ", map {
|
||
defined($attrs{$_}) ? "$_:$attrs{$_}" : $_
|
||
} sort keys %attrs
|
||
)
|
||
." "
|
||
.$readingFnAttributes;
|
||
|
||
$hash->{parseParams} = 1;
|
||
$hash->{NotifyOrderPrefix} = '45-'; # we are a data provider
|
||
|
||
$data{FWEXT}{"/Astro_moonwidget"}{FUNC} = "FHEM::Astro::Moonwidget";
|
||
$data{FWEXT}{"/Astro_moonwidget"}{FORKABLE} = 0;
|
||
|
||
return FHEM::Meta::InitMod( __FILE__, $hash );
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# Define - Implements DefFn function
|
||
#
|
||
# Parameter hash = hash of device addressed, a = array of arguments, h = hash of parameters
|
||
#
|
||
########################################################################################################
|
||
|
||
sub Define ($@) {
|
||
my ($hash,$a,$h) = @_;
|
||
my $name = shift @$a;
|
||
my $type = shift @$a;
|
||
my $global = shift @$a;
|
||
|
||
return $@ unless ( FHEM::Meta::SetInternals($hash) );
|
||
use version 0.77; our $VERSION = FHEM::Meta::Get( $hash, 'version' );
|
||
return 'Astro may not be run on Windows systems' if $^O eq 'MSWin32';
|
||
|
||
if ($global) {
|
||
return "$type global device $modules{$type}{global}{NAME} is already defined"
|
||
if ( defined( $modules{$type}{global} )
|
||
&& $modules{$type}{global}{NAME} ne $name );
|
||
$modules{$type}{global} = $hash;
|
||
$hash->{SCOPE} = 'global';
|
||
}
|
||
|
||
$hash->{NOTIFYDEV} = "global";
|
||
$hash->{INTERVAL} = 3600;
|
||
readingsSingleUpdate( $hash, "state", "Initialized", $init_done );
|
||
|
||
$modules{Astro}{defptr}{$name} = $hash;
|
||
|
||
# for the very first definition, set some default attributes
|
||
if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
|
||
$attr{$name}{icon} = 'telescope';
|
||
$attr{$name}{recomputeAt} = 'NewDay,SunRise,SunSet,AstroTwilightEvening,AstroTwilightMorning,CivilTwilightEvening,CivilTwilightMorning,CustomTwilightEvening,CustomTwilightMorning';
|
||
$hash->{RECOMPUTEAT} = $attr{$name}{recomputeAt};
|
||
}
|
||
|
||
return undef;
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# Undef - Implements Undef function
|
||
#
|
||
# Parameter hash = hash of device addressed, arg = array of arguments
|
||
#
|
||
########################################################################################################
|
||
|
||
sub Undef ($$) {
|
||
my ($hash,$arg) = @_;
|
||
my $name = $hash->{NAME};
|
||
my $type = $hash->{TYPE};
|
||
|
||
RemoveInternalTimer($hash);
|
||
|
||
delete $modules{$type}{defptr}{$name};
|
||
|
||
if ( defined( $modules{$type}{global} )
|
||
&& $modules{$type}{global}{NAME} eq $name )
|
||
{
|
||
delete $modules{$type}{global};
|
||
Debug "Yes";
|
||
}
|
||
|
||
return undef;
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# Notify - Implements Notify function
|
||
#
|
||
# Parameter hash = hash of device addressed, dev = hash of device that triggered notification
|
||
#
|
||
########################################################################################################
|
||
|
||
sub Notify ($$) {
|
||
my ($hash,$dev) = @_;
|
||
my $name = $hash->{NAME};
|
||
my $TYPE = $hash->{TYPE};
|
||
my $devName = $dev->{NAME};
|
||
my $devType = GetType($devName);
|
||
|
||
return "" if ( IsDisabled($name) );
|
||
|
||
if ( $devName eq "global" ) {
|
||
my $events = deviceEvents( $dev, 1 );
|
||
return "" unless ($events);
|
||
|
||
foreach my $event ( @{$events} ) {
|
||
next unless ( defined($event) );
|
||
next if ( $event =~ m/^[A-Za-z\d_-]+:/ );
|
||
|
||
if ( $event =~ m/^INITIALIZED|REREADCFG$/ ) {
|
||
if ( ( defined( $hash->{INTERVAL} ) && $hash->{INTERVAL} > 0 )
|
||
|| defined( $hash->{RECOMPUTEAT} ) )
|
||
{
|
||
RemoveInternalTimer($hash);
|
||
InternalTimer(gettimeofday()+5,"FHEM::Astro::Update",$hash,0);
|
||
}
|
||
}
|
||
elsif ( $event =~
|
||
m/^(DEFINED|MODIFIED)\s+([A-Za-z\d_-]+)$/ &&
|
||
$2 eq $name )
|
||
{
|
||
if ( ( defined( $hash->{INTERVAL} ) && $hash->{INTERVAL} > 0 )
|
||
|| defined( $hash->{RECOMPUTEAT} ) )
|
||
{
|
||
RemoveInternalTimer($hash);
|
||
InternalTimer(gettimeofday()+1,"FHEM::Astro::Update",$hash,0);
|
||
}
|
||
}
|
||
|
||
# only process attribute events
|
||
next
|
||
unless ( $event =~
|
||
m/^((?:DELETE)?ATTR)\s+([A-Za-z\d._]+)\s+([A-Za-z\d_\.\-\/]+)(?:\s+(.*)\s*)?$/
|
||
);
|
||
|
||
my $cmd = $1;
|
||
my $d = $2;
|
||
my $attr = $3;
|
||
my $val = $4;
|
||
my $type = GetType($d);
|
||
|
||
# filter attributes to be processed
|
||
next
|
||
unless ( $attr eq "altitude"
|
||
|| $attr eq "language"
|
||
|| $attr eq "latitude"
|
||
|| $attr eq "lc_numeric"
|
||
|| $attr eq "lc_time"
|
||
|| $attr eq "longitude"
|
||
|| $attr eq "timezone" );
|
||
|
||
# when global attributes were changed
|
||
if ( $d eq "global" ) {
|
||
RemoveInternalTimer($hash);
|
||
InternalTimer( gettimeofday() + 1,
|
||
"FHEM::Astro::Update", $hash, 0 );
|
||
}
|
||
}
|
||
}
|
||
|
||
return undef;
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# Attr - Implements Attr function
|
||
#
|
||
# Parameter do = action, name = name of device, key = attribute name, value = attribute value
|
||
#
|
||
########################################################################################################
|
||
|
||
sub Attr(@) {
|
||
my ($do,$name,$key,$value) = @_;
|
||
|
||
my $hash = $defs{$name};
|
||
my $ret;
|
||
|
||
if ( $do eq "set") {
|
||
ARGUMENT_HANDLER: {
|
||
#-- altitude modified at runtime
|
||
$key eq "altitude" and do {
|
||
#-- check value
|
||
return "[Astro] $do $name attribute $key must be a float number >= 0 meters"
|
||
unless($value =~ m/^(\d+(?:\.\d+)?)$/ && $1 >= 0.);
|
||
};
|
||
#-- disable modified at runtime
|
||
$key eq "disable" and do {
|
||
#-- check value
|
||
return "[Astro] $do $name attribute $key can only be 1 or 0"
|
||
unless($value =~ m/^(1|0)$/);
|
||
readingsSingleUpdate($hash,"state",$value?"inactive":"Initialized",$init_done);
|
||
};
|
||
#-- horizon modified at runtime
|
||
$key eq "horizon" and do {
|
||
#-- check value
|
||
return "[Astro] $do $name attribute $key must be a float number >= -45 and <= 45 degrees"
|
||
unless($value =~ m/^(-?\d+(?:\.\d+)?)(?::(-?\d+(?:\.\d+)?))?$/ && $1 >= -45. && $1 <= 45. && (!$2 || $2 >= -45. && $2 <= 45.));
|
||
};
|
||
#-- interval modified at runtime
|
||
$key eq "interval" and do {
|
||
#-- check value
|
||
return "[Astro] $do $name attribute $key must be >= 0 seconds"
|
||
unless($value =~ m/^\d+$/);
|
||
#-- update timer
|
||
$hash->{INTERVAL} = $value;
|
||
};
|
||
#-- latitude modified at runtime
|
||
$key eq "latitude" and do {
|
||
#-- check value
|
||
return "[Astro] $do $name attribute $key must be float number >= -90 and <= 90 degrees"
|
||
unless($value =~ m/^(-?\d+(?:\.\d+)?)$/ && $1 >= -90. && $1 <= 90.);
|
||
};
|
||
#-- longitude modified at runtime
|
||
$key eq "longitude" and do {
|
||
#-- check value
|
||
return "[Astro] $do $name attribute $key must be float number >= -180 and <= 180 degrees"
|
||
unless($value =~ m/^(-?\d+(?:\.\d+)?)$/ && $1 >= -180. && $1 <= 180.);
|
||
};
|
||
#-- recomputeAt modified at runtime
|
||
$key eq "recomputeAt" and do {
|
||
my @skel = split(',', $attrs{recomputeAt});
|
||
shift @skel;
|
||
#-- check value 1/2
|
||
return "[Astro] $do $name attribute $key must be one or many of ".join(',', @skel)
|
||
if(!$value || $value eq "");
|
||
#-- check value 2/2
|
||
my @vals = split(',', $value);
|
||
foreach my $val (@vals) {
|
||
return "[Astro] $do $name attribute value $val is invalid, must be one or many of ".join(',', @skel)
|
||
unless(grep( m/^$val$/, @skel ));
|
||
}
|
||
$hash->{RECOMPUTEAT} = join(',', @vals);
|
||
};
|
||
}
|
||
}
|
||
|
||
elsif ( $do eq "del") {
|
||
readingsSingleUpdate($hash,"state","Initialized",$init_done)
|
||
if ($key eq "disable");
|
||
$hash->{INTERVAL} = 3600
|
||
if ($key eq "interval");
|
||
delete $hash->{RECOMPUTEAT}
|
||
if ($key eq "recomputeAt");
|
||
}
|
||
|
||
if ( $init_done
|
||
&& exists( $attrs{$key} )
|
||
&& ( $hash->{INTERVAL} > 0 || $hash->{RECOMPUTEAT} || $hash->{NEXTUPDATE} )
|
||
)
|
||
{
|
||
RemoveInternalTimer($hash);
|
||
InternalTimer( gettimeofday() + 2, "FHEM::Astro::Update", $hash, 0 );
|
||
}
|
||
|
||
return $ret;
|
||
}
|
||
|
||
sub _mod($$) { my ($a,$b)=@_;if( $a =~ /\d*\.\d*/){return($a-floor($a/$b)*$b)}else{return undef}; }
|
||
sub _mod2Pi($) { my ($x)=@_;$x = _mod($x, 2.*pi);return($x); }
|
||
sub _round($$) { my ($x,$n)=@_; return int(10**$n*$x+0.5)/10**$n};
|
||
|
||
sub _tzoffset($) {
|
||
my ($t) = @_;
|
||
my $utc = mktime(gmtime($t));
|
||
#-- the following does not properly calculate dst
|
||
my $local = mktime(localtime($t));
|
||
#-- this is the correction
|
||
my $isdst = (localtime($t))[8];
|
||
#-- correction
|
||
if($isdst == 1){
|
||
$local+=3600;
|
||
}
|
||
return (($local - $utc)/36);
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# _LoadOptionalPackages - Load Perl packages that may not be installed
|
||
#
|
||
########################################################################################################
|
||
|
||
sub _LoadOptionalPackages {
|
||
|
||
# JSON preference order
|
||
local $ENV{PERL_JSON_BACKEND} =
|
||
'Cpanel::JSON::XS,JSON::XS,JSON::PP,JSON::backportPP'
|
||
unless ( defined( $ENV{PERL_JSON_BACKEND} ) );
|
||
|
||
# try to use JSON::MaybeXS wrapper
|
||
# for chance of better performance + open code
|
||
eval {
|
||
require JSON::MaybeXS;
|
||
$json = JSON::MaybeXS->new;
|
||
1;
|
||
};
|
||
if ($@) {
|
||
$@ = undef;
|
||
|
||
# try to use JSON wrapper
|
||
# for chance of better performance
|
||
eval {
|
||
require JSON;
|
||
$json = JSON->new;
|
||
1;
|
||
};
|
||
|
||
if ($@) {
|
||
$@ = undef;
|
||
|
||
# In rare cases, Cpanel::JSON::XS may
|
||
# be installed but JSON|JSON::MaybeXS not ...
|
||
eval {
|
||
require Cpanel::JSON::XS;
|
||
$json = Cpanel::JSON::XS->new;
|
||
1;
|
||
};
|
||
|
||
if ($@) {
|
||
$@ = undef;
|
||
|
||
# In rare cases, JSON::XS may
|
||
# be installed but JSON not ...
|
||
eval {
|
||
require JSON::XS;
|
||
$json = JSON::XS->new;
|
||
1;
|
||
};
|
||
|
||
if ($@) {
|
||
$@ = undef;
|
||
|
||
# Fallback to built-in JSON which SHOULD
|
||
# be available since 5.014 ...
|
||
eval {
|
||
require JSON::PP;
|
||
$json = JSON::PP->new;
|
||
1;
|
||
};
|
||
|
||
if ($@) {
|
||
$@ = undef;
|
||
|
||
# Last chance may be a backport
|
||
eval {
|
||
require JSON::backportPP;
|
||
$json = JSON::backportPP->new;
|
||
1;
|
||
};
|
||
$@ = undef if ($@);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if ($@) {
|
||
$@ = undef;
|
||
} else {
|
||
$json->allow_nonref;
|
||
$json->shrink;
|
||
$json->utf8;
|
||
}
|
||
|
||
# only available for Perl versions after 2018-06-09
|
||
eval {
|
||
import Time::Local 'timelocal_modern';
|
||
1;
|
||
};
|
||
eval {
|
||
import Time::Local 'timegm_modern';
|
||
1;
|
||
};
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# subroutines for 99_SUNRISE_EL.pm compatibility layer
|
||
#
|
||
########################################################################################################
|
||
|
||
sub SUNRISE_EL_sr($$$$$$) {
|
||
my ( $rise, $seconds, $isrel, $daycheck, $min, $max ) = @_;
|
||
SUNRISE_EL_sr_alt(
|
||
time(), $rise, $isrel, $daycheck,
|
||
1, $main::defaultaltit, $seconds, $min,
|
||
$max
|
||
);
|
||
}
|
||
|
||
sub SUNRISE_EL_sr_alt($$$$$$$$$) {
|
||
my $nt=shift;
|
||
my $rise=shift;
|
||
my $isrel=shift;
|
||
my $daycheck=shift;
|
||
my $nextDay=shift;
|
||
my $altit = defined($_[0]) ? $_[0] : "";
|
||
my $hasalt = 0;
|
||
if(exists $main::alti{uc($altit)}) {
|
||
$hasalt = 1;
|
||
$altit=$main::alti{uc($altit)};
|
||
shift;
|
||
} elsif($altit =~ /HORIZON=(([\-\+]*[0-9\.]+)(?::([\-\+]*[0-9\.]+))?)/i) {
|
||
$hasalt = 1;
|
||
$altit=$1;
|
||
shift;
|
||
} else {
|
||
$altit=-6; #default
|
||
}
|
||
my($seconds, $min, $max)=@_;
|
||
my $needrise = ($rise || $daycheck) ? 1 : 0;
|
||
my $needset = (!$rise || $daycheck) ? 1 : 0;
|
||
$seconds = 0 if(!$seconds);
|
||
|
||
my $hash = defined( $modules{Astro}{global} ) ? $modules{Astro}{global} : ();
|
||
my $name = defined( $hash->{NAME} ) ? $hash->{NAME} : '';
|
||
|
||
############################
|
||
# If set in global, use longitude/latitude
|
||
# from global, otherwise set Frankfurt/Germany as
|
||
# default
|
||
my $long = AttrVal( $name, "longitude", AttrVal( "global", "longitude", 10.0 ) );
|
||
my $lat = AttrVal( $name, "latitude", AttrVal( "global", "latitude", 50.0 ) );
|
||
$altit = AttrVal( $name, "horizon", AttrVal( "global", "horizon", -6. ) ) unless($hasalt);
|
||
Log3 $name ne '' ? $name : undef, $name ne '' ? 4 : 5,
|
||
"[FHEM::Astro::SUNRISE_EL] "
|
||
. ( $name ne '' ? "$name: " : '' )
|
||
. "Compute sunrise/sunset for latitude $lat , longitude $long , horizon $altit at "
|
||
. FmtDateTime($nt);
|
||
|
||
#-- readjust timezone
|
||
my $tz =
|
||
AttrVal( $name, "timezone", AttrVal( "global", "timezone", undef ) );
|
||
local $ENV{TZ} = $tz if ($tz);
|
||
eval { tzset() if ( exists &{'tzset'} ) };
|
||
|
||
#my $nt = time;
|
||
my @lt = localtime($nt);
|
||
my $gmtoff = main::_calctz($nt,@lt); # in hour
|
||
|
||
my ($rt,$st) = _SUNRISE_EL_sr_alt($lat,$long,$altit,$needrise,$needset,$nt,$gmtoff);
|
||
my $sst = ($rise ? $rt : $st) + ($seconds/3600);
|
||
|
||
my $nh = $lt[2] + $lt[1]/60 + $lt[0]/3600; # Current hour since midnight
|
||
if($daycheck) {
|
||
if(defined($min) && defined($max)) { #Forum #43742
|
||
$min = main::hms2h($min); $max = main::hms2h($max);
|
||
if($min < $max) {
|
||
$rt = $min if($rt < $min);
|
||
$st = $max if($st > $max);
|
||
} else {
|
||
$rt = $max if($rt > $max);
|
||
$st = $min if($st < $min);
|
||
}
|
||
}
|
||
return 1 if($rt <= $nh && $nh <= $st);
|
||
return 0;
|
||
}
|
||
|
||
$sst = main::hms2h($min) if(defined($min) && (main::hms2h($min) > $sst));
|
||
$sst = main::hms2h($max) if(defined($max) && (main::hms2h($max) < $sst));
|
||
|
||
my $diff = 0;
|
||
if (($data{AT_RECOMPUTE} || # compute it for tommorow
|
||
int(($nh-$sst)*3600) >= 0) && $nextDay) { # if called a subsec earlier
|
||
$nt += 86400;
|
||
@lt = localtime($nt);
|
||
my $ngmtoff = main::_calctz($nt,@lt); # in hour
|
||
$diff = 24;
|
||
|
||
($rt,$st) = _SUNRISE_EL_sr_alt($lat,$long,$altit,$needrise,$needset,$nt,$ngmtoff);
|
||
$sst = ($rise ? $rt : $st) + ($seconds/3600);
|
||
|
||
$sst = main::hms2h($min) if(defined($min) && (main::hms2h($min) > $sst));
|
||
$sst = main::hms2h($max) if(defined($max) && (main::hms2h($max) < $sst));
|
||
}
|
||
|
||
$sst += $diff if($isrel);
|
||
$sst -= $nh if($isrel == 1);
|
||
|
||
delete local $ENV{TZ};
|
||
tzset() if ( exists &{'tzset'} );
|
||
|
||
return main::h2hms_fmt($sst);
|
||
}
|
||
|
||
sub _SUNRISE_EL_sr_alt($$$$$$$) {
|
||
my ( $lat, $long, $altit, $needrise, $needset, $nt, $offset ) = @_;
|
||
my $hash =
|
||
defined( $modules{Astro}{global} ) ? $modules{Astro}{global} : ();
|
||
my $name = defined( $hash->{NAME} ) ? $hash->{NAME} : '';
|
||
|
||
my $horM = 0.0;
|
||
my $horE = 0.0;
|
||
if ( $altit =~ m/^([^:]+)(?::(.+))?$/ ) {
|
||
$horM = $1;
|
||
$horE = defined($2) ? $2 : $1;
|
||
}
|
||
|
||
my $tz =
|
||
AttrVal( $name, "timezone", AttrVal( "global", "timezone", undef ) );
|
||
SetTime( $nt, $tz );
|
||
my $JD0 = Date2JD( $Date{day}, $Date{month}, $Date{year} );
|
||
|
||
my (
|
||
$suntransit, $sunrise,
|
||
$sunset, $CivilTwilightMorning,
|
||
$CivilTwilightEvening, $NauticTwilightMorning,
|
||
$NauticTwilightEvening, $AstroTwilightMorning,
|
||
$AstroTwilightEvening, $CustomTwilightMorning,
|
||
$CustomTwilightEvening
|
||
)
|
||
= SunRise(
|
||
$JD0, $deltaT,
|
||
$long * $DEG,
|
||
$lat * $DEG,
|
||
$Date{zonedelta}, $horM, $horE, 0
|
||
);
|
||
|
||
return ( $needrise ? $CustomTwilightMorning : undef ),
|
||
( $needset ? $CustomTwilightEvening : undef );
|
||
}
|
||
|
||
sub SUNRISE_EL_sunrise_rel(@) { return SUNRISE_EL_sr_alt(time(),1,1,0,1,shift,shift,shift,shift); }
|
||
sub SUNRISE_EL_sunset_rel (@) { return SUNRISE_EL_sr_alt(time(),0,1,0,1,shift,shift,shift,shift); }
|
||
sub SUNRISE_EL_sunrise_abs(@) { return SUNRISE_EL_sr_alt(time(),1,0,0,0,shift,shift,shift,shift); }
|
||
sub SUNRISE_EL_sunset_abs (@) { return SUNRISE_EL_sr_alt(time(),0,0,0,0,shift,shift,shift,shift); }
|
||
sub SUNRISE_EL_sunrise (@) { return SUNRISE_EL_sr_alt(time(),1,2,0,1,shift,shift,shift,shift); }
|
||
sub SUNRISE_EL_sunset (@) { return SUNRISE_EL_sr_alt(time(),0,2,0,1,shift,shift,shift,shift); }
|
||
sub SUNRISE_EL_isday (@) { return SUNRISE_EL_sr_alt(time(),1,0,1,1,shift,shift,shift,shift); }
|
||
|
||
sub SUNRISE_EL_sunrise_abs_dat(@) {
|
||
return SUNRISE_EL_sr_alt(main::sr_noon(shift),1,0,0,0,shift,shift,shift,shift);
|
||
}
|
||
sub SUNRISE_EL_sunset_abs_dat (@) {
|
||
return SUNRISE_EL_sr_alt(main::sr_noon(shift),0,0,0,0,shift,shift,shift,shift);
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# wrapper for Perl versions before 2018-06-09
|
||
#
|
||
########################################################################################################
|
||
|
||
sub _timelocal_modern {
|
||
return timelocal_modern(@_)
|
||
if ( exists &{'timelocal_modern'} );
|
||
|
||
my @r = @_;
|
||
$r[5] -= 1900;
|
||
return timelocal(@r);
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# time fragments into minutes, seconds
|
||
#
|
||
########################################################################################################
|
||
|
||
sub HHMM($){
|
||
my ($hh) = @_;
|
||
return("---")
|
||
if (!defined($hh) || $hh !~ /^-?\d+/ || $hh==0) ;
|
||
|
||
my $h = floor($hh);
|
||
my $m = ($hh-$h)*60.;
|
||
return sprintf("%02d:%02d",$h,$m);
|
||
}
|
||
|
||
sub HHMMSS($){
|
||
my ($hh) = @_;
|
||
return("---")
|
||
if (!defined($hh) || $hh !~ /^-?\d+/ || $hh==0) ;
|
||
|
||
my $m = ($hh-floor($hh))*60.;
|
||
my $s = ($m-floor($m))*60;
|
||
my $h = floor($hh);
|
||
return sprintf("%02d:%02d:%02d",$h,$m,$s);
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# Date2JD - Calculate Julian date, adopted from Perl CPAN module Astro::Coord::ECI::Utils
|
||
#
|
||
########################################################################################################
|
||
|
||
sub Date2JD {
|
||
my @args = @_;
|
||
unshift @args, 0 while @args < 6.;
|
||
my ( $sec, $min, $hr, $day, $mon, $yr ) = @args;
|
||
return undef if ( ( $yr < -4712. ) or ( $mon < 1. || $mon > 12. ) );
|
||
if ( $mon < 3. ) {
|
||
--$yr;
|
||
$mon += 12.;
|
||
}
|
||
|
||
my $JD_GREGORIAN = 2299160.5;
|
||
my $A = floor( $yr / 100 );
|
||
my $B = 2 - $A + floor( $A / 4 );
|
||
my $jd =
|
||
floor( 365.25 * ( $yr + 4716 ) ) +
|
||
floor( 30.6001 * ( $mon + 1 ) ) +
|
||
$day + $B - 1524.5 +
|
||
( ( ( $sec || 0 ) / 60 + ( $min || 0 ) ) / 60 + ( $hr || 0 ) ) / 24;
|
||
$jd < $JD_GREGORIAN
|
||
and $jd =
|
||
floor( 365.25 * ( $yr + 4716 ) ) +
|
||
floor( 30.6001 * ( $mon + 1 ) ) +
|
||
$day - 1524.5 +
|
||
( ( ( $sec || 0 ) / 60 + ( $min || 0 ) ) / 60 + ( $hr || 0 ) ) / 24;
|
||
return $jd;
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# GMST - Julian Date to Greenwich Mean Sidereal Time
|
||
#
|
||
########################################################################################################
|
||
|
||
sub GMST($){
|
||
my ($JD) = @_;
|
||
my $UT = ($JD-0.5) - int($JD-0.5);
|
||
$UT = $UT*24.; # UT in hours
|
||
$JD = floor($JD-0.5)+0.5; # JD at 0 hours UT
|
||
my $T = ($JD-2451545.0)/36525.0;
|
||
my $T0 = 6.697374558 + $T*(2400.051336 + $T*0.000025862);
|
||
|
||
return( _mod($T0+$UT*1.002737909,24.));
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# GMST2UT - Convert Greenweek mean sidereal time to UT
|
||
#
|
||
########################################################################################################
|
||
|
||
sub GMST2UT($$){
|
||
my ($JD, $gmst) = @_;
|
||
$JD = floor($JD-0.5)+0.5; # JD at 0 hours UT
|
||
my $T = ($JD-2451545.0)/36525.0;
|
||
my $T0 = _mod(6.697374558 + $T*(2400.051336 + $T*0.000025862), 24.);
|
||
my $UT = 0.9972695663*(($gmst-$T0));
|
||
return($UT);
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# GMST2LMST - Local Mean Sidereal Time, geographical longitude in radians,
|
||
# East is positive
|
||
#
|
||
########################################################################################################
|
||
|
||
sub GMST2LMST($$){
|
||
my ($gmst, $lon) = @_;
|
||
my $lmst = _mod($gmst+$RAD*$lon/15, 24.);
|
||
return( $lmst );
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# Ecl2Equ - Transform ecliptical coordinates (lon/lat) to equatorial coordinates (RA/dec)
|
||
#
|
||
########################################################################################################
|
||
|
||
sub Ecl2Equ($$$){
|
||
my ($lon, $lat, $TDT) = @_;
|
||
my $T = ($TDT-2451545.0)/36525.; # Epoch 2000 January 1.5
|
||
my $eps = (23.+(26+21.45/60.)/60. + $T*(-46.815 +$T*(-0.0006 + $T*0.00181) )/3600. )*$DEG;
|
||
my $coseps = cos($eps);
|
||
my $sineps = sin($eps);
|
||
my $sinlon = sin($lon);
|
||
my $ra = _mod2Pi(atan2( ($sinlon*$coseps-tan($lat)*$sineps), cos($lon) ));
|
||
my $dec = asin( sin($lat)*$coseps + cos($lat)*$sineps*$sinlon );
|
||
|
||
return ($ra,$dec);
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# Equ2Altaz - Transform equatorial coordinates (RA/Dec) to horizontal coordinates
|
||
# (azimuth/altitude). Refraction is ignored
|
||
#
|
||
########################################################################################################
|
||
|
||
sub Equ2Altaz($$$$$){
|
||
my ($ra, $dec, $TDT, $lat, $lmst)=@_;
|
||
my $cosdec = cos($dec);
|
||
my $sindec = sin($dec);
|
||
my $lha = $lmst - $ra;
|
||
my $coslha = cos($lha);
|
||
my $sinlha = sin($lha);
|
||
my $coslat = cos($lat);
|
||
my $sinlat = sin($lat);
|
||
|
||
my $N = -$cosdec * $sinlha;
|
||
my $D = $sindec * $coslat - $cosdec * $coslha * $sinlat;
|
||
my $az = _mod2Pi( atan2($N, $D) );
|
||
my $alt = asin( $sindec * $sinlat + $cosdec * $coslha * $coslat );
|
||
|
||
return ($az,$alt);
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# GeoEqu2TopoEqu - Transform geocentric equatorial coordinates (RA/Dec) to
|
||
# topocentric equatorial coordinates
|
||
#
|
||
########################################################################################################
|
||
|
||
sub GeoEqu2TopoEqu($$$$$$$){
|
||
my ($ra, $dec, $distance, $lon, $lat, $radius, $lmst) = @_;
|
||
|
||
my $cosdec = cos($dec);
|
||
my $sindec = sin($dec);
|
||
my $coslst = cos($lmst);
|
||
my $sinlst = sin($lmst);
|
||
my $coslat = cos($lat); # we should use geocentric latitude, not geodetic latitude
|
||
my $sinlat = sin($lat);
|
||
my $rho = $radius; # observer-geocenter in km
|
||
|
||
my $x = $distance*$cosdec*cos($ra) - $rho*$coslat*$coslst;
|
||
my $y = $distance*$cosdec*sin($ra) - $rho*$coslat*$sinlst;
|
||
my $z = $distance*$sindec - $rho*$sinlat;
|
||
|
||
my $distanceTopocentric = sqrt($x*$x + $y*$y + $z*$z);
|
||
my $decTopocentric = asin($z/$distanceTopocentric);
|
||
my $raTopocentric = _mod2Pi( atan2($y, $x) );
|
||
|
||
return ( ($distanceTopocentric,$decTopocentric,$raTopocentric) );
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# EquPolar2Cart - Calculate cartesian from polar coordinates
|
||
#
|
||
########################################################################################################
|
||
|
||
sub EquPolar2Cart($$$){
|
||
my ($lon,$lat,$distance) = @_;
|
||
my $rcd = cos($lat)*$distance;
|
||
my $x = $rcd*cos($lon);
|
||
my $y = $rcd*sin($lon);
|
||
my $z = sin($lat)*$distance;
|
||
return( ($x,$y,$z) );
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# Observer2EquCart - Calculate observers cartesian equatorial coordinates (x,y,z in celestial frame)
|
||
# from geodetic coordinates (longitude, latitude, height above WGS84 ellipsoid)
|
||
# Currently only used to calculate distance of a body from the observer
|
||
#
|
||
########################################################################################################
|
||
|
||
sub Observer2EquCart($$$$){
|
||
my ($lon, $lat, $height, $gmst ) = @_;
|
||
|
||
my $flat = 298.257223563; # WGS84 flatening of earth
|
||
my $aearth = 6378.137; # GRS80/WGS84 semi major axis of earth ellipsoid
|
||
|
||
#-- Calculate geocentric latitude from geodetic latitude
|
||
my $co = cos ($lat);
|
||
my $si = sin ($lat);
|
||
$si = $si * $si;
|
||
my $fl = 1.0 - 1.0 / $flat;
|
||
$fl = $fl * $fl;
|
||
my $u = 1.0 / sqrt ($co * $co + $fl * $si);
|
||
my $a = $aearth * $u + $height;
|
||
my $b = $aearth * $fl * $u + $height;
|
||
my $radius = sqrt ($a * $a * $co *$co + $b *$b * $si); # geocentric distance from earth center
|
||
my $y = acos ($a * $co / $radius); # geocentric latitude, rad
|
||
my $x = $lon; # longitude stays the same
|
||
my $z;
|
||
if ($lat < 0.0) { $y = -$y; } # adjust sign
|
||
|
||
#-- convert from geocentric polar to geocentric cartesian, with regard to Greenwich
|
||
($x,$y,$z) = EquPolar2Cart( $x, $y, $radius );
|
||
|
||
#-- rotate around earth's polar axis to align coordinate system from Greenwich to vernal equinox
|
||
my $rotangle = $gmst/24*2*pi; # sideral time gmst given in hours. Convert to radians
|
||
my $x2 = $x*cos($rotangle) - $y*sin($rotangle);
|
||
my $y2 = $x*sin($rotangle) + $y*cos($rotangle);
|
||
|
||
return( ($x2,$y2,$z,$radius) );
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# SunPosition - Calculate coordinates for Sun
|
||
# Coordinates are accurate to about 10s (right ascension)
|
||
# and a few minutes of arc (declination)
|
||
#
|
||
########################################################################################################
|
||
|
||
sub SunPosition($$$){
|
||
my ($TDT, $observerlat, $lmst)=@_;
|
||
|
||
my $D = $TDT-2447891.5;
|
||
my $eg = 279.403303*$DEG;
|
||
my $wg = 282.768422*$DEG;
|
||
my $e = 0.016713;
|
||
my $a = 149598500; # km
|
||
#-- mean angular diameter of sun
|
||
my $diameter0 = 0.533128*$DEG;
|
||
|
||
my $MSun = 360*$DEG/365.242191*$D+$eg-$wg;
|
||
my $nu = $MSun + 360.*$DEG/pi*$e*sin($MSun);
|
||
|
||
my %sunCoor;
|
||
|
||
$sunCoor{lon} = _mod2Pi($nu+$wg);
|
||
$sunCoor{lat} = 0;
|
||
$sunCoor{anomalyMean} = $MSun;
|
||
|
||
my $distance = (1-$e*$e)/(1+$e*cos($nu)); # distance in astronomical units
|
||
$sunCoor{diameter} = $diameter0/$distance; # angular diameter
|
||
$sunCoor{distance} = $distance*$a; # distance in km
|
||
$sunCoor{parallax} = 6378.137/$sunCoor{distance}; # horizontal parallax
|
||
|
||
($sunCoor{ra},$sunCoor{dec}) = Ecl2Equ($sunCoor{lon}, $sunCoor{lat}, $TDT);
|
||
|
||
#-- calculate horizontal coordinates of sun, if geographic positions is given
|
||
if (defined($observerlat) && defined($lmst) ) {
|
||
($sunCoor{az},$sunCoor{alt}) = Equ2Altaz($sunCoor{ra}, $sunCoor{dec}, $TDT, $observerlat, $lmst);
|
||
}
|
||
$sunCoor{sign} = floor($sunCoor{lon}*$RAD/30.);
|
||
$sunCoor{sig} = $zodiac[$sunCoor{sign}];
|
||
|
||
return ( \%sunCoor );
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# MoonPosition - Calculate data and coordinates for the Moon
|
||
# Coordinates are accurate to about 1/5 degree (in ecliptic coordinates)
|
||
#
|
||
########################################################################################################
|
||
|
||
sub MoonPosition($$$$$$$){
|
||
my ($sunlon, $sunanomalyMean, $TDT, $observerlon, $observerlat, $observerradius, $lmst) = @_;
|
||
|
||
my $D = $TDT-2447891.5;
|
||
|
||
#-- Mean Moon orbit elements as of 1990.0
|
||
my $l0 = 318.351648*$DEG;
|
||
my $P0 = 36.340410*$DEG;
|
||
my $N0 = 318.510107*$DEG;
|
||
my $i = 5.145396*$DEG;
|
||
my $e = 0.054900;
|
||
my $a = 384401; # km
|
||
my $diameter0 = 0.5181*$DEG; # angular diameter of Moon at a distance
|
||
my $parallax0 = 0.9507*$DEG; # parallax at distance a
|
||
|
||
my $l = 13.1763966*$DEG*$D+$l0;
|
||
my $MMoon = $l-0.1114041*$DEG*$D-$P0; # Moon's mean anomaly M
|
||
my $N = $N0-0.0529539*$DEG*$D; # Moon's mean ascending node longitude
|
||
my $C = $l-$sunlon;
|
||
my $Ev = 1.2739*$DEG*sin(2*$C-$MMoon);
|
||
my $Ae = 0.1858*$DEG*sin($sunanomalyMean);
|
||
my $A3 = 0.37*$DEG*sin($sunanomalyMean);
|
||
my $MMoon2 = $MMoon+$Ev-$Ae-$A3; # corrected Moon anomaly
|
||
my $Ec = 6.2886*$DEG*sin($MMoon2); # equation of centre
|
||
my $A4 = 0.214*$DEG*sin(2*$MMoon2);
|
||
my $l2 = $l+$Ev+$Ec-$Ae+$A4; # corrected Moon's longitude
|
||
my $V = 0.6583*$DEG*sin(2*($l2-$sunlon));
|
||
my $l3 = $l2+$V; # true orbital longitude;
|
||
|
||
my $N2 = $N-0.16*$DEG*sin($sunanomalyMean);
|
||
|
||
my %moonCoor;
|
||
$moonCoor{lon} = _mod2Pi( $N2 + atan2( sin($l3-$N2)*cos($i), cos($l3-$N2) ) );
|
||
$moonCoor{lat} = asin( sin($l3-$N2)*sin($i) );
|
||
$moonCoor{orbitLon} = $l3;
|
||
|
||
($moonCoor{ra},$moonCoor{dec}) = Ecl2Equ($moonCoor{lon},$moonCoor{lat},$TDT);
|
||
#-- relative distance to semi mayor axis of lunar oribt
|
||
my $distance = (1-$e*$e) / (1+$e*cos($MMoon2+$Ec) );
|
||
$moonCoor{diameter} = $diameter0/$distance; # angular diameter in radians
|
||
$moonCoor{parallax} = $parallax0/$distance; # horizontal parallax in radians
|
||
$moonCoor{distance} = $distance*$a; # distance in km
|
||
|
||
#-- Calculate horizontal coordinates of moon, if geographic positions is given
|
||
|
||
#-- backup geocentric coordinates
|
||
$moonCoor{raGeocentric} = $moonCoor{ra};
|
||
$moonCoor{decGeocentric} = $moonCoor{dec};
|
||
$moonCoor{distanceGeocentric} = $moonCoor{distance};
|
||
|
||
if (defined($observerlat) && defined($observerlon) && defined($lmst) ) {
|
||
#-- transform geocentric coordinates into topocentric (==observer based) coordinates
|
||
my ($distanceTopocentric,$decTopocentric,$raTopocentric) =
|
||
GeoEqu2TopoEqu($moonCoor{ra}, $moonCoor{dec}, $moonCoor{distance}, $observerlon, $observerlat, $observerradius, $lmst);
|
||
#-- now ra and dec are topocentric
|
||
$moonCoor{ra} = $raTopocentric;
|
||
$moonCoor{dec} = $decTopocentric;
|
||
($moonCoor{az},$moonCoor{alt})= Equ2Altaz($moonCoor{ra}, $moonCoor{dec}, $TDT, $observerlat, $lmst);
|
||
}
|
||
|
||
#-- Age of Moon in radians since New Moon (0) - Full Moon (pi)
|
||
$moonCoor{age} = _mod2Pi($l3-$sunlon);
|
||
$moonCoor{phasen} = 0.5*(1-cos($moonCoor{age})); # Moon phase numerical, 0-1
|
||
|
||
my $mainPhase = 1./29.53*360*$DEG; # show 'Newmoon, 'Quarter' for +/-1 day around the actual event
|
||
my $p = _mod($moonCoor{age}, 90.*$DEG);
|
||
if ($p < $mainPhase || $p > 90*$DEG-$mainPhase){
|
||
$p = 2*floor($moonCoor{age} / (90.*$DEG)+0.5);
|
||
}else{
|
||
$p = 2*floor($moonCoor{age} / (90.*$DEG))+1;
|
||
}
|
||
$p = $p % 8;
|
||
$moonCoor{phases} = $phases[$p];
|
||
$moonCoor{phasei} = $p;
|
||
$moonCoor{sign} = floor($moonCoor{lon}*$RAD/30);
|
||
$moonCoor{sig} = $zodiac[$moonCoor{sign}];
|
||
|
||
return ( \%moonCoor );
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# Refraction - Input true altitude in radians, Output: increase in altitude in degrees
|
||
#
|
||
########################################################################################################
|
||
|
||
sub Refraction($){
|
||
my ($alt) = @_;
|
||
my $altdeg = $alt*$RAD;
|
||
if ($altdeg<-2 || $altdeg>=90){
|
||
return(0);
|
||
}
|
||
|
||
my $pressure = 1015;
|
||
my $temperature = 10;
|
||
if ($altdeg>15){
|
||
return( 0.00452*$pressure/( (273+$temperature)*tan($alt)) );
|
||
}
|
||
|
||
my $y = $alt;
|
||
my $D = 0.0;
|
||
my $P = ($pressure-80.)/930.;
|
||
my $Q = 0.0048*($temperature-10.);
|
||
my $y0 = $y;
|
||
my $D0 = $D;
|
||
my $N;
|
||
|
||
for (my $i=0; $i<3; $i++) {
|
||
$N = $y+(7.31/($y+4.4));
|
||
$N = 1./tan($N*$DEG);
|
||
$D = $N*$P/(60.+$Q*($N+39.));
|
||
$N = $y-$y0;
|
||
$y0 = $D-$D0-$N;
|
||
if (($N != 0.) && ($y0 != 0.)) {
|
||
$N = $y-$N*($alt+$D-$y)/$y0;
|
||
} else {
|
||
$N = $alt+$D;
|
||
}
|
||
$y0 = $y;
|
||
$D0 = $D;
|
||
$y = $N;
|
||
}
|
||
return( $D );
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# GMSTRiseSet - returns Greenwich sidereal time (hours) of time of rise
|
||
# and set of object with coordinates ra/dec
|
||
# at geographic position lon/lat (all values in radians)
|
||
# Correction for refraction and semi-diameter/parallax of body is taken care of in function RiseSet
|
||
# h is used to calculate the twilights. It gives the required elevation of the disk center of the sun
|
||
#
|
||
########################################################################################################
|
||
|
||
sub GMSTRiseSet($$$$$){
|
||
my ($ra, $dec, $lon, $lat, $h) = @_;
|
||
|
||
$h = (defined($h)) ? $h : 0.0; # set default value
|
||
#Log 1,"-------------------> Called GMSTRiseSet with $ra $dec $lon $lat $h";
|
||
|
||
# my $tagbogen = acos(-tan(lat)*tan(coor.dec)); // simple formula if twilight is not required
|
||
my $tagbarg = (sin($h) - sin($lat)*sin($dec)) / (cos($lat)*cos($dec));
|
||
if( ($tagbarg > 1.000000) || ($tagbarg < -1.000000) ){
|
||
Log 5,"[FHEM::Astro::GMSTRiseSet] Parameters $ra $dec $lon $lat $h give complex angle";
|
||
return( ("---","---","---") );
|
||
};
|
||
my $tagbogen = acos($tagbarg);
|
||
|
||
my $transit = $RAD/15*( +$ra-$lon);
|
||
my $rise = 24.+$RAD/15*(-$tagbogen+$ra-$lon); # calculate GMST of rise of object
|
||
my $set = $RAD/15*(+$tagbogen+$ra-$lon); # calculate GMST of set of object
|
||
|
||
#--Using the modulo function mod, the day number goes missing. This may get a problem for the moon
|
||
$transit = _mod($transit, 24);
|
||
$rise = _mod($rise, 24);
|
||
$set = _mod($set, 24);
|
||
|
||
return( ($transit, $rise, $set) );
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# InterpolateGMST - Find GMST of rise/set of object from the two calculated
|
||
# (start)points (day 1 and 2) and at midnight UT(0)
|
||
#
|
||
########################################################################################################
|
||
|
||
sub InterpolateGMST($$$$){
|
||
my ($gmst0, $gmst1, $gmst2, $timefactor) = @_;
|
||
return( ($timefactor*24.07*$gmst1- $gmst0*($gmst2-$gmst1)) / ($timefactor*24.07+$gmst1-$gmst2) );
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# RiseSet
|
||
# // JD is the Julian Date of 0h UTC time (midnight)
|
||
#
|
||
########################################################################################################
|
||
|
||
sub RiseSet($$$$$$$$$$$){
|
||
my ($jd0UT, $diameter, $parallax, $ra1, $dec1, $ra2, $dec2, $lon, $lat, $timeinterval, $altip) = @_;
|
||
|
||
#--altitude of sun center: semi-diameter, horizontal parallax and (standard) refraction of 34'
|
||
# true height of sun center for sunrise and set calculation. Is kept 0 for twilight (ie. altitude given):
|
||
my $alt = (!defined($altip)) ? 0.5*$diameter-$parallax+34./60*$DEG : 0.;
|
||
my $altitude = (!defined($altip)) ? 0. : $altip;
|
||
|
||
my ($transit1, $rise1, $set1) = GMSTRiseSet($ra1, $dec1, $lon, $lat, $altitude);
|
||
my ($transit2, $rise2, $set2) = GMSTRiseSet($ra2, $dec2, $lon, $lat, $altitude);
|
||
|
||
#-- complex angle
|
||
if( ($transit1 eq "---") || ($transit2 eq "---") ){
|
||
return( ("---","---","---") );
|
||
}
|
||
|
||
#-- unwrap GMST in case we move across 24h -> 0h
|
||
$transit2 += 24
|
||
if ($transit1 > $transit2 && abs($transit1-$transit2)>18);
|
||
$rise2 += 24
|
||
if ($rise1 > $rise2 && abs($rise1-$rise2)>18);
|
||
$set2 += 24
|
||
if ($set1 > $set2 && abs($set1-$set2)>18);
|
||
|
||
my $T0 = GMST($jd0UT);
|
||
# my $T02 = T0-zone*1.002738; // Greenwich sidereal time at 0h time zone (zone: hours)
|
||
#-- Greenwich sidereal time for 0h at selected longitude
|
||
my $T02 = $T0-$lon*$RAD/15*1.002738;
|
||
$T02 +=24 if ($T02 < 0);
|
||
|
||
if ($transit1 < $T02) {
|
||
$transit1 += 24;
|
||
$transit2 += 24;
|
||
}
|
||
if ($rise1 < $T02) {
|
||
$rise1 += 24;
|
||
$rise2 += 24;
|
||
}
|
||
if ($set1 < $T02) {
|
||
$set1 += 24;
|
||
$set2 += 24;
|
||
}
|
||
|
||
#-- Refraction and Parallax correction
|
||
my $decMean = 0.5*($dec1+$dec2);
|
||
my $psi = acos(sin($lat)/cos($decMean));
|
||
my $y = asin(sin($alt)/sin($psi));
|
||
my $dt = 240*$RAD*$y/cos($decMean)/3600; # time correction due to refraction, parallax
|
||
|
||
my $transit = GMST2UT( $jd0UT, InterpolateGMST( $T0, $transit1, $transit2, $timeinterval) );
|
||
my $rise = GMST2UT( $jd0UT, InterpolateGMST( $T0, $rise1, $rise2, $timeinterval) - $dt );
|
||
my $set = GMST2UT( $jd0UT, InterpolateGMST( $T0, $set1, $set2, $timeinterval) + $dt );
|
||
|
||
return( ($transit,$rise,$set) );
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# SunRise - Find (local) time of sunrise and sunset, and twilights
|
||
# JD is the Julian Date of 0h local time (midnight)
|
||
# Accurate to about 1-2 minutes
|
||
# recursive: 1 - calculate rise/set in UTC in a second run
|
||
# recursive: 0 - find rise/set on the current local day.
|
||
# This is set when doing the first call to this function
|
||
#
|
||
########################################################################################################
|
||
|
||
sub SunRise($$$$$$$$){
|
||
my ($JD, $deltaT, $lon, $lat, $zone, $horM, $horE, $recursive) = @_;
|
||
|
||
my $jd0UT = floor($JD-0.5)+0.5; # JD at 0 hours UT
|
||
|
||
#-- calculations for noon
|
||
my $sunCoor1 = SunPosition($jd0UT+ $deltaT/24./3600.,undef,undef);
|
||
|
||
#-- calculations for next day's UTC midnight
|
||
my $sunCoor2 = SunPosition($jd0UT+1.+$deltaT/24./3600.,undef,undef);
|
||
|
||
#-- rise/set time in UTC
|
||
my ($transit,$rise,$set) = RiseSet($jd0UT, $sunCoor1->{diameter}, $sunCoor1->{parallax},
|
||
$sunCoor1->{ra}, $sunCoor1->{dec}, $sunCoor2->{ra}, $sunCoor2->{dec}, $lon, $lat, 1,undef);
|
||
if( $transit eq "---" ){
|
||
Log 3,"[FHEM::Astro::SunRise] no solution possible - maybe the sun never sets ?";
|
||
return( ($transit,$rise,$set) );
|
||
}
|
||
|
||
my ($transittemp,$risetemp,$settemp);
|
||
#-- check and adjust to have rise/set time on local calendar day
|
||
if ( $recursive==0 ) {
|
||
if ($zone>0) {
|
||
#rise time was yesterday local time -> calculate rise time for next UTC day
|
||
if ($rise >=24-$zone || $transit>=24-$zone || $set>=24-$zone) {
|
||
($transittemp,$risetemp,$settemp) = SunRise($JD+1, $deltaT, $lon, $lat, $zone, $horM, $horE, 1);
|
||
$transit = $transittemp
|
||
if ($transit>=24-$zone);
|
||
$rise = $risetemp
|
||
if ($rise>=24-$zone);
|
||
$set = $settemp
|
||
if ($set>=24-$zone);
|
||
}
|
||
}elsif ($zone<0) {
|
||
#rise time was yesterday local time -> calculate rise time for previous UTC day
|
||
if ($rise<-$zone || $transit<-zone || $set<-zone) {
|
||
($transittemp,$risetemp,$settemp) = SunRise($JD-1, $deltaT, $lon, $lat, $zone, $horM, $horE, 1);
|
||
$transit = $transittemp
|
||
if ($transit<-$zone);
|
||
$rise = $risetemp
|
||
if ($rise<-$zone);
|
||
$set = $settemp
|
||
if ($set <-$zone);
|
||
}
|
||
}
|
||
|
||
$transit = _mod($transit+$zone, 24.);
|
||
$rise = _mod($rise +$zone, 24.);
|
||
$set = _mod($set +$zone, 24.);
|
||
|
||
#-- Twilight calculation
|
||
#-- civil twilight time in UTC.
|
||
my $CivilTwilightMorning;
|
||
my $CivilTwilightEvening;
|
||
($transittemp,$risetemp,$settemp) = RiseSet($jd0UT, $sunCoor1->{diameter}, $sunCoor1->{parallax},
|
||
$sunCoor1->{ra}, $sunCoor1->{dec}, $sunCoor2->{ra}, $sunCoor2->{dec}, $lon, $lat, 1, -6.*$DEG);
|
||
if( $transittemp eq "---" ){
|
||
Log 4,"[FHEM::Astro::SunRise] no solution possible for civil twilight - maybe the sun never sets below -6 degrees?";
|
||
$CivilTwilightMorning = "---";
|
||
$CivilTwilightEvening = "---";
|
||
}else{
|
||
$CivilTwilightMorning = _mod($risetemp +$zone, 24.);
|
||
$CivilTwilightEvening = _mod($settemp +$zone, 24.);
|
||
}
|
||
|
||
#-- nautical twilight time in UTC.
|
||
my $NauticTwilightMorning;
|
||
my $NauticTwilightEvening;
|
||
($transittemp,$risetemp,$settemp) = RiseSet($jd0UT, $sunCoor1->{diameter}, $sunCoor1->{parallax},
|
||
$sunCoor1->{ra}, $sunCoor1->{dec}, $sunCoor2->{ra}, $sunCoor2->{dec}, $lon, $lat, 1, -12.*$DEG);
|
||
if( $transittemp eq "---" ){
|
||
Log 4,"[FHEM::Astro::SunRise] no solution possible for nautical twilight - maybe the sun never sets below -12 degrees?";
|
||
$NauticTwilightMorning = "---";
|
||
$NauticTwilightEvening = "---";
|
||
}else{
|
||
$NauticTwilightMorning = _mod($risetemp +$zone, 24.);
|
||
$NauticTwilightEvening = _mod($settemp +$zone, 24.);
|
||
}
|
||
|
||
#-- astronomical twilight time in UTC.
|
||
my $AstroTwilightMorning;
|
||
my $AstroTwilightEvening;
|
||
($transittemp,$risetemp,$settemp) = RiseSet($jd0UT, $sunCoor1->{diameter}, $sunCoor1->{parallax},
|
||
$sunCoor1->{ra}, $sunCoor1->{dec}, $sunCoor2->{ra}, $sunCoor2->{dec}, $lon, $lat, 1, -18.*$DEG);
|
||
if( $transittemp eq "---" ){
|
||
Log 4,"[FHEM::Astro::SunRise] no solution possible for astronomical twilight - maybe the sun never sets below -18 degrees?";
|
||
$AstroTwilightMorning = "---";
|
||
$AstroTwilightEvening = "---";
|
||
}else{
|
||
$AstroTwilightMorning = _mod($risetemp +$zone, 24.);
|
||
$AstroTwilightEvening = _mod($settemp +$zone, 24.);
|
||
}
|
||
|
||
#-- custom twilight time in UTC
|
||
my $CustomTwilightMorning;
|
||
my $CustomTwilightEvening;
|
||
($transittemp,$risetemp,$settemp) = RiseSet($jd0UT, $sunCoor1->{diameter}, $sunCoor1->{parallax},
|
||
$sunCoor1->{ra}, $sunCoor1->{dec}, $sunCoor2->{ra}, $sunCoor2->{dec}, $lon, $lat, 1, $horM*$DEG);
|
||
if( $transittemp eq "---" ){
|
||
Log 4,"[FHEM::Astro::SunRise] no solution possible for custom morning twilight - maybe the sun never sets below ".$horM." degrees?";
|
||
$CustomTwilightMorning = "---";
|
||
}else{
|
||
$CustomTwilightMorning = _mod($risetemp +$zone, 24.);
|
||
}
|
||
($transittemp,$risetemp,$settemp) = RiseSet($jd0UT, $sunCoor1->{diameter}, $sunCoor1->{parallax},
|
||
$sunCoor1->{ra}, $sunCoor1->{dec}, $sunCoor2->{ra}, $sunCoor2->{dec}, $lon, $lat, 1, $horE*$DEG);
|
||
if( $transittemp eq "---" ){
|
||
Log 4,"[FHEM::Astro::SunRise] no solution possible for custom evening twilight - maybe the sun never sets below ".$horE." degrees?";
|
||
$CustomTwilightEvening = "---";
|
||
}else{
|
||
$CustomTwilightEvening = _mod($settemp +$zone, 24.);
|
||
}
|
||
|
||
return( ($transit,$rise,$set,$CivilTwilightMorning,$CivilTwilightEvening,
|
||
$NauticTwilightMorning,$NauticTwilightEvening,$AstroTwilightMorning,$AstroTwilightEvening,$CustomTwilightMorning,$CustomTwilightEvening) );
|
||
}else{
|
||
return( ($transit,$rise,$set) );
|
||
}
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# MoonRise - Find local time of moonrise and moonset
|
||
# JD is the Julian Date of 0h local time (midnight)
|
||
# Accurate to about 5 minutes or better
|
||
# recursive: 1 - calculate rise/set in UTC
|
||
# recursive: 0 - find rise/set on the current local day (set could also be first)
|
||
# returns '' for moonrise/set does not occur on selected day
|
||
#
|
||
########################################################################################################
|
||
|
||
sub MoonRise($$$$$$$){
|
||
my ($JD, $deltaT, $lon, $lat, $radius, $zone, $recursive) = @_;
|
||
my $timeinterval = 0.5;
|
||
|
||
my $jd0UT = floor($JD-0.5)+0.5; # JD at 0 hours UT
|
||
#-- calculations for noon
|
||
my $sunCoor1 = SunPosition($jd0UT+ $deltaT/24./3600.,undef,undef);
|
||
my $moonCoor1 = MoonPosition($sunCoor1->{lon}, $sunCoor1->{anomalyMean}, $jd0UT+ $deltaT/24./3600.,undef,undef,undef,undef);
|
||
|
||
#-- calculations for next day's midnight
|
||
my $sunCoor2 = SunPosition($jd0UT +$timeinterval + $deltaT/24./3600.,undef,undef);
|
||
my $moonCoor2 = MoonPosition($sunCoor2->{lon}, $sunCoor2->{anomalyMean}, $jd0UT +$timeinterval + $deltaT/24./3600.,undef,undef,undef,undef);
|
||
|
||
# rise/set time in UTC, time zone corrected later.
|
||
# Taking into account refraction, semi-diameter and parallax
|
||
my ($transit,$rise,$set) = RiseSet($jd0UT, $moonCoor1->{diameter}, $moonCoor1->{parallax},
|
||
$moonCoor1->{ra}, $moonCoor1->{dec}, $moonCoor2->{ra}, $moonCoor2->{dec}, $lon, $lat, $timeinterval,undef);
|
||
my ($transittemp,$risetemp,$settemp);
|
||
my ($transitprev,$riseprev,$setprev);
|
||
|
||
# check and adjust to have rise/set time on local calendar day
|
||
if ( $recursive==0 ) {
|
||
if ($zone>0) {
|
||
# recursive call to MoonRise returns events in UTC
|
||
($transitprev,$riseprev,$setprev) = MoonRise($JD-1., $deltaT, $lon, $lat, $radius, $zone, 1);
|
||
if ($transit >= 24.-$zone || $transit < -$zone) { # transit time is tomorrow local time
|
||
if ($transitprev < 24.-$zone){
|
||
$transit = ""; # there is no moontransit today
|
||
}else{
|
||
$transit = $transitprev;
|
||
}
|
||
}
|
||
|
||
if ($rise >= 24.-$zone || $rise < -$zone) { # rise time is tomorrow local time
|
||
if ($riseprev < 24.-$zone){
|
||
$rise = ""; # there is no moonrise today
|
||
}else{
|
||
$rise = $riseprev;
|
||
}
|
||
}
|
||
|
||
if ($set >= 24.-$zone || $set < -$zone) { # set time is tomorrow local time
|
||
if ($setprev < 24.-$zone){
|
||
$set = ""; # there is no moonset today
|
||
}else{
|
||
$set = $setprev;
|
||
}
|
||
}
|
||
|
||
}elsif ($zone<0) { # rise/set time was tomorrow local time -> calculate rise time for previous UTC day
|
||
if ($rise<-$zone || $set<-$zone || $transit<-$zone) {
|
||
($transittemp,$risetemp,$settemp) = MoonRise($JD+1., $deltaT, $lon, $lat, $radius, $zone, 1);
|
||
if ($transit < -$zone){
|
||
if ($transittemp > -$zone){
|
||
$transit = ''; # there is no moontransit today
|
||
}else{
|
||
$transit = $transittemp;
|
||
}
|
||
}
|
||
|
||
if ($rise < -$zone) {
|
||
if ($risetemp > -$zone){
|
||
$rise = ''; # there is no moonrise today
|
||
}else{
|
||
$rise = $risetemp;
|
||
}
|
||
}
|
||
|
||
if ($set < -$zone){
|
||
if ($settemp > -$zone){
|
||
$set = ''; # there is no moonset today
|
||
}else{
|
||
$set = $settemp;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#-- correct for time zone, if time is valid
|
||
$transit = _mod($transit +$zone, 24.)
|
||
if( $transit ne "");
|
||
$rise = _mod($rise +$zone, 24.)
|
||
if ($rise ne "");
|
||
$set = _mod($set +$zone, 24.)
|
||
if ($set ne "");
|
||
}
|
||
return( ($transit,$rise,$set) );
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# Season - Find astronomical season
|
||
#
|
||
########################################################################################################
|
||
|
||
sub Season($$;$) {
|
||
my ( $JD0, $deltaT, $lat ) = @_;
|
||
|
||
# season starts during the day so we need to
|
||
# look for tomorrows quarter at noon
|
||
my $sunCoor = SunPosition( $JD0 + 1. + $deltaT / 86400.0, undef, undef );
|
||
my $quarter = ceil( rad2deg( $sunCoor->{lon} ) / 90. ) % 4;
|
||
|
||
return
|
||
wantarray
|
||
? ( $quarter, $seasons{ $lat && $lat < 0 ? 'S' : 'N' }[$quarter] )
|
||
: $quarter;
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# SetTime - update of the %Date hash for today
|
||
#
|
||
########################################################################################################
|
||
|
||
sub SetTime (;$$$) {
|
||
my ( $time, $tz, $lc_time ) = @_;
|
||
|
||
#-- readjust locale
|
||
my $old_lctime = setlocale(LC_TIME);
|
||
setlocale(LC_TIME, $lc_time) if ($lc_time);
|
||
use locale ':not_characters';
|
||
|
||
#-- readjust timezone
|
||
local $ENV{TZ} = $tz if ($tz);
|
||
tzset() if ( exists &{'tzset'} );
|
||
|
||
$time = gettimeofday() unless ( defined($time) );
|
||
|
||
my ( $sec, $min, $hour, $day, $month, $year, $wday, $yday, $isdst ) =
|
||
localtime($time);
|
||
$year += 1900;
|
||
$month += 1;
|
||
$Date{timestamp} = $time;
|
||
$Date{timeday} = $hour + $min / 60. + $sec / 3600.;
|
||
$Date{year} = $year;
|
||
$Date{month} = $month;
|
||
$Date{day} = $day;
|
||
$Date{hour} = $hour;
|
||
$Date{min} = $min;
|
||
$Date{sec} = $sec;
|
||
$Date{isdst} = $isdst;
|
||
#-- broken on windows
|
||
#$Date{zonedelta} = (strftime "%z", localtime($time))/100;
|
||
$Date{zonedelta} = _tzoffset($time) / 100;
|
||
#-- half broken in windows
|
||
$Date{dayofyear} = 1 * strftime( "%j", localtime($time) );
|
||
|
||
$Date{wdayl} = strftime( "%A", localtime($time) );
|
||
$Date{wdays} = strftime( "%a", localtime($time) );
|
||
$Date{monthl} = strftime( "%B", localtime($time) );
|
||
$Date{months} = strftime( "%b", localtime($time) );
|
||
$Date{datetime} = strftime( "%c", localtime($time) );
|
||
$Date{week} = 1 * strftime( "%V", localtime($time) );
|
||
$Date{wday} = 1 * strftime( "%w", localtime($time) );
|
||
$Date{time} = strftime( "%X", localtime($time) );
|
||
$Date{date} = strftime( "%x", localtime($time) );
|
||
$Date{tz} = strftime( "%Z", localtime($time) );
|
||
|
||
delete $Date{tz} if (!$Date{tz} || $Date{tz} eq "" || $Date{tz} eq " ");
|
||
|
||
delete local $ENV{TZ};
|
||
tzset() if ( exists &{'tzset'} );
|
||
|
||
setlocale(LC_TIME, "");
|
||
setlocale(LC_TIME, $old_lctime);
|
||
no locale;
|
||
|
||
return (undef);
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# Compute - sequential calculation of properties
|
||
#
|
||
########################################################################################################
|
||
|
||
sub Compute($;$){
|
||
my ($hash,$params) = @_;
|
||
undef %Astro;
|
||
my $name = $hash->{NAME};
|
||
SetTime() if (scalar keys %Date == 0); # fill %Date if it is still empty after restart to avoid warnings
|
||
|
||
return undef if( !$init_done );
|
||
|
||
#-- readjust language
|
||
my $lang = uc(AttrVal($name,"language",AttrVal("global","language","EN")));
|
||
if( defined($params->{"language"}) &&
|
||
exists($transtable{uc($params->{"language"})})
|
||
){
|
||
$lang = uc($params->{"language"});
|
||
$tt = $transtable{$lang};
|
||
}elsif( exists($transtable{uc($lang)}) ){
|
||
$tt = $transtable{uc($lang)};
|
||
}else{
|
||
$tt = $transtable{EN};
|
||
}
|
||
|
||
#-- readjust timezone
|
||
my $tz = AttrVal($name,"timezone",AttrVal("global","timezone",undef));
|
||
$tz = $params->{"timezone"}
|
||
if ( defined( $params->{"timezone"} ) );
|
||
local $ENV{TZ} = $tz if ($tz);
|
||
tzset() if ( exists &{'tzset'} );
|
||
|
||
#-- geodetic latitude and longitude of observer on WGS84
|
||
if( defined($params->{"latitude"}) ){
|
||
$Astro{ObsLat} = $params->{"latitude"};
|
||
}elsif( defined($attr{$name}) && defined($attr{$name}{"latitude"}) ){
|
||
$Astro{ObsLat} = $attr{$name}{"latitude"};
|
||
}elsif( defined($attr{"global"}{"latitude"}) ){
|
||
$Astro{ObsLat} = $attr{"global"}{"latitude"};
|
||
}else{
|
||
$Astro{ObsLat} = 50.0;
|
||
Log3 $name,3,"[Astro] No latitude attribute set in global device, using 50.0°";
|
||
}
|
||
if( defined($params->{"longitude"}) ){
|
||
$Astro{ObsLon} = $params->{"longitude"};
|
||
}elsif( defined($attr{$name}) && defined($attr{$name}{"longitude"}) ){
|
||
$Astro{ObsLon} = $attr{$name}{"longitude"};
|
||
}elsif( defined($attr{"global"}{"longitude"}) ){
|
||
$Astro{ObsLon} = $attr{"global"}{"longitude"};
|
||
}else{
|
||
$Astro{ObsLon} = 10.0;
|
||
Log3 $name,3,"[Astro] No longitude attribute set in global device, using 10.0°";
|
||
}
|
||
#-- altitude of observer in meters above WGS84 ellipsoid
|
||
if( defined($params->{"altitude"}) ){
|
||
$Astro{ObsAlt} = $params->{"altitude"};
|
||
}elsif( defined($attr{$name}) && defined($attr{$name}{"altitude"}) ){
|
||
$Astro{ObsAlt} = $attr{$name}{"altitude"};
|
||
}elsif( defined($attr{"global"}{"altitude"}) ){
|
||
$Astro{ObsAlt} = $attr{"global"}{"altitude"};
|
||
}else{
|
||
$Astro{ObsAlt} = 0.0;
|
||
Log3 $name,3,"[Astro] No altitude attribute set in global device, using 0.0 m above sea level";
|
||
}
|
||
#-- custom horizon of observer in degrees
|
||
if( defined($params->{"horizon"}) &&
|
||
$params->{"horizon"} =~ m/^([^:]+)(?::(.+))?$/
|
||
){
|
||
$Astro{ObsHorMorning} = $1;
|
||
$Astro{ObsHorEvening} = defined($2) ? $2 : $1;
|
||
}elsif( defined($attr{$name}) && defined($attr{$name}{"horizon"}) &&
|
||
$attr{$name}{"horizon"} =~ m/^([^:]+)(?::(.+))?$/
|
||
){
|
||
$Astro{ObsHorMorning} = $1;
|
||
$Astro{ObsHorEvening} = defined($2) ? $2 : $1;
|
||
} else {
|
||
$Astro{ObsHorMorning} = 0.0;
|
||
$Astro{ObsHorEvening} = 0.0;
|
||
Log3 $name,5,"[Astro] No horizon attribute defined, using 0.0° for morning and evening";
|
||
}
|
||
|
||
#-- internal variables converted to Radians and km
|
||
my $lat = $Astro{ObsLat}*$DEG;
|
||
my $lon = $Astro{ObsLon}*$DEG;
|
||
my $height = $Astro{ObsAlt} * 0.001;
|
||
|
||
#if (eval(form.Year.value)<=1900 || eval(form.Year.value)>=2100 ) {
|
||
# alert("Dies Script erlaubt nur Berechnungen"+
|
||
# return;
|
||
#}
|
||
|
||
my $JD0 = Date2JD( $Date{day}, $Date{month}, $Date{year} );
|
||
my $JD = $JD0 + ( $Date{hour} - $Date{zonedelta} + $Date{min}/60. + $Date{sec}/3600.)/24;
|
||
my $TDT = $JD + $deltaT/86400.0;
|
||
|
||
$Astro{".ObsJD"} = $JD;
|
||
$Astro{ObsJD} = _round($JD,2);
|
||
|
||
my $gmst = GMST($JD);
|
||
my $lmst = GMST2LMST($gmst, $lon);
|
||
$Astro{".ObsGMST"} = $gmst;
|
||
$Astro{".ObsLMST"} = $lmst;
|
||
$Astro{ObsGMST} = HHMMSS($gmst);
|
||
$Astro{ObsLMST} = HHMMSS($lmst);
|
||
|
||
#-- geocentric cartesian coordinates of observer
|
||
my ($x,$y,$z,$radius) = Observer2EquCart($lon, $lat, $height, $gmst);
|
||
|
||
#-- calculate data for the sun at given time
|
||
my $sunCoor = SunPosition($TDT, $lat, $lmst*15.*$DEG);
|
||
$Astro{SunLon} = _round($sunCoor->{lon}*$RAD,1);
|
||
#$Astro{SunLat} = $sunCoor->{lat}*$RAD;
|
||
$Astro{".SunRa"}= _round($sunCoor->{ra} *$RAD/15,1);
|
||
$Astro{SunRa} = HHMM($Astro{".SunRa"});
|
||
$Astro{SunDec} = _round($sunCoor->{dec}*$RAD,1);
|
||
$Astro{SunAz} = _round($sunCoor->{az} *$RAD,1);
|
||
$Astro{SunAlt} = _round($sunCoor->{alt}*$RAD + Refraction($sunCoor->{alt}),1); # including refraction WARNUNG => *RAD ???
|
||
$Astro{SunSign} = $tt->{$sunCoor->{sig}};
|
||
$Astro{SunSignN}= $sunCoor->{sign};
|
||
$Astro{SunDiameter}=_round($sunCoor->{diameter}*$RAD*60,1); #angular diameter in arc seconds
|
||
$Astro{SunDistance}=_round($sunCoor->{distance},0);
|
||
|
||
#-- calculate distance from the observer (on the surface of earth) to the center of the sun
|
||
my ($xs,$ys,$zs) = EquPolar2Cart($sunCoor->{ra}, $sunCoor->{dec}, $sunCoor->{distance});
|
||
$Astro{SunDistanceObserver} = _round(sqrt( ($xs-$x)**2 + ($ys-$y)**2 + ($zs-$z)**2 ),0);
|
||
|
||
my ($suntransit,$sunrise,$sunset,$CivilTwilightMorning,$CivilTwilightEvening,
|
||
$NauticTwilightMorning,$NauticTwilightEvening,$AstroTwilightMorning,$AstroTwilightEvening,$CustomTwilightMorning,$CustomTwilightEvening) =
|
||
SunRise($JD0, $deltaT, $lon, $lat, $Date{zonedelta}, $Astro{ObsHorMorning}, $Astro{ObsHorEvening}, 0);
|
||
$Astro{".SunTransit"} = $suntransit;
|
||
$Astro{".SunRise"} = $sunrise;
|
||
$Astro{".SunSet"} = $sunset;
|
||
$Astro{".CivilTwilightMorning"} = $CivilTwilightMorning;
|
||
$Astro{".CivilTwilightEvening"} = $CivilTwilightEvening;
|
||
$Astro{".NauticTwilightMorning"} = $NauticTwilightMorning;
|
||
$Astro{".NauticTwilightEvening"} = $NauticTwilightEvening;
|
||
$Astro{".AstroTwilightMorning"} = $AstroTwilightMorning;
|
||
$Astro{".AstroTwilightEvening"} = $AstroTwilightEvening;
|
||
$Astro{".CustomTwilightMorning"} = $CustomTwilightMorning;
|
||
$Astro{".CustomTwilightEvening"} = $CustomTwilightEvening;
|
||
$Astro{SunTransit} = HHMM($suntransit);
|
||
$Astro{SunRise} = HHMM($sunrise);
|
||
$Astro{SunSet} = HHMM($sunset);
|
||
$Astro{CivilTwilightMorning} = HHMM($CivilTwilightMorning);
|
||
$Astro{CivilTwilightEvening} = HHMM($CivilTwilightEvening);
|
||
$Astro{NauticTwilightMorning} = HHMM($NauticTwilightMorning);
|
||
$Astro{NauticTwilightEvening} = HHMM($NauticTwilightEvening);
|
||
$Astro{AstroTwilightMorning} = HHMM($AstroTwilightMorning);
|
||
$Astro{AstroTwilightEvening} = HHMM($AstroTwilightEvening);
|
||
$Astro{CustomTwilightMorning} = HHMM($CustomTwilightMorning);
|
||
$Astro{CustomTwilightEvening} = HHMM($CustomTwilightEvening);
|
||
|
||
#-- hours of day and night
|
||
my $hoursofsunlight;
|
||
my $hoursofnight;
|
||
if (
|
||
(!defined($sunset) && !defined($sunrise)) ||
|
||
($sunset !~ m/^\d+/ && $sunrise !~ m/^\d+/)
|
||
){
|
||
if ($Astro{SunAlt} > 0.) {
|
||
$hoursofsunlight = 24.;
|
||
$hoursofnight = 0.;
|
||
} else {
|
||
$hoursofsunlight = 0.;
|
||
$hoursofnight = 24.;
|
||
}
|
||
}
|
||
elsif (!defined($sunset) || $sunset !~ m/^\d+/) {
|
||
$hoursofsunlight = 24. - $sunrise;
|
||
$hoursofnight = 24. - $hoursofsunlight;
|
||
}
|
||
elsif (!defined($sunrise) || $sunrise !~ m/^\d+/) {
|
||
$hoursofsunlight = 24. - $sunset;
|
||
$hoursofnight = 24. - $hoursofsunlight;
|
||
} else {
|
||
my $ss = $sunset;
|
||
$ss += 24.
|
||
if ($sunrise > $sunset);
|
||
$hoursofsunlight = $ss - $sunrise;
|
||
$hoursofnight = 24. - $hoursofsunlight;
|
||
}
|
||
$Astro{".SunHrsVisible"} = $hoursofsunlight;
|
||
$Astro{".SunHrsInvisible"} = $hoursofnight;
|
||
$Astro{SunHrsVisible} = HHMM($hoursofsunlight);
|
||
$Astro{SunHrsInvisible} = HHMM($hoursofnight);
|
||
|
||
#-- calculate data for the moon at given time
|
||
my $moonCoor = MoonPosition($sunCoor->{lon}, $sunCoor->{anomalyMean}, $TDT, $lon, $lat, $radius, $lmst*15.*$DEG);
|
||
$Astro{MoonLon} = _round($moonCoor->{lon}*$RAD,1);
|
||
$Astro{MoonLat} = _round($moonCoor->{lat}*$RAD,1);
|
||
$Astro{".MoonRa"} = _round($moonCoor->{ra} *$RAD/15.,1);
|
||
$Astro{MoonRa} = HHMM($Astro{".MoonRa"});
|
||
$Astro{MoonDec} = _round($moonCoor->{dec}*$RAD,1);
|
||
$Astro{MoonAz} = _round($moonCoor->{az} *$RAD,1);
|
||
$Astro{MoonAlt} = _round($moonCoor->{alt}*$RAD + Refraction($moonCoor->{alt}),1); # including refraction WARNUNG => *RAD ???
|
||
$Astro{MoonSign} = $tt->{$moonCoor->{sig}};
|
||
$Astro{MoonSignN} = $moonCoor->{sign};
|
||
$Astro{MoonDistance} = _round($moonCoor->{distance},0);
|
||
$Astro{MoonDiameter} = _round($moonCoor->{diameter}*$RAD*60.,1); # angular diameter in arc seconds
|
||
$Astro{MoonAge} = _round($moonCoor->{age}*$RAD,1);
|
||
$Astro{MoonPhaseN} = _round($moonCoor->{phasen},2);
|
||
$Astro{MoonPhaseI} = $moonCoor->{phasei};
|
||
$Astro{MoonPhaseS} = $tt->{$moonCoor->{phases}};
|
||
|
||
#-- calculate distance from the observer (on the surface of earth) to the center of the moon
|
||
my ($xm,$ym,$zm) = EquPolar2Cart($moonCoor->{ra}, $moonCoor->{dec}, $moonCoor->{distance});
|
||
#Log 1," distance=".$moonCoor->{distance}." test=".sqrt( ($xm)**2 + ($ym)**2 + ($zm)**2 )." $xm $ym $zm";
|
||
#Log 1," distance=".$radius." test=".sqrt( ($x)**2 + ($y)**2 + ($z)**2 )." $x $y $z";
|
||
$Astro{".MoonDistanceObserver"} = sqrt( ($xm-$x)**2 + ($ym-$y)**2 + ($zm-$z)**2 );
|
||
$Astro{MoonDistanceObserver} = _round($Astro{".MoonDistanceObserver"},0);
|
||
|
||
my ($moontransit,$moonrise,$moonset) = MoonRise($JD0, $deltaT, $lon, $lat, $radius, $Date{zonedelta}, 0);
|
||
$Astro{".MoonTransit"} = $moontransit;
|
||
$Astro{".MoonRise"} = $moonrise;
|
||
$Astro{".MoonSet"} = $moonset;
|
||
$Astro{MoonTransit} = HHMM($moontransit);
|
||
$Astro{MoonRise} = HHMM($moonrise);
|
||
$Astro{MoonSet} = HHMM($moonset);
|
||
|
||
#-- moon visiblity
|
||
my $moonvisible;
|
||
my $mooninvisible;
|
||
if (
|
||
(!defined($moonset) && !defined($moonrise)) ||
|
||
($moonset !~ m/^\d+/ && $moonrise !~ m/^\d+/)
|
||
){
|
||
if ($Astro{MoonAlt} >= 0.) {
|
||
$moonvisible = 24.;
|
||
$mooninvisible = 0.;
|
||
} else {
|
||
$moonvisible = 0.;
|
||
$mooninvisible = 24.;
|
||
}
|
||
}
|
||
elsif (!defined($moonset) || $moonset !~ m/^\d+/) {
|
||
$moonvisible = 24. - $moonrise;
|
||
$mooninvisible = 24. - $moonvisible;
|
||
}
|
||
elsif (!defined($moonrise) || $moonrise !~ m/^\d+/) {
|
||
$moonvisible = 24. - $moonset;
|
||
$mooninvisible = 24. - $moonvisible;
|
||
} else {
|
||
my $ss = $moonset;
|
||
$ss += 24.
|
||
if ($moonrise > $moonset);
|
||
$moonvisible = $ss - $moonrise;
|
||
$mooninvisible = 24. - $moonvisible;
|
||
}
|
||
$Astro{".MoonHrsVisible"} = $moonvisible;
|
||
$Astro{".MoonHrsInvisible"} = $mooninvisible;
|
||
$Astro{MoonHrsVisible} = HHMM($moonvisible);
|
||
$Astro{MoonHrsInvisible} = HHMM($mooninvisible);
|
||
|
||
#-- fix date
|
||
$Astro{ObsDate} = $Date{date};
|
||
$Astro{ObsTime} = $Date{time};
|
||
$Astro{ObsTimezone} = $Date{zonedelta};
|
||
$Astro{ObsTimezoneS} = $Date{tz} if ( $Date{tz} );
|
||
$Astro{ObsDayofyear} = $Date{dayofyear};
|
||
$Astro{ObsIsDST} = $Date{isdst};
|
||
$Astro{".timestamp"} = $Date{timestamp};
|
||
$Astro{".timeday"} = $Date{timeday};
|
||
$Astro{".year"} = $Date{year};
|
||
$Astro{".month"} = $Date{month};
|
||
$Astro{".day"} = $Date{day};
|
||
$Astro{".hour"} = $Date{hour};
|
||
$Astro{".min"} = $Date{min};
|
||
$Astro{".sec"} = $Date{sec};
|
||
$Astro{".wdayl"} = $Date{wdayl};
|
||
$Astro{".wdays"} = $Date{wdays};
|
||
$Astro{".monthl"} = $Date{monthl};
|
||
$Astro{".months"} = $Date{months};
|
||
$Astro{".datetime"} = $Date{datetime};
|
||
$Astro{".week"} = $Date{week};
|
||
$Astro{".wday"} = $Date{wday};
|
||
|
||
#-- check astro season
|
||
my ($seasonn, $season) = Season($JD0, $deltaT, $Astro{ObsLat});
|
||
$Astro{ObsSeason} = $tt->{$season};
|
||
$Astro{ObsSeasonN} = $seasonn;
|
||
|
||
delete local $ENV{TZ};
|
||
tzset() if ( exists &{'tzset'} );
|
||
|
||
return( undef );
|
||
};
|
||
|
||
########################################################################################################
|
||
#
|
||
# Moonwidget - SVG picture of the moon
|
||
#
|
||
# Parameter arg = argument array
|
||
#
|
||
########################################################################################################
|
||
|
||
sub Moonwidget($){
|
||
my ($arg) = @_;
|
||
my $name = $FW_webArgs{name};
|
||
$name =~ s/'//g if ($name);
|
||
my $hash = $name && $name ne "" && defined($defs{$name}) ? $defs{$name} : ();
|
||
|
||
my $mooncolor = 'rgb(255,220,100)';
|
||
my $moonshadow = 'rgb(70,70,100)';
|
||
|
||
$mooncolor = $FW_webArgs{mooncolor}
|
||
if ($FW_webArgs{mooncolor} );
|
||
$moonshadow = $FW_webArgs{moonshadow}
|
||
if ($FW_webArgs{moonshadow} );
|
||
|
||
my @size = split('x', ($FW_webArgs{size} ? $FW_webArgs{size} : '400x400'));
|
||
|
||
$FW_RETTYPE = "image/svg+xml";
|
||
$FW_RET="";
|
||
FW_pO '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" width="'.$size[0].'px" height="'.$size[1].'px">';
|
||
my $ma = Get($hash,("","text","MoonAge"));
|
||
my $mb = Get($hash,("","text","MoonPhaseS"));
|
||
|
||
my ($radius,$axis,$dir,$start,$middle);
|
||
$radius = 250;
|
||
$axis = sin(($ma+90)*$DEG)*$radius;
|
||
$axis = -$axis
|
||
if ($axis < 0);
|
||
|
||
if( (0.0 <= $ma && $ma <= 90) || (270.0 < $ma && $ma <= 360.0) ){
|
||
$dir = 1;
|
||
}else{
|
||
$dir = 0;
|
||
}
|
||
if( 0.0 < $ma && $ma <= 180 ){
|
||
$start = $radius;
|
||
$middle = -$radius;
|
||
}else{
|
||
$start = -$radius;
|
||
$middle = $radius;
|
||
}
|
||
|
||
FW_pO '<g transform="translate(400,400) scale(-1,1)">';
|
||
FW_pO '<circle cx="0" cy="0" r="250" fill="'.$moonshadow.'"/>';
|
||
FW_pO '<path d="M 0 '.$start.' A '.$axis.' '.$radius.' 0 0 '.$dir.' 0 '.$middle.' A '.$radius.' '.$radius.' 0 0 0 0 '.$start.' Z" fill="'.$mooncolor.'"/>';
|
||
FW_pO '</g>';
|
||
#FW_pO '<text x="100" y="710" style="font-family:Helvetica;font-size:60px;font-weight:bold" fill="black">'.$mb.'</text>';
|
||
FW_pO '</svg>';
|
||
return ($FW_RETTYPE, $FW_RET);
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# Update - Update readings
|
||
#
|
||
# Parameter hash = hash of the bus master
|
||
#
|
||
########################################################################################################
|
||
|
||
sub Update($@) {
|
||
my ($hash) = @_;
|
||
|
||
my $name = $hash->{NAME};
|
||
RemoveInternalTimer($hash);
|
||
delete $hash->{NEXTUPDATE};
|
||
|
||
return undef if (IsDisabled($name));
|
||
|
||
my $tz = AttrVal( $name, "timezone", AttrVal( "global", "timezone", undef ) );
|
||
my $lang = AttrVal( $name, "language", AttrVal( "global", "language", undef ) );
|
||
my $lc_time = AttrVal(
|
||
$name,
|
||
"lc_time",
|
||
AttrVal(
|
||
"global", "lc_time",
|
||
( $lang ? lc($lang) . "_" . uc($lang) . ".UTF-8" : undef )
|
||
)
|
||
);
|
||
my $now = gettimeofday(); # conserve timestamp before recomputing
|
||
|
||
SetTime(undef, $tz, $lc_time);
|
||
Compute($hash);
|
||
|
||
my @next;
|
||
|
||
#-- add regular update interval time
|
||
push @next, $now + $hash->{INTERVAL}
|
||
if ( defined( $hash->{INTERVAL} ) && $hash->{INTERVAL} > 0 );
|
||
|
||
#-- add event times
|
||
foreach my $comp (
|
||
defined( $hash->{RECOMPUTEAT} ) ? split( ',', $hash->{RECOMPUTEAT} ) : () )
|
||
{
|
||
if ( $comp eq 'NewDay' ) {
|
||
my @dch = localtime($now + 86400 + 3600*((localtime($now))[8] - (localtime($now + 86400.))[8]));
|
||
push @next,
|
||
_timelocal_modern( 0, 0, 0, $dch[3], $dch[4], $dch[5]+1900. );
|
||
next;
|
||
}
|
||
my $k = ".$comp";
|
||
next unless ( defined( $Astro{$k} ) && $Astro{$k} =~ /^\d+(?:\.\d+)?$/ );
|
||
my $t =
|
||
_timelocal_modern( 0, 0, 0, (localtime($now))[3,4], (localtime($now))[5]+1900. ) + $Astro{$k} * 3600.;
|
||
$t += 86400. if ( $t < $now ); # that is for tomorrow
|
||
push @next, $t;
|
||
}
|
||
|
||
#-- set timer for next update
|
||
if (@next) {
|
||
my $n = minNum( $next[0], @next );
|
||
$hash->{NEXTUPDATE} = FmtDateTime($n);
|
||
InternalTimer( $n, "FHEM::Astro::Update", $hash, 1 );
|
||
}
|
||
|
||
readingsBeginUpdate($hash);
|
||
foreach my $key (keys %Astro){
|
||
readingsBulkUpdateIfChanged($hash,$key,encode_utf8($Astro{$key}));
|
||
}
|
||
readingsEndUpdate($hash,1);
|
||
readingsSingleUpdate($hash,"state","Updated",1);
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# FormatReading - Convert a reading into text format
|
||
#
|
||
# Parameter r = reading name, h = parameter hash, locale = locale string, val = optional value
|
||
#
|
||
########################################################################################################
|
||
|
||
sub FormatReading($$;$$) {
|
||
my ( $r, $h, $lc_numeric, $val ) = @_;
|
||
my $ret;
|
||
$val = $Astro{$r} unless ( defined($val) );
|
||
|
||
my $f = "%s";
|
||
|
||
#-- number formatting
|
||
$f = "%2.1f" if ( $r eq "MoonAge" );
|
||
$f = "%2.1f" if ( $r eq "MoonAlt" );
|
||
$f = "%2.1f" if ( $r eq "MoonAz" );
|
||
$f = "%2.1f" if ( $r eq "MoonDec" );
|
||
$f = "%2.1f" if ( $r eq "MoonDiameter" );
|
||
$f = "%.0f" if ( $r eq "MoonDistance" );
|
||
$f = "%.0f" if ( $r eq "MoonDistanceObserver" );
|
||
$f = "%2.1f" if ( $r eq "MoonLat" );
|
||
$f = "%2.1f" if ( $r eq "MoonLon" );
|
||
$f = "%1.2f" if ( $r eq "MoonPhaseN" );
|
||
$f = "%.0f" if ( $r eq "ObsAlt" );
|
||
$f = "%d" if ( $r eq "ObsDayofyear" );
|
||
$f = "%2.1f" if ( $r eq "ObsHorEvening" );
|
||
$f = "%2.1f" if ( $r eq "ObsHorMorning" );
|
||
$f = "%.2f" if ( $r eq "ObsJD" );
|
||
$f = "%.5f" if ( $r eq "ObsLat" );
|
||
$f = "%.5f" if ( $r eq "ObsLon" );
|
||
$f = "%2d" if ( $r eq "ObsTimezone" );
|
||
$f = "%2.1f" if ( $r eq "SunAlt" );
|
||
$f = "%2.1f" if ( $r eq "SunAz" );
|
||
$f = "%2.1f" if ( $r eq "SunDec" );
|
||
$f = "%2.1f" if ( $r eq "SunDiameter" );
|
||
$f = "%.0f" if ( $r eq "SunDistance" );
|
||
$f = "%.0f" if ( $r eq "SunDistanceObserver" );
|
||
$f = "%2.1f" if ( $r eq "SunLon" );
|
||
|
||
$ret = $val ne "" ? sprintf( $f, $val ) : "";
|
||
$ret = UConv::decimal_mark( $ret, $lc_numeric )
|
||
unless ( $h && ref($h) && defined( $h->{html} ) && $h->{html} eq "0" );
|
||
|
||
$ret = ( $val == 1. ? $tt->{"dst"} : $tt->{"nt"} )
|
||
if ( $r eq "ObsIsDST" );
|
||
|
||
if ( $h && ref($h) && ( !$h->{html} || $h->{html} ne "0" ) ) {
|
||
|
||
#-- add unit if desired
|
||
if ( $h->{unit}
|
||
|| ( $h->{long} && ( !defined( $h->{unit} ) || $h->{unit} ne "0" ) ) )
|
||
{
|
||
$ret .= "°" if ( $r eq "MoonAge" );
|
||
$ret .= "°" if ( $r eq "MoonAlt" );
|
||
$ret .= "°" if ( $r eq "MoonAz" );
|
||
$ret .= "°" if ( $r eq "MoonDec" );
|
||
$ret .= "′" if ( $r eq "MoonDiameter" );
|
||
$ret .= chr(0x00A0) . "km" if ( $r eq "MoonDistance" );
|
||
$ret .= chr(0x00A0) . "km" if ( $r eq "MoonDistanceObserver" );
|
||
$ret .= chr(0x00A0) . "h" if ( $r eq "MoonHrsInvisible" );
|
||
$ret .= chr(0x00A0) . "h" if ( $r eq "MoonHrsVisible" );
|
||
$ret .= "°" if ( $r eq "MoonLat" );
|
||
$ret .= "°" if ( $r eq "MoonLon" );
|
||
$ret .= chr(0x00A0) . "h" if ( $r eq "MoonRa" );
|
||
$ret .= chr(0x00A0) . "m" if ( $r eq "ObsAlt" );
|
||
$ret .= "." if ( $r eq "ObsDayofyear" );
|
||
$ret .= "°" if ( $r eq "ObsHorEvening" );
|
||
$ret .= "°" if ( $r eq "ObsHorMorning" );
|
||
$ret .= chr(0x00A0) . $tt->{"days"} if ( $r eq "ObsJD" );
|
||
$ret .= "°" if ( $r eq "ObsLat" );
|
||
$ret .= "°" if ( $r eq "ObsLon" );
|
||
$ret .= "°" if ( $r eq "SunAlt" );
|
||
$ret .= "°" if ( $r eq "SunAz" );
|
||
$ret .= "°" if ( $r eq "SunDec" );
|
||
$ret .= "′" if ( $r eq "SunDiameter" );
|
||
$ret .= chr(0x00A0) . "km" if ( $r eq "SunDistance" );
|
||
$ret .= chr(0x00A0) . "km" if ( $r eq "SunDistanceObserver" );
|
||
$ret .= chr(0x00A0) . "h" if ( $r eq "SunHrsInvisible" );
|
||
$ret .= chr(0x00A0) . "h" if ( $r eq "SunHrsVisible" );
|
||
$ret .= "°" if ( $r eq "SunLon" );
|
||
$ret .= chr(0x00A0) . "h" if ( $r eq "SunRa" );
|
||
}
|
||
|
||
#-- add text if desired
|
||
if ( $h->{long} ) {
|
||
my $sep = " ";
|
||
$sep = ": "
|
||
if ( $h->{long} > 1. && ( $h->{long} < 4. || $r !~ /^Sun|Moon/ ) );
|
||
$sep = "" if ($ret eq "");
|
||
|
||
$ret = $tt->{"twilightastro"} . $sep . $ret
|
||
if ( $r eq "AstroTwilightEvening" );
|
||
$ret = $tt->{"twilightastro"} . $sep . $ret
|
||
if ( $r eq "AstroTwilightMorning" );
|
||
$ret = $tt->{"twilightcivil"} . $sep . $ret
|
||
if ( $r eq "CivilTwilightEvening" );
|
||
$ret = $tt->{"twilightcivil"} . $sep . $ret
|
||
if ( $r eq "CivilTwilightMorning" );
|
||
$ret = $tt->{"twilightcustom"} . $sep . $ret
|
||
if ( $r eq "CustomTwilightEvening" );
|
||
$ret = $tt->{"twilightcustom"} . $sep . $ret
|
||
if ( $r eq "CustomTwilightMorning" );
|
||
$ret = $tt->{"age"} . $sep . $ret if ( $r eq "MoonAge" );
|
||
$ret = $tt->{"alt"} . $sep . $ret if ( $r eq "MoonAlt" );
|
||
$ret = $tt->{"az"} . $sep . $ret if ( $r eq "MoonAz" );
|
||
$ret = $tt->{"dec"} . $sep . $ret if ( $r eq "MoonDec" );
|
||
$ret = $tt->{"diameter"} . $sep . $ret if ( $r eq "MoonDiameter" );
|
||
$ret = $ret . $sep . $tt->{"toce"}
|
||
if ( $r eq "MoonDistance" );
|
||
$ret = $ret . $sep . $tt->{"toobs"}
|
||
if ( $r eq "MoonDistanceObserver" );
|
||
$ret = $tt->{"hoursofvisibility"} . $sep . $ret
|
||
if ( $r eq "MoonHrsVisible" );
|
||
$ret = $tt->{"latecl"} . $sep . $ret if ( $r eq "MoonLat" );
|
||
$ret = $tt->{"lonecl"} . $sep . $ret if ( $r eq "MoonLon" );
|
||
$ret = $tt->{"phase"} . $sep . $ret if ( $r eq "MoonPhaseN" );
|
||
$ret = $tt->{"phase"} . $sep . $ret if ( $r eq "MoonPhaseS" );
|
||
$ret = $tt->{"ra"} . $sep . $ret if ( $r eq "MoonRa" );
|
||
$ret = $tt->{"rise"} . $sep . $ret if ( $r eq "MoonRise" );
|
||
$ret = $tt->{"set"} . $sep . $ret if ( $r eq "MoonSet" );
|
||
$ret = $tt->{"sign"} . $sep . $ret if ( $r eq "MoonSign" );
|
||
$ret = $tt->{"transit"} . $sep . $ret if ( $r eq "MoonTransit" );
|
||
$ret = $tt->{"twilightnautic"} . $sep . $ret
|
||
if ( $r eq "NauticTwilightEvening" );
|
||
$ret = $tt->{"twilightnautic"} . $sep . $ret
|
||
if ( $r eq "NauticTwilightMorning" );
|
||
$ret = $ret . $sep . $tt->{"altitude"} if ( $r eq "ObsAlt" );
|
||
$ret = $tt->{"date"} . $sep . $ret if ( $r eq "ObsDate" );
|
||
$ret = $ret . $sep . $tt->{"dayofyear"} if ( $r eq "ObsDayofyear" );
|
||
$ret = $tt->{"alt"} . $sep . $ret if ( $r eq "ObsHorEvening" );
|
||
$ret = $tt->{"alt"} . $sep . $ret if ( $r eq "ObsHorMorning" );
|
||
$ret = ( $val == 1. ? $tt->{"switchtodst"} : $tt->{"switchtont"} )
|
||
if ( $r eq "ObsIsDST" );
|
||
$ret = $tt->{"jdate"} . $sep . $ret if ( $r eq "ObsJD" );
|
||
$ret = $tt->{"lmst"} . $sep . $ret if ( $r eq "ObsLMST" );
|
||
$ret = $ret . $sep . $tt->{"latitude"} if ( $r eq "ObsLat" );
|
||
$ret = $ret . $sep . $tt->{"longitude"} if ( $r eq "ObsLon" );
|
||
$ret = $tt->{"season"} . $sep . $ret if ( $r eq "ObsSeason" );
|
||
$ret = $tt->{"time"} . $sep . $ret if ( $r eq "ObsTime" );
|
||
$ret = $tt->{"timezone"} . $sep . $ret if ( $r eq "ObsTimezone" );
|
||
$ret = $tt->{"alt"} . $sep . $ret if ( $r eq "SunAlt" );
|
||
$ret = $tt->{"az"} . $sep . $ret if ( $r eq "SunAz" );
|
||
$ret = $tt->{"dec"} . $sep . $ret if ( $r eq "SunDec" );
|
||
$ret = $tt->{"diameter"} . $sep . $ret if ( $r eq "SunDiameter" );
|
||
$ret = $ret . $sep . $tt->{"toce"}
|
||
if ( $r eq "SunDistance" );
|
||
$ret = $ret . $sep . $tt->{"toobs"}
|
||
if ( $r eq "SunDistanceObserver" );
|
||
$ret = $tt->{"hoursofnight"} . $sep . $ret
|
||
if ( $r eq "SunHrsInvisible" );
|
||
$ret = $tt->{"hoursofsunlight"} . $sep . $ret
|
||
if ( $r eq "SunHrsVisible" );
|
||
$ret = $tt->{"lonecl"} . $sep . $ret if ( $r eq "SunLon" );
|
||
$ret = $tt->{"ra"} . $sep . $ret if ( $r eq "SunRa" );
|
||
$ret = $tt->{"rise"} . $sep . $ret if ( $r eq "SunRise" );
|
||
$ret = $tt->{"set"} . $sep . $ret if ( $r eq "SunSet" );
|
||
$ret = $tt->{"sign"} . $sep . $ret if ( $r eq "SunSign" );
|
||
$ret = $tt->{"transit"} . $sep . $ret if ( $r eq "SunTransit" );
|
||
|
||
#-- add prefix for Sun/Moon if desired
|
||
if ( $h->{long} > 2. && $r =~ /^Sun|Moon/ ) {
|
||
$ret = " " . $ret;
|
||
|
||
#-- add a separator after prefix if desired
|
||
$ret = ":" . $ret if ( $h->{long} > 3. );
|
||
|
||
$ret = $tt->{"sun"} . $ret
|
||
if ( $r =~ /^Sun/ );
|
||
$ret = $tt->{"moon"} . $ret
|
||
if ( $r =~ /^Moon/ );
|
||
}
|
||
}
|
||
}
|
||
|
||
return encode_utf8($ret);
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# Set - Implements SetFn function
|
||
#
|
||
# Parameter hash = hash of device addressed, a = array of arguments, h = hash of parameters
|
||
#
|
||
########################################################################################################
|
||
|
||
sub Set($@) {
|
||
my ($hash,$a,$h) = @_;
|
||
|
||
my $name = shift @$a;
|
||
|
||
if ( $a->[0] eq "update" ) {
|
||
return "[FHEM::Astro::Set] $name is disabled" if ( IsDisabled($name) );
|
||
RemoveInternalTimer($hash);
|
||
InternalTimer( gettimeofday() + 1, "FHEM::Astro::Update", $hash, 1 );
|
||
}
|
||
else {
|
||
return
|
||
"[FHEM::Astro::Set] $name with unknown argument $a->[0], choose one of "
|
||
. join( " ",
|
||
map { defined( $sets{$_} ) ? "$_:$sets{$_}" : $_ }
|
||
sort keys %sets );
|
||
}
|
||
|
||
return "";
|
||
}
|
||
|
||
########################################################################################################
|
||
#
|
||
# Get - Implements GetFn function
|
||
#
|
||
# Parameter hash = hash of the bus master a = argument array
|
||
#
|
||
########################################################################################################
|
||
|
||
sub Get($@) {
|
||
my ($hash,$a,$h,@a) = @_;
|
||
my $name = "#APIcall";
|
||
my $type = "dummy";
|
||
|
||
#-- backwards compatibility for non-parseParams requests
|
||
if (!ref($a)) {
|
||
$hash = exists($defs{$hash}) ? $defs{$hash} : ()
|
||
if ($hash && !ref($hash));
|
||
unshift @a, $h;
|
||
$h = undef;
|
||
$type = $a;
|
||
$a = \@a;
|
||
}
|
||
else {
|
||
$type = shift @$a;
|
||
}
|
||
if (defined($hash->{NAME})) {
|
||
$name = $hash->{NAME};
|
||
} else {
|
||
$hash->{NAME} = $name;
|
||
}
|
||
|
||
my $wantsreading = 0;
|
||
my $dayOffset = 0;
|
||
my $now = gettimeofday();
|
||
my $html = defined( $hash->{CL} ) && $hash->{CL}{TYPE} eq "FHEMWEB" ? 1 : undef;
|
||
my $tz = AttrVal( $name, "timezone", AttrVal( "global", "timezone", undef ) );
|
||
my $lang = AttrVal( $name, "language", AttrVal( "global", "language", undef ) );
|
||
my $lc_numeric = AttrVal(
|
||
$name,
|
||
"lc_numeric",
|
||
AttrVal(
|
||
"global", "lc_numeric",
|
||
( $lang ? lc($lang) . "_" . uc($lang) . ".UTF-8" : undef )
|
||
)
|
||
);
|
||
my $lc_time = AttrVal(
|
||
$name,
|
||
"lc_time",
|
||
AttrVal(
|
||
"global", "lc_time",
|
||
( $lang ? lc($lang) . "_" . uc($lang) . ".UTF-8" : undef )
|
||
)
|
||
);
|
||
if ( $h && ref($h) ) {
|
||
$html = $h->{html} if ( defined( $h->{html} ) );
|
||
$tz = $h->{timezone} if ( defined( $h->{timezone} ) );
|
||
$lc_numeric = $h->{lc_numeric} if ( defined( $h->{lc_numeric} ) );
|
||
$lc_numeric = lc( $h->{language} ) . "_" . uc( $h->{language} ) . ".UTF-8"
|
||
if ( !$lc_numeric && defined( $h->{language} ) );
|
||
$lc_time = $h->{lc_time} if ( defined( $h->{lc_time} ) );
|
||
$lc_time = lc( $h->{language} ) . "_" . uc( $h->{language} ) . ".UTF-8"
|
||
if ( !$lc_time && defined( $h->{language} ) );
|
||
}
|
||
|
||
#-- fill %Astro if it is still empty after restart to avoid warnings
|
||
Compute($hash, $h) if (scalar keys %Astro == 0);
|
||
|
||
#-- second parameter may be one or many readings
|
||
my @readings;
|
||
if( (int(@$a)>1) ) {
|
||
@readings = split(',', $a->[1]);
|
||
foreach (@readings) {
|
||
if(exists($Astro{$_})) {
|
||
$wantsreading = 1;
|
||
last;
|
||
}
|
||
}
|
||
}
|
||
|
||
#-- last parameter may be indicating day offset
|
||
if(
|
||
(int(@$a)>4+$wantsreading && $a->[4+$wantsreading] =~ /^\+?([-+]\d+|yesterday|tomorrow)$/i) ||
|
||
(int(@$a)>3+$wantsreading && $a->[3+$wantsreading] =~ /^\+?([-+]\d+|yesterday|tomorrow)$/i) ||
|
||
(int(@$a)>2+$wantsreading && $a->[2+$wantsreading] =~ /^\+?([-+]\d+|yesterday|tomorrow)$/i) ||
|
||
(int(@$a)>1+$wantsreading && $a->[1+$wantsreading] =~ /^\+?([-+]\d+|yesterday|tomorrow)$/i)
|
||
){
|
||
$dayOffset = $1;
|
||
pop @$a;
|
||
$dayOffset = -1 if (lc($dayOffset) eq "yesterday");
|
||
$dayOffset = 1 if (lc($dayOffset) eq "tomorrow");
|
||
}
|
||
|
||
if( int(@$a) > (1+$wantsreading) ) {
|
||
my $str = (int(@$a) == (3+$wantsreading)) ? $a->[1+$wantsreading]." ".$a->[2+$wantsreading] : $a->[1+$wantsreading];
|
||
if( $str =~ /^(\d{2}):(\d{2})(?::(\d{2}))?$|^(?:(?:(\d{4})-)?(\d{2})-(\d{2}))(?:\D+(\d{2}):(\d{2})(?::(\d{2}))?)?$/){
|
||
return "[FHEM::Astro::Get] hours can only be between 00 and 23" if (defined($1) && $1 > 23.);
|
||
return "[FHEM::Astro::Get] minutes can only be between 00 and 59" if (defined($2) && $2 > 59.);
|
||
return "[FHEM::Astro::Get] seconds can only be between 00 and 59" if (defined($3) && $3 > 59.);
|
||
return "[FHEM::Astro::Get] month can only be between 01 and 12" if (defined($5) && ($5 > 12. || $5 < 1.));
|
||
return "[FHEM::Astro::Get] day can only be between 01 and 31" if (defined($6) && ($6 > 31. || $6 < 1.));
|
||
return "[FHEM::Astro::Get] hours can only be between 00 and 23" if (defined($7) && $7 > 23.);
|
||
return "[FHEM::Astro::Get] minutes can only be between 00 and 59" if (defined($8) && $8 > 59.);
|
||
return "[FHEM::Astro::Get] seconds can only be between 00 and 59" if (defined($9) && $9 > 59.);
|
||
|
||
SetTime(
|
||
_timelocal_modern(
|
||
defined($3) ? $3 : (defined($9) ? $9 : 0),
|
||
defined($2) ? $2 : (defined($8) ? $8 : 0),
|
||
defined($1) ? $1 : (defined($7) ? $7 : 12),
|
||
(defined($5)? ($6,$5-1.) : (localtime($now))[3,4]),
|
||
(defined($4)? $4 : (localtime($now))[5]+1900.),
|
||
) + ( $dayOffset * 86400. ), $tz, $lc_time
|
||
)
|
||
}else{
|
||
return "[FHEM::Astro::Get] $name has improper time specification $str, use [YYYY-]MM-DD [HH:MM[:SS]] [-1|yesterday|+1|tomorrow]";
|
||
}
|
||
}else{
|
||
SetTime($now + ($dayOffset * 86400.), $tz, $lc_time);
|
||
}
|
||
|
||
#-- disable automatic links to FHEM devices
|
||
delete $FW_webArgs{addLinks};
|
||
|
||
if( $a->[0] eq "version") {
|
||
return version->parse(FHEM::Astro::->VERSION())->normal;
|
||
|
||
}elsif( $a->[0] eq "json") {
|
||
Compute($hash, $h);
|
||
|
||
#-- beautify JSON at cost of performance only when debugging
|
||
if (ref($json) && AttrVal($name,"verbose",AttrVal("global","verbose",3)) > 3.) {
|
||
$json->canonical;
|
||
$json->pretty;
|
||
}
|
||
|
||
if( $wantsreading==1 ){
|
||
my %ret;
|
||
foreach (@readings) {
|
||
next unless(exists($Astro{$_}) && !ref($Astro{$_}));
|
||
if ($h && ref($h) && ($h->{text} || $h->{unit} || $h->{long})) {
|
||
$ret{text}{$_} = FormatReading($_, $h, $lc_numeric);
|
||
}
|
||
$ret{$_} = $Astro{$_};
|
||
}
|
||
return $json->encode(\%ret) if (ref($json));
|
||
return toJSON(\%ret);
|
||
}else{
|
||
if ($h && ref($h) && ($h->{text} || $h->{unit} || $h->{long})) {
|
||
foreach (keys %Astro) {
|
||
next if (ref($Astro{$_}) || $_ =~ /^\./);
|
||
$Astro{text}{$_} = FormatReading($_, $h, $lc_numeric);
|
||
}
|
||
}
|
||
return $json->encode(\%Astro) if (ref($json));
|
||
return toJSON(\%Astro);
|
||
}
|
||
|
||
}elsif( $a->[0] eq "text") {
|
||
Compute($hash, $h);
|
||
my $ret = "";
|
||
|
||
if ( $wantsreading==1 && $h && ref($h) && scalar keys %{$h} > 0 ) {
|
||
foreach (@readings) {
|
||
next if (!defined($Astro{$_}) || ref($Astro{$_}));
|
||
$ret .= $html && $html eq "1" ? "<br/>\n" : "\n"
|
||
if ( $ret ne "" );
|
||
$ret .= FormatReading( $_, $h, $lc_numeric ) unless($_ =~ /^\./);
|
||
$ret .= encode_utf8($Astro{$_}) if ($_ =~ /^\./);
|
||
}
|
||
$ret = "<html>" . $ret . "</html>" if (defined($html) && $html ne "0");
|
||
}
|
||
elsif ( $wantsreading==1 ) {
|
||
foreach (@readings) {
|
||
next if (!defined($Astro{$_}) || ref($Astro{$_}));
|
||
$ret .= $html && $html eq "1" ? "<br/>\n" : "\n"
|
||
if ( $ret ne "" );
|
||
$ret .= encode_utf8($Astro{$_});
|
||
}
|
||
$ret = "<html>" . $ret . "</html>" if (defined($html) && $html ne "0");
|
||
}
|
||
else {
|
||
$h->{long} = 1 unless ( defined( $h->{long} ) );
|
||
|
||
$ret = FormatReading( "ObsDate", $h, $lc_numeric ) . " " . $Astro{ObsTime};
|
||
$ret .= (
|
||
( $Astro{ObsIsDST} == 1 )
|
||
? " (" . FormatReading( "ObsIsDST", undef ) . ")"
|
||
: ""
|
||
);
|
||
$ret .= ", " . FormatReading( "ObsTimezone", $h ) . "\n";
|
||
$ret .= FormatReading( "ObsJD", $h, $lc_numeric ) . ", "
|
||
. FormatReading( "ObsDayofyear", $h, $lc_numeric ) . "\n";
|
||
$ret .= FormatReading( "ObsSeason", $h ) . "\n";
|
||
$ret .=
|
||
$tt->{"coord"} . ": "
|
||
. FormatReading( "ObsLon", $h, $lc_numeric ) . ", "
|
||
. FormatReading( "ObsLat", $h, $lc_numeric ) . ", "
|
||
. FormatReading( "ObsAlt", $h, $lc_numeric ) . "\n";
|
||
$ret .= FormatReading( "ObsLMST", $h ) . "\n\n";
|
||
|
||
$ret .= "\n" . $tt->{"sun"} . "\n";
|
||
$ret .=
|
||
FormatReading( "SunRise", $h ) . " "
|
||
. FormatReading( "SunSet", $h ) . " "
|
||
. FormatReading( "SunTransit", $h ) . "\n";
|
||
$ret .= FormatReading( "SunHrsVisible", $h ) . " "
|
||
. FormatReading( "SunHrsInvisible", $h ) . "\n";
|
||
$ret .= FormatReading( "CivilTwilightMorning", $h ) . " - "
|
||
. $Astro{CivilTwilightEvening} . "\n";
|
||
$ret .= FormatReading( "NauticTwilightMorning", $h ) . " - "
|
||
. $Astro{NauticTwilightEvening} . "\n";
|
||
$ret .= FormatReading( "AstroTwilightMorning", $h ) . " - "
|
||
. $Astro{AstroTwilightEvening} . "\n";
|
||
$ret .=
|
||
$tt->{"distance"} . ": "
|
||
. FormatReading( "SunDistance", $h, $lc_numeric ) . " ("
|
||
. FormatReading( "SunDistanceObserver", $h, $lc_numeric ) . ")\n";
|
||
$ret .=
|
||
$tt->{"position"} . ": "
|
||
. FormatReading( "SunLon", $h, $lc_numeric ) . ", "
|
||
. FormatReading( "SunRa", $h, $lc_numeric ) . ", "
|
||
. FormatReading( "SunDec", $h, $lc_numeric ) . "; "
|
||
. FormatReading( "SunAz", $h, $lc_numeric ) . ", "
|
||
. FormatReading( "SunAlt", $h, $lc_numeric ) . "\n";
|
||
$ret .= FormatReading( "SunDiameter", $h, $lc_numeric ) . ", "
|
||
. FormatReading( "SunSign", $h ) . "\n\n";
|
||
|
||
$ret .= "\n" . $tt->{"moon"} . "\n";
|
||
$ret .=
|
||
FormatReading( "MoonRise", $h ) . " "
|
||
. FormatReading( "MoonSet", $h ) . " "
|
||
. FormatReading( "MoonTransit", $h ) . "\n";
|
||
$ret .= FormatReading( "MoonHrsVisible", $h ) . "\n";
|
||
$ret .=
|
||
$tt->{"distance"} . ": "
|
||
. FormatReading( "MoonDistance", $h, $lc_numeric ) . " ("
|
||
. FormatReading( "MoonDistanceObserver", $h, $lc_numeric ) . ")\n";
|
||
$ret .=
|
||
$tt->{"position"} . ": "
|
||
. FormatReading( "MoonLon", $h, $lc_numeric ) . ", "
|
||
. FormatReading( "MoonLat", $h, $lc_numeric ) . "; "
|
||
. FormatReading( "MoonRa", $h, $lc_numeric ) . ", "
|
||
. FormatReading( "MoonDec", $h, $lc_numeric ) . ", "
|
||
. FormatReading( "MoonAz", $h, $lc_numeric ) . ", "
|
||
. FormatReading( "MoonAlt", $h, $lc_numeric ) . "\n";
|
||
$ret .=
|
||
FormatReading( "MoonDiameter", $h, $lc_numeric ) . ", "
|
||
. FormatReading( "MoonAge", $h, $lc_numeric ) . ", "
|
||
. FormatReading( "MoonPhaseN", $h, $lc_numeric ) . " = "
|
||
. $Astro{MoonPhaseS} . ", "
|
||
. FormatReading( "MoonSign", $h );
|
||
|
||
if ($html && $html eq "1") {
|
||
$ret = "<html>" . $ret . "</html>";
|
||
$ret =~ s/ / /g;
|
||
$ret =~ s/ / /g;
|
||
$ret =~ s/\n/<br\/>\n/g;
|
||
}
|
||
}
|
||
|
||
return $ret;
|
||
}else {
|
||
return "[FHEM::Astro::Get] $name with unknown argument $a->[0], choose one of ".
|
||
join(" ", map { defined($gets{$_})?"$_:$gets{$_}":$_ } sort keys %gets);
|
||
}
|
||
}
|
||
|
||
1;
|
||
|
||
=pod
|
||
=encoding utf8
|
||
=item helper
|
||
=item summary collection of various routines for astronomical data
|
||
=item summary_DE Sammlung verschiedener Routinen für astronomische Daten
|
||
=begin html
|
||
|
||
<a name="Astro"></a>
|
||
<h3>Astro</h3>
|
||
<ul>
|
||
<p> FHEM module with a collection of various routines for astronomical data</p>
|
||
<a name="Astrodefine"></a>
|
||
<h4>Define</h4>
|
||
<p>
|
||
<code>define <name> Astro [global]</code>
|
||
<br />Defines the Astro device (only one is needed per FHEM installation).
|
||
<br />
|
||
Optional parameter 'global' will mark this device for global configuration options, see SUNRISE_EL compatibility mode below for details.</p>
|
||
<p>
|
||
Readings with prefix <i>Sun</i> refer to the sun, with prefix <i>Moon</i> refer to the moon.
|
||
The suffixes for these readings are:
|
||
<ul>
|
||
<li><i>Age</i> = angle (in degrees) of body along its track</li>
|
||
<li><i>Az,Alt</i> = azimuth and altitude angle (in degrees) of body above horizon</li>
|
||
<li><i>Dec,Ra</i> = declination (in degrees) and right ascension (in HH:MM) of body position</li>
|
||
<li><i>HrsVisible,HrsInvisible</i> = Hours of visiblity and invisiblity of the body</li>
|
||
<li><i>Lat,Lon</i> = latitude and longituds (in degrees) of body position</li>
|
||
<li><i>Diameter</i> = virtual diameter (in arc minutes) of body</li>
|
||
<li><i>Distance,DistanceObserver</i> = distance (in km) of body to center of earth or to observer</li>
|
||
<li><i>PhaseN,PhaseS</i> = Numerical and string value for phase of body</li>
|
||
<li><i>Sign,SignN</i> = Circadian sign for body along its track</li>
|
||
<li><i>Rise,Transit,Set</i> = times (in HH:MM) for rise and set as well as for highest position of body</li>
|
||
</ul>
|
||
<p>
|
||
Readings with prefix <i>Obs</i> refer to the observer.
|
||
In addition to some of the suffixes gives above, the following may occur:
|
||
<ul>
|
||
<li><i>Date,Dayofyear</i> = date</li>
|
||
<li><i>JD</i> = Julian date</li>
|
||
<li><i>Time,Timezone,TimezoneS</i> = obvious meaning</li>
|
||
<li><i>IsDST</i> = 1 if running on daylight savings time, 0 otherwise</li>
|
||
<li><i>GMST,LMST</i> = Greenwich and Local Mean Sidereal Time (in HH:MM)</li>
|
||
</ul>
|
||
<p>
|
||
An SVG image of the current moon phase may be obtained under the link
|
||
<code><ip address of fhem>/fhem/Astro_moonwidget?name='<device name>'</code>.
|
||
Optional web parameters are <code>[&size='<width>x<height>'][&mooncolor=<color>][&moonshadow=<color>]</code>
|
||
<p>
|
||
Notes: <ul>
|
||
<li>Calculations are only valid between the years 1900 and 2100</li>
|
||
<li>Attention: Timezone is taken from the local Perl settings, NOT automatically defined for a location</li>
|
||
<li>This module uses the global attribute <code>language</code> to determine its output data<br/>
|
||
(default: EN=english). For German output, set <code>attr global language DE</code>.<br/>
|
||
If a local attribute on the device was set for language it will take precedence.</li>
|
||
<li>The time zone is determined automatically from the local settings of the <br/>
|
||
operating system. If geocordinates from a different time zone are used, the results are<br/>
|
||
not corrected automatically.</li>
|
||
<li>Some definitions determining the observer position are used<br/>
|
||
from the global device, i.e.<br/>
|
||
<ul>
|
||
<code>attr global longitude <value></code><br/>
|
||
<code>attr global latitude <value></code><br/>
|
||
<code>attr global altitude <value></code> (in m above sea level)
|
||
</ul>
|
||
These definitions are only used when there are no corresponding local attribute settings on the device.
|
||
</li>
|
||
<li>
|
||
It is not necessary to define an Astro device to use the data provided by this module.<br/>
|
||
To use its data in any other module, you just need to put <code>LoadModule("Astro");</code> <br/>
|
||
at the start of your own code, and then may call, for example, the function<br/>
|
||
<ul><code>Astro_Get( SOME_HASH_REFERENCE,"dummy","text", "SunRise","2019-12-24");</code></ul>
|
||
to acquire the sunrise on Christmas Eve 2019. The hash reference may also be undefined or an existing device name of any type. Note that device attributes of the respective device will be respected as long as their name matches those mentioned for an Astro device.
|
||
attribute=value pairs may be added in text format to enforce
|
||
settings like language that would otherwise be defined by a real device.<br/>
|
||
You may also add parameters to customize your request:<br/>
|
||
<ul><code>Astro_Get( SOME_HASH_REFERENCE, ["dummy","text"], {html=>1} );</code><br/>
|
||
<code>Astro_Get( SOME_HASH_REFERENCE, ["dummy","text","SunRise,SunSet,SunAz,SunDistanceObserver"], {html=>1, long=>2} );</code></ul>
|
||
</li>
|
||
<li>Functions that can replace those from <a href="#SUNRISE_EL">SUNRISE_EL</a> are available under the same name and adding <i>a</i> as a prefix in front of it (for example, use <code>asunrise()</code> instead of <code>sunrise()</code>). A single Astro device can act as a global configuration device for such functions if it was created using the <i>global</i> define parameter. If you don't want to create any Astro device at all, you may put <code>LoadModule("Astro");</code> into your 99_myUtils.pm to only load the SUNRISE_EL replacement functions.
|
||
</li>
|
||
</ul>
|
||
<a name="Astroset"></a>
|
||
<h4>Set</h4>
|
||
<ul>
|
||
<li><a name="Astro_update"></a>
|
||
<code>set <name> update</code>
|
||
<br />trigger to recompute values immediately.</li>
|
||
</ul>
|
||
<a name="Astroget"></a>
|
||
<h4>Get</h4>
|
||
Attention: Get-calls are NOT written into the readings of the device. Readings change only through periodic updates.<br/>
|
||
<ul>
|
||
<li><a name="Astro_json"></a>
|
||
<code>get <name> json [<reading>,[<reading>]] [-1|yesterday|+1|tomorrow]</code><br/>
|
||
<code>get <name> json [<reading>,[<reading>]] MM-DD [-1|yesterday|+1|tomorrow]</code><br/>
|
||
<code>get <name> json [<reading>,[<reading>]] YYYY-MM-DD [-1|yesterday|+1|tomorrow]</code><br/>
|
||
<code>get <name> json [<reading>,[<reading>]] HH:MM[:SS] [-1|yesterday|+1|tomorrow]</code><br/>
|
||
<code>get <name> json [<reading>,[<reading>]] YYYY-MM-DD HH:MM[:SS] [-1|yesterday|+1|tomorrow]</code>
|
||
<br />returns the complete set of an individual reading of astronomical data either for the current time, or for a day and time given in the argument. <code>yesterday</code>, <code>tomorrow</code> or any other integer number may be given at the end to get data relative to the given day and time.<br/>
|
||
Formatted values as described below may be generated in a subtree <code>text</code> by adding <code>text=1</code> to the request.</li>
|
||
<li><a name="Astro_text"></a>
|
||
<code>get <name> text [<reading>,[<reading>]] [-1|yesterday|+1|tomorrow]</code><br/>
|
||
<code>get <name> text [<reading>,[<reading>]] MM-DD [-1|yesterday|+1|tomorrow]</code><br/>
|
||
<code>get <name> text [<reading>,[<reading>]] YYYY-MM-DD [-1|yesterday|+1|tomorrow]</code><br/>
|
||
<code>get <name> text [<reading>,[<reading>]] HH:MM[:SS] [-1|yesterday|+1|tomorrow]</code><br/>
|
||
<code>get <name> text [<reading>,[<reading>]] YYYY-MM-DD HH:MM[:SS] [-1|yesterday|+1|tomorrow]</code>
|
||
<br />returns the complete set of an individual reading of astronomical data either for the current time, or for a day and time given in the argument. <code>yesterday</code>, <code>tomorrow</code> or any other integer number may be given at the end to get data relative to the given day and time.<br/>
|
||
The return value may be formatted and/or labeled by adding one or more of the following key=value pairs to the request:
|
||
<ul>
|
||
<li><i>unit=1</i> = Will add a unit to numerical values. Depending on attribute lc_numeric, the decimal separator will be in regional format as well.</li>
|
||
<li><i>long=1</i> = A describtive label will be added to the value.</li>
|
||
<li><i>long=2</i> = Same as long=1 but the label will be separated from the value by a colon.</li>
|
||
<li><i>long=3</i> = Same as long=2 but Sun or Moon will be added as a prefix.</li>
|
||
<li><i>long=4</i> = Same as long=3 but the separator will be used to separate Sun/Moon instead.</li>
|
||
</ul></li>
|
||
<li><a name="Astro_version"></a>
|
||
<code>get <name> version</code>
|
||
<br />Display the version of the module</li>
|
||
</ul>
|
||
<a name="Astroattr"></a>
|
||
<h4>Attributes</h4>
|
||
<ul>
|
||
<li><a name="Astro_interval"></a>
|
||
<code><interval></code>
|
||
<br />Update interval in seconds. The default is 3600 seconds, a value of 0 disables the periodic update.</li>
|
||
<li><a name="Astro_language"></a>
|
||
<code><language></code>
|
||
<br />A language may be set to overwrite global attribute settings.</li>
|
||
<li><a name="Astro_lc_numeric"></a>
|
||
<code><lc_numeric></code>
|
||
<br />Set regional settings to format numerical values in textual output. If not set, it will generate the locale based on the attribute <i>language</i> (if set).</li>
|
||
<li><a name="Astro_lc_time"></a>
|
||
<code><lc_time></code>
|
||
<br />Set regional settings to format time related values in textual output. If not set, it will generate the locale based on the attribute <i>language</i> (if set).</li>
|
||
<li><a name="Astro_recomputeAt"></a>
|
||
<code><recomputeAt></code>
|
||
<br />Enforce recomputing values at specific event times, independant from update interval. This attribute contains a list of one or many of the following values:<br />
|
||
<ul>
|
||
<li><i>MoonRise,MoonSet,MoonTransit</i> = for moon rise, set, and transit</li>
|
||
<li><i>NewDay</i> = for 00:00:00 hours of the next calendar day</li>
|
||
<li><i>SunRise,SunSet,SunTransit</i> = for sun rise, set, and transit</li>
|
||
<li><i>*TwilightEvening,*TwilightMorning</i> = for the respective twilight stage begin</li>
|
||
</ul></li>
|
||
<li><a name="Astro_timezone"></a>
|
||
<code><timezone></code>
|
||
<br />A timezone may be set to overwrite global and system settings. Format may depend on your local system implementation but is likely in the format similar to <code>Europe/Berlin</code>.</li>
|
||
<li>Some definitions determining the observer position:<br/>
|
||
<ul>
|
||
<code>attr <name> longitude <value></code><br/>
|
||
<code>attr <name> latitude <value></code><br/>
|
||
<code>attr <name> altitude <value></code> (in m above sea level)<br/>
|
||
<code>attr <name> horizon <value></code> custom horizon angle in degrees, default 0. Different values for morning/evening may be set as <code><morning>:<evening></code>
|
||
</ul>
|
||
These definitions take precedence over global attribute settings.</li>
|
||
<li><a name="Astro_disable"></a>
|
||
<code><disable></code>
|
||
<br />When set, this will completely disable any device update.</li>
|
||
<li>Standard attributes <a href="#alias">alias</a>, <a href="#comment">comment</a>, <a
|
||
href="#event-on-update-reading">event-on-update-reading</a>, <a
|
||
href="#event-on-change-reading">event-on-change-reading</a>, <a href="#room"
|
||
>room</a>, <a href="#eventMap">eventMap</a>, <a href="#loglevel">loglevel</a>,
|
||
<a href="#webCmd">webCmd</a></li>
|
||
</ul>
|
||
</ul>
|
||
=end html
|
||
=begin html_DE
|
||
|
||
<a name="Astro"></a>
|
||
<h3>Astro</h3>
|
||
<ul>
|
||
<a href="https://wiki.fhem.de/wiki/Modul_Astro">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="commandref.html#Astro">Astro</a>
|
||
</ul>
|
||
=end html_DE
|
||
=for :application/json;q=META.json 95_Astro.pm
|
||
{
|
||
"version": "v2.2",
|
||
"author": [
|
||
"Prof. Dr. Peter A. Henning <>",
|
||
"Julian Pawlowski <>",
|
||
"Marko Oldenburg <>"
|
||
],
|
||
"x_fhem_maintainer": [
|
||
"pahenning",
|
||
"loredo",
|
||
"CoolTux"
|
||
],
|
||
"resources": {
|
||
"x_wiki": {
|
||
"web": "https://wiki.fhem.de/wiki/Modul_Astro"
|
||
}
|
||
},
|
||
"keywords": [
|
||
"astrology",
|
||
"astronomy",
|
||
"constellation",
|
||
"moon",
|
||
"moonrise",
|
||
"moonset",
|
||
"sun",
|
||
"sunrise",
|
||
"sunset",
|
||
"star sign",
|
||
"twilight",
|
||
"zodiac",
|
||
"Astrologie",
|
||
"Astronomie",
|
||
"Mond",
|
||
"Mondaufgang",
|
||
"Monduntergang",
|
||
"Sonne",
|
||
"Sternbild",
|
||
"Sternzeichen",
|
||
"Sonnenaufgang",
|
||
"Sonnenuntergang",
|
||
"Tierkreiszeichen",
|
||
"Zodiak"
|
||
],
|
||
"prereqs": {
|
||
"runtime": {
|
||
"requires": {
|
||
"Encode": 0,
|
||
"GPUtils": 0,
|
||
"Math::Trig": 0,
|
||
"POSIX": 0,
|
||
"Time::HiRes": 0,
|
||
"Time::Local": 0,
|
||
"strict": 0,
|
||
"utf8": 0,
|
||
"warnings": 0
|
||
},
|
||
"recommends": {
|
||
"JSON": 0
|
||
},
|
||
"suggests": {
|
||
"Cpanel::JSON::XS": 0,
|
||
"JSON::XS": 0
|
||
}
|
||
}
|
||
}
|
||
}
|
||
=end :application/json;q=META.json
|
||
=cut
|