New Plugin: Sun position

master
Michael Zanetti 2020-09-04 02:07:31 +02:00
parent 34ac58feaa
commit bd510d11d2
13 changed files with 1178 additions and 1 deletions

16
debian/control vendored
View File

@ -889,6 +889,22 @@ Description: nymea.io plugin for snapd
This package will install the nymea.io plugin for snapd
Package: nymea-plugin-sunposition
Architecture: any
Depends: ${misc:Depends},
libatlas-base-dev,
python3-pip,
nymea-plugins-translations,
Description: nymea.io plugin for calculating the sun position
The nymea daemon is a plugin based IoT (Internet of Things) server. The
server works like a translator for devices, things and services and
allows them to interact.
With the powerful rule engine you are able to connect any device available
in the system and create individual scenes and behaviors for your environment.
.
This package will install the nymea.io plugin for calculating the sun position
Package: nymea-plugin-keba
Architecture: any
Depends: ${shlibs:Depends},

View File

@ -0,0 +1,4 @@
sunposition/integrationpluginsunposition.json usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/sunposition/
sunposition/integrationpluginsunposition.py usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/sunposition/
sunposition/sunposition.py usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/sunposition/
sunposition/requirements.txt usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/sunposition/

11
debian/rules vendored
View File

@ -6,6 +6,15 @@ PREPROCESS_FILES := $(wildcard debian/*.in)
$(PREPROCESS_FILES:.in=): %: %.in
sed 's,/@DEB_HOST_MULTIARCH@,$(DEB_HOST_MULTIARCH:%=/%),g' $< > $@
chmod --reference=$< $@
# Auto-generate install/remove hooks to fetch/cleanup python dependencies from requirements.txt
PYTHON_REQUIREMENTS := $(shell find -name requirements.txt)
$(PYTHON_REQUIREMENTS:.txt=): %: %.txt
$(eval PYTHON_PLUGIN=$(subst /requirements.txt,,$<))
echo "#DEBHELPER#" | tee debian/nymea-plugin-$(PYTHON_PLUGIN).postinst | tee debian/nymea-plugin-$(PYTHON_PLUGIN).prerm
echo "pip3 install --require-hashes --upgrade -r usr/lib/$(DEB_HOST_MULTIARCH)/nymea/plugins/$(PYTHON_PLUGIN)/requirements.txt -t usr/lib/$(DEB_HOST_MULTIARCH)/nymea/plugins/$(PYTHON_PLUGIN)/modules" >> debian/nymea-plugin-$(PYTHON_PLUGIN).postinst
echo "rm -rf usr/lib/$(DEB_HOST_MULTIARCH)/nymea/plugins/$(PYTHON_PLUGIN)/modules" >> debian/nymea-plugin-$(PYTHON_PLUGIN).prerm
%:
dh $@ --parallel
@ -14,7 +23,7 @@ override_dh_auto_build:
dh_auto_build
make lrelease
override_dh_install: $(PREPROCESS_FILES:.in=)
override_dh_install: $(PREPROCESS_FILES:.in=) $(PYTHON_REQUIREMENTS:.txt=)
dh_install --fail-missing
override_dh_auto_clean:

View File

@ -49,6 +49,7 @@ PLUGIN_DIRS = \
simulation \
snapd \
sonos \
sunposition \
tado \
tasmota \
tcpcommander \

5
sunposition/README.md Normal file
View File

@ -0,0 +1,5 @@
# Sunposition
This plugin calculates the current angle to the sun, depending on the local position.
The main purpose of this is for shading related automations.

View File

@ -0,0 +1,47 @@
{
"id": "30ff7e8f-4c67-4a8a-8b49-94c64bcbbe8a2",
"name": "sunPosition",
"displayName": "Sun Position",
"vendors": [
{
"id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6",
"name": "nymea",
"displayName": "nymea GmbH",
"thingClasses": [
{
"id": "a79bc8cb-89bc-4227-9b7c-70130d26ad34",
"name": "sunPosition",
"displayName": "Sun Position",
"createMethods": ["discovery", "user"],
"paramTypes": [
{
"id": "cc321533-787a-4fba-9abe-7b73e588ac5d",
"name": "latitude",
"displayName": "Latitude",
"type": "double",
"defaultValue": 0
},
{
"id": "1b0bbf53-634a-42ed-b3b3-c058ef1e015c",
"name": "longitude",
"displayName": "Longitude",
"type": "double",
"defaultValue": 0
}
],
"stateTypes": [
{
"id": "5c2f5149-5152-4e0e-b372-3d3edd302306",
"name": "angle",
"displayName": "Sun angle",
"displayNameEvent": "Sun angle changed",
"type": "double",
"unit": "Degree",
"defaultValue": 0
}
]
}
]
}
]
}

View File

@ -0,0 +1,67 @@
# Copyright 2013 - 2020, nymea GmbH
# Contact: contact@nymea.io
#
# This file is part of nymea.
# This project including source code and documentation is protected by
# copyright law, and remains the property of nymea GmbH. All rights, including
# reproduction, publication, editing and translation, are reserved. The use of
# this project is subject to the terms of a license agreement to be concluded
# with nymea GmbH in accordance with the terms of use of nymea GmbH, available
# under https://nymea.io/license
#
# GNU Lesser General Public License Usage
# Alternatively, this project may be redistributed and/or modified under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; version 3. This project 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this project. If not, see <https://www.gnu.org/licenses/>.
#
# For any further details and any questions please contact us under
# contact@nymea.io or see our FAQ/Licensing Information on
# https://nymea.io/license/faq
import nymea
from sunposition import sunpos
from datetime import datetime
from urllib.request import urlopen, Request
import simplejson
import time
loopRunning = False
def init():
global loopRunning
loopRunning = True
while loopRunning:
time.sleep(5)
now = datetime.utcnow()
for thing in myThings():
lat = thing.paramValue(sunPositionThingLatitudeParamTypeId)
lon = thing.paramValue(sunPositionThingLongitudeParamTypeId)
az,zen = sunpos(now, lat, lon, 0)[:2]
angle = 90 - zen.item()
logger.log("Updating thing", thing.name, "Angle:", angle)
thing.setStateValue(sunPositionAngleStateTypeId, angle)
def deinit():
global loopRunning
loopRunning = False
def discoverThings(info):
request = Request("http://ip-api.com/json")
data = simplejson.load(urlopen(request))
descriptor = nymea.ThingDescriptor(sunPositionThingClassId, "%s - %s" % (data["city"], data["country"]), "%s - %s" % (data["lat"], data["lon"]))
descriptor.params = [nymea.Param(sunPositionThingLatitudeParamTypeId, data["lat"]), nymea.Param(sunPositionThingLongitudeParamTypeId, data["lon"])]
info.addDescriptor(descriptor)
info.finish(nymea.ThingErrorNoError)
def setupThing(info):
info.finish(nymea.ThingErrorNoError)

11
sunposition/meta.json Normal file
View File

@ -0,0 +1,11 @@
{
"title": "Sunposition",
"tagline": "Calculate the current angle to the sun",
"icon": "sunpos.svg",
"stability": "consumer",
"offline": false,
"technologies": [
],
"categories": [
]
}

View File

@ -0,0 +1,38 @@
numpy==1.19.1 \
--hash=sha256:082f8d4dd69b6b688f64f509b91d482362124986d98dc7dc5f5e9f9b9c3bb983 \
--hash=sha256:1bc0145999e8cb8aed9d4e65dd8b139adf1919e521177f198529687dbf613065 \
--hash=sha256:309cbcfaa103fc9a33ec16d2d62569d541b79f828c382556ff072442226d1968 \
--hash=sha256:3673c8b2b29077f1b7b3a848794f8e11f401ba0b71c49fbd26fb40b71788b132 \
--hash=sha256:480fdd4dbda4dd6b638d3863da3be82873bba6d32d1fc12ea1b8486ac7b8d129 \
--hash=sha256:56ef7f56470c24bb67fb43dae442e946a6ce172f97c69f8d067ff8550cf782ff \
--hash=sha256:5a936fd51049541d86ccdeef2833cc89a18e4d3808fe58a8abeb802665c5af93 \
--hash=sha256:5b6885c12784a27e957294b60f97e8b5b4174c7504665333c5e94fbf41ae5d6a \
--hash=sha256:667c07063940e934287993366ad5f56766bc009017b4a0fe91dbd07960d0aba7 \
--hash=sha256:7ed448ff4eaffeb01094959b19cbaf998ecdee9ef9932381420d514e446601cd \
--hash=sha256:8343bf67c72e09cfabfab55ad4a43ce3f6bf6e6ced7acf70f45ded9ebb425055 \
--hash=sha256:92feb989b47f83ebef246adabc7ff3b9a59ac30601c3f6819f8913458610bdcc \
--hash=sha256:935c27ae2760c21cd7354402546f6be21d3d0c806fffe967f745d5f2de5005a7 \
--hash=sha256:aaf42a04b472d12515debc621c31cf16c215e332242e7a9f56403d814c744624 \
--hash=sha256:b12e639378c741add21fbffd16ba5ad25c0a1a17cf2b6fe4288feeb65144f35b \
--hash=sha256:b1cca51512299841bf69add3b75361779962f9cee7d9ee3bb446d5982e925b69 \
--hash=sha256:b8456987b637232602ceb4d663cb34106f7eb780e247d51a260b84760fd8f491 \
--hash=sha256:b9792b0ac0130b277536ab8944e7b754c69560dac0415dd4b2dbd16b902c8954 \
--hash=sha256:c9591886fc9cbe5532d5df85cb8e0cc3b44ba8ce4367bd4cf1b93dc19713da72 \
--hash=sha256:cf1347450c0b7644ea142712619533553f02ef23f92f781312f6a3553d031fc7 \
--hash=sha256:de8b4a9b56255797cbddb93281ed92acbc510fb7b15df3f01bd28f46ebc4edae \
--hash=sha256:e1b1dc0372f530f26a03578ac75d5e51b3868b9b76cd2facba4c9ee0eb252ab1 \
--hash=sha256:e45f8e981a0ab47103181773cc0a54e650b2aef8c7b6cd07405d0fa8d869444a \
--hash=sha256:e4f6d3c53911a9d103d8ec9518190e52a8b945bab021745af4939cfc7c0d4a9e \
--hash=sha256:ed8a311493cf5480a2ebc597d1e177231984c818a86875126cfd004241a73c3e \
--hash=sha256:ef71a1d4fd4858596ae80ad1ec76404ad29701f8ca7cdcebc50300178db14dfc \
--hash=sha256:391b47dd55a692bc40b525b049babf82edc6d277ec154624cf3c2bcf3d25bbb3
simplejson==3.17.2 \
--hash=sha256:4b3442249d5e3893b90cb9f72c7d6ce4d2ea144d2c0d9f75b9ae1e5460f3121a \
--hash=sha256:5c659a0efc80aaaba57fcd878855c8534ecb655a28ac8508885c50648e6e659d \
--hash=sha256:72d8a3ffca19a901002d6b068cf746be85747571c6a7ba12cbcf427bfb4ed971 \
--hash=sha256:75ecc79f26d99222a084fbdd1ce5aad3ac3a8bd535cd9059528452da38b68841 \
--hash=sha256:869a183c8e44bc03be1b2bbcc9ec4338e37fa8557fc506bf6115887c1d3bb956 \
--hash=sha256:8acf76443cfb5c949b6e781c154278c059b09ac717d2757a830c869ba000cf8d \
--hash=sha256:934115642c8ba9659b402c8bdbdedb48651fb94b576e3b3efd1ccb079609b04a \
--hash=sha256:e058c7656c44fb494a11443191e381355388443d543f6fc1a245d5d238544396 \
--hash=sha256:7ae6e6af2dc617d200fcebc943059ef7e60ddc4de49deb7317f0183ba5bdcdf6

280
sunposition/sunpos.svg Normal file
View File

@ -0,0 +1,280 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="96"
height="96"
id="svg4874"
version="1.1"
inkscape:version="0.91+devel r"
viewBox="0 0 96 96.000001"
sodipodi:docname="weather-clear-symbolic.svg">
<defs
id="defs4876" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.5967996"
inkscape:cx="-44.233777"
inkscape:cy="74.955491"
inkscape:document-units="px"
inkscape:current-layer="g4780"
showgrid="true"
showborder="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-global="true">
<inkscape:grid
type="xygrid"
id="grid5451"
empspacing="8" />
<sodipodi:guide
orientation="1,0"
position="8,-8.0000001"
id="guide4063" />
<sodipodi:guide
orientation="1,0"
position="4,-8.0000001"
id="guide4065" />
<sodipodi:guide
orientation="0,1"
position="-8,88.000001"
id="guide4067" />
<sodipodi:guide
orientation="0,1"
position="-8,92.000001"
id="guide4069" />
<sodipodi:guide
orientation="0,1"
position="104,4"
id="guide4071" />
<sodipodi:guide
orientation="0,1"
position="-5,8.0000001"
id="guide4073" />
<sodipodi:guide
orientation="1,0"
position="88,-8.0000001"
id="guide4077" />
<sodipodi:guide
orientation="0,1"
position="-8,84.000001"
id="guide4074" />
<sodipodi:guide
orientation="1,0"
position="12,-8.0000001"
id="guide4076" />
<sodipodi:guide
orientation="1,0"
position="84,-8.0000001"
id="guide4080" />
<sodipodi:guide
position="48,-8.0000001"
orientation="1,0"
id="guide4170" />
<sodipodi:guide
position="-8,48"
orientation="0,1"
id="guide4172" />
<sodipodi:guide
position="92,-8.0000001"
orientation="1,0"
id="guide4760" />
</sodipodi:namedview>
<metadata
id="metadata4879">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(67.857146,-78.50504)">
<g
transform="matrix(0,-1,-1,0,373.50506,516.50504)"
id="g4845"
style="display:inline">
<g
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="next01.png"
transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)"
id="g4778"
inkscape:label="Layer 1">
<g
transform="matrix(-1,0,0,1,575.99999,611)"
id="g4780"
style="display:inline">
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 417.99414,393.36133 c 0,15.44069 -12.56425,28 -28.00976,28 -15.44552,0 -28.01172,-12.55931 -28.01172,-28 0,-15.44069 12.5662,-28 28.01172,-28 15.44551,0 28.00976,12.55931 28.00976,28 z m -4.00195,0 c 0,-13.27765 -10.72363,-23.99805 -24.00781,-23.99805 -13.28419,0 -24.00977,10.7204 -24.00977,23.99805 0,13.27764 10.72558,24 24.00977,24 13.28418,0 24.00781,-10.72236 24.00781,-24 z"
id="path4187"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate"
id="rect4782"
width="96.037987"
height="96"
x="-438.00244"
y="345.36221"
transform="scale(-1,1)" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6;marker:none;enable-background:accumulate"
d="m 381.56406,421.97322 a 34.712014,34.698285 0 0 1 -2.39786,-0.56823 34.712014,34.698285 0 0 0 2.39786,0.56823 z"
id="path4162" />
<rect
inkscape:transform-center-y="-32.042929"
transform="matrix(-0.50014835,-0.86593974,-0.86611103,0.49985167,0,0)"
y="-182.97517"
x="-537.62946"
height="10.002968"
width="4.0003901"
id="rect4198"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:transform-center-x="-18.5" />
<rect
inkscape:transform-center-x="-32.042931"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4200"
width="4.0011816"
height="10.00099"
x="-536.44226"
y="103.75733"
transform="matrix(-0.86611103,-0.49985167,-0.50014835,0.86593974,0,0)"
inkscape:transform-center-y="-18.499998" />
<rect
transform="scale(-1,1)"
y="351.36221"
x="-391.98425"
height="10.000001"
width="4.0015774"
id="rect4202"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:transform-center-x="-37.000003" />
<rect
inkscape:transform-center-y="-32.042915"
transform="matrix(0.50014835,-0.86593974,-0.86611103,-0.49985167,0,0)"
y="-576.4541"
x="-147.76167"
height="10.002968"
width="4.0003901"
id="rect4204"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:transform-center-x="18.499986" />
<rect
inkscape:transform-center-x="32.042943"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4206"
width="4.0011816"
height="10.00099"
x="138.96213"
y="-577.63342"
transform="matrix(0.86611103,-0.49985167,-0.50014835,-0.86593974,0,0)"
inkscape:transform-center-y="-18.5" />
<rect
transform="scale(1,-1)"
y="-435.36221"
x="387.98267"
height="10.000001"
width="4.0015774"
id="rect4208"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:transform-center-x="36.999997" />
<rect
inkscape:transform-center-x="32.042959"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4210"
width="4.0011816"
height="10.00099"
x="532.4411"
y="-187.76564"
transform="matrix(0.86611103,0.49985167,0.50014835,-0.86593974,0,0)"
inkscape:transform-center-y="18.499977" />
<rect
inkscape:transform-center-y="32.042932"
transform="matrix(0.50014835,0.86593974,0.86611103,-0.49985167,0,0)"
y="98.950249"
x="533.62909"
height="10.002968"
width="4.0003905"
id="rect4212"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:transform-center-x="18.500021" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:transform-center-y="-36.999984"
d="m 432.00006,395.36221 0.50022,-3.99999 -10.50418,0 0,3.99999 z"
id="rect4196"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<rect
inkscape:transform-center-x="2.4545496e-05"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4195"
width="3.9999948"
height="10.003957"
x="391.36224"
y="347.96683"
transform="matrix(-5.7482221e-10,1,1,-2.7999817e-9,0,0)"
inkscape:transform-center-y="37.00001" />
<rect
inkscape:transform-center-y="32.04295"
transform="matrix(-0.50014835,0.86593974,0.86611103,0.49985166,0,0)"
y="492.4292"
x="143.76131"
height="10.002968"
width="4.0003905"
id="rect4197"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:transform-center-x="-18.499986" />
<rect
inkscape:transform-center-x="-32.042923"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4199"
width="4.0011821"
height="10.00099"
x="-142.96329"
y="493.62512"
transform="matrix(-0.86611103,0.49985167,0.50014835,0.86593973,0,0)"
inkscape:transform-center-y="18.500009" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,5 @@
TEMPLATE = aux
OTHER_FILES = integrationpluginsunposition.json \
integrationpluginsunposition.py

653
sunposition/sunposition.py Normal file
View File

@ -0,0 +1,653 @@
# The MIT License (MIT)
#
# Copyright (c) 2016 Samuel Bear Powell
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import numpy as np
from datetime import datetime
class _sp:
@staticmethod
def calendar_time(dt):
try:
x = dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond
return x
except AttributeError:
try:
return _sp.calendar_time(datetime.utcfromtimestamp(dt)) #will raise OSError if dt is not acceptable
except:
raise TypeError('dt must be datetime object or POSIX timestamp')
@staticmethod
def julian_day(dt):
"""Calculate the Julian Day from a datetime.datetime object in UTC"""
# year and month numbers
yr, mo, dy, hr, mn, sc, us = _sp.calendar_time(dt)
if mo <= 2: # From paper: "if M = 1 or 2, then Y = Y - 1 and M = M + 12"
mo += 12
yr -= 1
# day of the month with decimal time
dy = dy + hr/24.0 + mn/(24.0*60.0) + sc/(24.0*60.0*60.0) + us/(24.0*60.0*60.0*1e6)
# b is equal to 0 for the julian calendar and is equal to (2- A +
# INT(A/4)), A = INT(Y/100), for the gregorian calendar
a = int(yr / 100)
b = 2 - a + int(a / 4)
jd = int(365.25 * (yr + 4716)) + int(30.6001 * (mo + 1)) + dy + b - 1524.5
return jd
@staticmethod
def julian_ephemeris_day(jd, deltat):
"""Calculate the Julian Ephemeris Day from the Julian Day and delta-time = (terrestrial time - universal time) in seconds"""
return jd + deltat / 86400.0
@staticmethod
def julian_century(jd):
"""Caluclate the Julian Century from Julian Day or Julian Ephemeris Day"""
return (jd - 2451545.0) / 36525.0
@staticmethod
def julian_millennium(jc):
"""Calculate the Julian Millennium from Julian Ephemeris Century"""
return jc / 10.0
# Earth Periodic Terms
# Earth Heliocentric Longitude coefficients (L0, L1, L2, L3, L4, and L5 in paper)
_EHL_ = [#L0:
[(175347046, 0.0, 0.0), (3341656, 4.6692568, 6283.07585), (34894, 4.6261, 12566.1517),
(3497, 2.7441, 5753.3849), (3418, 2.8289, 3.5231), (3136, 3.6277, 77713.7715),
(2676, 4.4181, 7860.4194), (2343, 6.1352, 3930.2097), (1324, 0.7425, 11506.7698),
(1273, 2.0371, 529.691), (1199, 1.1096, 1577.3435), (990, 5.233, 5884.927),
(902, 2.045, 26.298), (857, 3.508, 398.149), (780, 1.179, 5223.694),
(753, 2.533, 5507.553), (505, 4.583, 18849.228), (492, 4.205, 775.523),
(357, 2.92, 0.067), (317, 5.849, 11790.629), (284, 1.899, 796.298),
(271, 0.315, 10977.079), (243, 0.345, 5486.778), (206, 4.806, 2544.314),
(205, 1.869, 5573.143), (202, 2.4458, 6069.777), (156, 0.833, 213.299),
(132, 3.411, 2942.463), (126, 1.083, 20.775), (115, 0.645, 0.98),
(103, 0.636, 4694.003), (102, 0.976, 15720.839), (102, 4.267, 7.114),
(99, 6.21, 2146.17), (98, 0.68, 155.42), (86, 5.98, 161000.69),
(85, 1.3, 6275.96), (85, 3.67, 71430.7), (80, 1.81, 17260.15),
(79, 3.04, 12036.46), (71, 1.76, 5088.63), (74, 3.5, 3154.69),
(74, 4.68, 801.82), (70, 0.83, 9437.76), (62, 3.98, 8827.39),
(61, 1.82, 7084.9), (57, 2.78, 6286.6), (56, 4.39, 14143.5),
(56, 3.47, 6279.55), (52, 0.19, 12139.55), (52, 1.33, 1748.02),
(51, 0.28, 5856.48), (49, 0.49, 1194.45), (41, 5.37, 8429.24),
(41, 2.4, 19651.05), (39, 6.17, 10447.39), (37, 6.04, 10213.29),
(37, 2.57, 1059.38), (36, 1.71, 2352.87), (36, 1.78, 6812.77),
(33, 0.59, 17789.85), (30, 0.44, 83996.85), (30, 2.74, 1349.87),
(25, 3.16, 4690.48)],
#L1:
[(628331966747, 0.0, 0.0), (206059, 2.678235, 6283.07585), (4303, 2.6351, 12566.1517),
(425, 1.59, 3.523), (119, 5.796, 26.298), (109, 2.966, 1577.344),
(93, 2.59, 18849.23), (72, 1.14, 529.69), (68, 1.87, 398.15),
(67, 4.41, 5507.55), (59, 2.89, 5223.69), (56, 2.17, 155.42),
(45, 0.4, 796.3), (36, 0.47, 775.52), (29, 2.65, 7.11),
(21, 5.34, 0.98), (19, 1.85, 5486.78), (19, 4.97, 213.3),
(17, 2.99, 6275.96), (16, 0.03, 2544.31), (16, 1.43, 2146.17),
(15, 1.21, 10977.08), (12, 2.83, 1748.02), (12, 3.26, 5088.63),
(12, 5.27, 1194.45), (12, 2.08, 4694), (11, 0.77, 553.57),
(10, 1.3, 3286.6), (10, 4.24, 1349.87), (9, 2.7, 242.73),
(9, 5.64, 951.72), (8, 5.3, 2352.87), (6, 2.65, 9437.76),
(6, 4.67, 4690.48)],
#L2:
[(52919, 0.0, 0.0), (8720, 1.0721, 6283.0758), (309, 0.867, 12566.152),
(27, 0.05, 3.52), (16, 5.19, 26.3), (16, 3.68, 155.42),
(10, 0.76, 18849.23), (9, 2.06, 77713.77), (7, 0.83, 775.52),
(5, 4.66, 1577.34), (4, 1.03, 7.11), (4, 3.44, 5573.14),
(3, 5.14, 796.3), (3, 6.05, 5507.55), (3, 1.19, 242.73),
(3, 6.12, 529.69), (3, 0.31, 398.15), (3, 2.28, 553.57),
(2, 4.38, 5223.69), (2, 3.75, 0.98)],
#L3:
[(289, 5.844, 6283.076), (35, 0.0, 0.0,), (17, 5.49, 12566.15),
(3, 5.2, 155.42), (1, 4.72, 3.52), (1, 5.3, 18849.23),
(1, 5.97, 242.73)],
#L4:
[(114, 3.142, 0.0), (8, 4.13, 6283.08), (1, 3.84, 12566.15)],
#L5:
[(1, 3.14, 0.0)]
]
#Earth Heliocentric Longitude coefficients (B0 and B1 in paper)
_EHB_ = [ #B0:
[(280, 3.199, 84334.662), (102, 5.422, 5507.553), (80, 3.88, 5223.69),
(44, 3.7, 2352.87), (32, 4.0, 1577.34)],
#B1:
[(9, 3.9, 5507.55), (6, 1.73, 5223.69)]
]
#Earth Heliocentric Radius coefficients (R0, R1, R2, R3, R4)
_EHR_ = [#R0:
[(100013989, 0.0, 0.0), (1670700, 3.0984635, 6283.07585), (13956, 3.05525, 12566.1517),
(3084, 5.1985, 77713.7715), (1628, 1.1739, 5753.3849), (1576, 2.8469, 7860.4194),
(925, 5.453, 11506.77), (542, 4.564, 3930.21), (472, 3.661, 5884.927),
(346, 0.964, 5507.553), (329, 5.9, 5223.694), (307, 0.299, 5573.143),
(243, 4.273, 11790.629), (212, 5.847, 1577.344), (186, 5.022, 10977.079),
(175, 3.012, 18849.228), (110, 5.055, 5486.778), (98, 0.89, 6069.78),
(86, 5.69, 15720.84), (86, 1.27, 161000.69), (85, 0.27, 17260.15),
(63, 0.92, 529.69), (57, 2.01, 83996.85), (56, 5.24, 71430.7),
(49, 3.25, 2544.31), (47, 2.58, 775.52), (45, 5.54, 9437.76),
(43, 6.01, 6275.96), (39, 5.36, 4694), (38, 2.39, 8827.39),
(37, 0.83, 19651.05), (37, 4.9, 12139.55), (36, 1.67, 12036.46),
(35, 1.84, 2942.46), (33, 0.24, 7084.9), (32, 0.18, 5088.63),
(32, 1.78, 398.15), (28, 1.21, 6286.6), (28, 1.9, 6279.55),
(26, 4.59, 10447.39)],
#R1:
[(103019, 1.10749, 6283.07585), (1721, 1.0644, 12566.1517), (702, 3.142, 0.0),
(32, 1.02, 18849.23), (31, 2.84, 5507.55), (25, 1.32, 5223.69),
(18, 1.42, 1577.34), (10, 5.91, 10977.08), (9, 1.42, 6275.96),
(9, 0.27, 5486.78)],
#R2:
[(4359, 5.7846, 6283.0758), (124, 5.579, 12566.152), (12, 3.14, 0.0),
(9, 3.63, 77713.77), (6, 1.87, 5573.14), (3, 5.47, 18849)],
#R3:
[(145, 4.273, 6283.076), (7, 3.92, 12566.15)],
#R4:
[(4, 2.56, 6283.08)]
]
@staticmethod
def heliocentric_longitude(jme):
"""Compute the Earth Heliocentric Longitude (L) in degrees given the Julian Ephemeris Millennium"""
#L5, ..., L0
Li = [sum(a*np.cos(b + c*jme) for a,b,c in abcs) for abcs in reversed(_sp._EHL_)]
L = np.polyval(Li, jme) / 1e8
L = np.rad2deg(L) % 360
return L
@staticmethod
def heliocentric_latitude(jme):
"""Compute the Earth Heliocentric Latitude (B) in degrees given the Julian Ephemeris Millennium"""
Bi = [sum(a*np.cos(b + c*jme) for a,b,c in abcs) for abcs in reversed(_sp._EHB_)]
B = np.polyval(Bi, jme) / 1e8
B = np.rad2deg(B) % 360
return B
@staticmethod
def heliocentric_radius(jme):
"""Compute the Earth Heliocentric Radius (R) in astronimical units given the Julian Ephemeris Millennium"""
Ri = [sum(a*np.cos(b + c*jme) for a,b,c in abcs) for abcs in reversed(_sp._EHR_)]
R = np.polyval(Ri, jme) / 1e8
return R
@staticmethod
def heliocentric_position(jme):
"""Compute the Earth Heliocentric Longitude, Latitude, and Radius given the Julian Ephemeris Millennium
Returns (L, B, R) where L = longitude in degrees, B = latitude in degrees, and R = radius in astronimical units
"""
return _sp.heliocentric_longitude(jme), _sp.heliocentric_latitude(jme), _sp.heliocentric_radius(jme)
@staticmethod
def geocentric_position(helio_pos):
"""Compute the geocentric latitude (Theta) and longitude (beta) (in degrees) of the sun given the earth's heliocentric position (L, B, R)"""
L,B,R = helio_pos
th = L + 180
b = -B
return (th, b)
#Nutation Longitude and Obliquity coefficients (Y)
_NLOY_ = [(0, 0, 0, 0, 1), (-2, 0, 0, 2, 2), (0, 0, 0, 2, 2),
(0, 0, 0, 0, 2), (0, 1, 0, 0, 0), (0, 0, 1, 0, 0),
(-2, 1, 0, 2, 2), (0, 0, 0, 2, 1), (0, 0, 1, 2, 2),
(-2, -1, 0, 2, 2), (-2, 0, 1, 0, 0), (-2, 0, 0, 2, 1),
(0, 0, -1, 2, 2), (2, 0, 0, 0, 0), (0, 0, 1, 0, 1),
(2, 0, -1, 2, 2), (0, 0, -1, 0, 1), (0, 0, 1, 2, 1),
(-2, 0, 2, 0, 0), (0, 0, -2, 2, 1), (2, 0, 0, 2, 2),
(0, 0, 2, 2, 2), (0, 0, 2, 0, 0), (-2, 0, 1, 2, 2),
(0, 0, 0, 2, 0), (-2, 0, 0, 2, 0), (0, 0, -1, 2, 1),
(0, 2, 0, 0, 0), (2, 0, -1, 0, 1), (-2, 2, 0, 2, 2),
(0, 1, 0, 0, 1), (-2, 0, 1, 0, 1), (0, -1, 0, 0, 1),
(0, 0, 2, -2, 0), (2, 0, -1, 2, 1), (2, 0, 1, 2, 2),
(0, 1, 0, 2, 2), (-2, 1, 1, 0, 0), (0, -1, 0, 2, 2),
(2, 0, 0, 2, 1), (2, 0, 1, 0, 0), (-2, 0, 2, 2, 2),
(-2, 0, 1, 2, 1), (2, 0, -2, 0, 1), (2, 0, 0, 0, 1),
(0, -1, 1, 0, 0), (-2, -1, 0, 2, 1), (-2, 0, 0, 0, 1),
(0, 0, 2, 2, 1), (-2, 0, 2, 0, 1), (-2, 1, 0, 2, 1),
(0, 0, 1, -2, 0), (-1, 0, 1, 0, 0), (-2, 1, 0, 0, 0),
(1, 0, 0, 0, 0), (0, 0, 1, 2, 0), (0, 0, -2, 2, 2),
(-1, -1, 1, 0, 0), (0, 1, 1, 0, 0), (0, -1, 1, 2, 2),
(2, -1, -1, 2, 2), (0, 0, 3, 2, 2), (2, -1, 0, 2, 2)]
#Nutation Longitude and Obliquity coefficients (a,b)
_NLOab_ = [(-171996, -174.2), (-13187, -1.6), (-2274, -0.2), (2062, 0.2), (1426, -3.4), (712, 0.1),
(-517, 1.2), (-386, -0.4), (-301, 0), (217, -0.5), (-158, 0), (129, 0.1),
(123, 0), (63, 0), (63, 0.1), (-59, 0), (-58, -0.1), (-51, 0),
(48, 0), (46, 0), (-38, 0), (-31, 0), (29, 0), (29, 0),
(26, 0), (-22, 0), (21, 0), (17, -0.1), (16, 0), (-16, 0.1),
(-15, 0), (-13, 0), (-12, 0), (11, 0), (-10, 0), (-8, 0),
(7, 0), (-7, 0), (-7, 0), (-7, 0), (6, 0), (6, 0),
(6, 0), (-6, 0), (-6, 0), (5, 0), (-5, 0), (-5, 0),
(-5, 0), (4, 0), (4, 0), (4, 0), (-4, 0), (-4, 0),
(-4, 0), (3, 0), (-3, 0), (-3, 0), (-3, 0), (-3, 0),
(-3, 0), (-3, 0), (-3, 0)]
#Nutation Longitude and Obliquity coefficients (c,d)
_NLOcd_ = [(92025, 8.9), (5736, -3.1), (977, -0.5), (-895, 0.5),
(54, -0.1), (-7, 0), (224, -0.6), (200, 0),
(129, -0.1), (-95, 0.3), (0, 0), (-70, 0),
(-53, 0), (0, 0), (-33, 0), (26, 0),
(32, 0), (27, 0), (0, 0), (-24, 0),
(16, 0), (13, 0), (0, 0), (-12, 0),
(0, 0), (0, 0), (-10, 0), (0, 0),
(-8, 0), (7, 0), (9, 0), (7, 0),
(6, 0), (0, 0), (5, 0), (3, 0),
(-3, 0), (0, 0), (3, 0), (3, 0),
(0, 0), (-3, 0), (-3, 0), (3, 0),
(3, 0), (0, 0), (3, 0), (3, 0),
(3, 0)]
@staticmethod
def ecliptic_obliquity(jme, delta_epsilon):
"""Calculate the true obliquity of the ecliptic (epsilon, in degrees) given the Julian Ephemeris Millennium and the obliquity"""
u = jme/10
e0 = np.polyval([2.45, 5.79, 27.87, 7.12, -39.05, -249.67, -51.38, 1999.25, -1.55, -4680.93, 84381.448], u)
e = e0/3600.0 + delta_epsilon
return e
@staticmethod
def nutation_obliquity(jce):
"""compute the nutation in longitude (delta_psi) and the true obliquity (epsilon) given the Julian Ephemeris Century"""
#mean elongation of the moon from the sun, in radians:
#x0 = 297.85036 + 445267.111480*jce - 0.0019142*(jce**2) + (jce**3)/189474
x0 = np.deg2rad(np.polyval([1./189474, -0.0019142, 445267.111480, 297.85036],jce))
#mean anomaly of the sun (Earth), in radians:
x1 = np.deg2rad(np.polyval([-1/3e5, -0.0001603, 35999.050340, 357.52772], jce))
#mean anomaly of the moon, in radians:
x2 = np.deg2rad(np.polyval([1./56250, 0.0086972, 477198.867398, 134.96298], jce))
#moon's argument of latitude, in radians:
x3 = np.deg2rad(np.polyval([1./327270, -0.0036825, 483202.017538, 93.27191], jce))
#Longitude of the ascending node of the moon's mean orbit on the ecliptic
# measured from the mean equinox of the date, in radians
x4 = np.deg2rad(np.polyval([1./45e4, 0.0020708, -1934.136261, 125.04452], jce))
x = (x0, x1, x2, x3, x4)
dp = 0.0
for y, ab in zip(_sp._NLOY_, _sp._NLOab_):
a,b = ab
dp += (a + b*jce)*np.sin(np.dot(x, y))
dp = np.rad2deg(dp)/36e6
de = 0.0
for y, cd in zip(_sp._NLOY_, _sp._NLOcd_):
c,d = cd
de += (c + d*jce)*np.cos(np.dot(x, y))
de = np.rad2deg(de)/36e6
e = _sp.ecliptic_obliquity(_sp.julian_millennium(jce), de)
return dp, e
@staticmethod
def abberation_correction(R):
"""Calculate the abberation correction (delta_tau, in degrees) given the Earth Heliocentric Radius (in AU)"""
return -20.4898/(3600*R)
@staticmethod
def sun_longitude(helio_pos, delta_psi):
"""Calculate the apparent sun longitude (lambda, in degrees) and geocentric longitude (beta, in degrees) given the earth heliocentric position and delta_psi"""
L,B,R = helio_pos
theta = L + 180 #geocentric latitude
beta = -B
ll = theta + delta_psi + _sp.abberation_correction(R)
return ll, beta
@staticmethod
def greenwich_sidereal_time(jd, delta_psi, epsilon):
"""Calculate the apparent Greenwich sidereal time (v, in degrees) given the Julian Day"""
jc = _sp.julian_century(jd)
#mean sidereal time at greenwich, in degrees:
v0 = (280.46061837 + 360.98564736629*(jd - 2451545) + 0.000387933*(jc**2) - (jc**3)/38710000) % 360
v = v0 + delta_psi*np.cos(np.deg2rad(epsilon))
return v
@staticmethod
def sun_ra_decl(llambda, epsilon, beta):
"""Calculate the sun's geocentric right ascension (alpha, in degrees) and declination (delta, in degrees)"""
l, e, b = map(np.deg2rad, (llambda, epsilon, beta))
alpha = np.arctan2(np.sin(l)*np.cos(e) - np.tan(b)*np.sin(e), np.cos(l)) #x1 / x2
alpha = np.rad2deg(alpha) % 360
delta = np.arcsin(np.sin(b)*np.cos(e) + np.cos(b)*np.sin(e)*np.sin(l))
delta = np.rad2deg(delta)
return alpha, delta
@staticmethod
def sun_topo_ra_decl_hour(latitude, longitude, elevation, jd, delta_t = 0):
"""Calculate the sun's topocentric right ascension (alpha'), declination (delta'), and hour angle (H')"""
jde = _sp.julian_ephemeris_day(jd, delta_t)
jce = _sp.julian_century(jde)
jme = _sp.julian_millennium(jce)
helio_pos = _sp.heliocentric_position(jme)
R = helio_pos[-1]
phi, sigma, E = latitude, longitude, elevation
#equatorial horizontal parallax of the sun, in radians
xi = np.deg2rad(8.794/(3600*R)) #
#rho = distance from center of earth in units of the equatorial radius
#phi-prime = geocentric latitude
#NB: These equations look like their based on WGS-84, but are rounded slightly
# The WGS-84 reference ellipsoid has major axis a = 6378137 m, and flattening factor 1/f = 298.257223563
# minor axis b = a*(1-f) = 6356752.3142 = 0.996647189335*a
u = np.arctan(0.99664719*np.tan(phi)) #
x = np.cos(u) + E*np.cos(phi)/6378140 #rho sin(phi-prime)
y = 0.99664719*np.sin(u) + E*np.sin(phi)/6378140 #rho cos(phi-prime)
delta_psi, epsilon = _sp.nutation_obliquity(jce) #
llambda, beta = _sp.sun_longitude(helio_pos, delta_psi) #
alpha, delta = _sp.sun_ra_decl(llambda, epsilon, beta) #
v = _sp.greenwich_sidereal_time(jd, delta_psi, epsilon) #
H = v + longitude - alpha #
Hr, dr = map(np.deg2rad,(H,delta))
dar = np.arctan2(-x*np.sin(xi)*np.sin(Hr), np.cos(dr)-x*np.sin(xi)*np.cos(Hr))
delta_alpha = np.rad2deg(dar) #
alpha_prime = alpha + delta_alpha #
delta_prime = np.rad2deg(np.arctan2((np.sin(dr) - y*np.sin(xi))*np.cos(dar), np.cos(dr) - y*np.sin(xi)*np.cos(Hr))) #
H_prime = H - delta_alpha #
return alpha_prime, delta_prime, H_prime
@staticmethod
def sun_topo_azimuth_zenith(latitude, delta_prime, H_prime, temperature=14.6, pressure=1013):
"""Compute the sun's topocentric azimuth and zenith angles
azimuth is measured eastward from north, zenith from vertical
temperature = average temperature in C (default is 14.6 = global average in 2013)
pressure = average pressure in mBar (default 1013 = global average)
"""
phi = np.deg2rad(latitude)
dr, Hr = map(np.deg2rad,(delta_prime, H_prime))
P, T = pressure, temperature
e0 = np.rad2deg(np.arcsin(np.sin(phi)*np.sin(dr) + np.cos(phi)*np.cos(dr)*np.cos(Hr)))
tmp = np.deg2rad(e0 + 10.3/(e0+5.11))
delta_e = (P/1010.0)*(283.0/(273+T))*(1.02/(60*np.tan(tmp)))
e = e0 + delta_e
zenith = 90 - e
gamma = np.rad2deg(np.arctan2(np.sin(Hr), np.cos(Hr)*np.sin(phi) - np.tan(dr)*np.cos(phi))) % 360
Phi = (gamma + 180) % 360 #azimuth from north
return Phi, zenith
@staticmethod
def norm_lat_lon(lat,lon):
if lat < -90 or lat > 90:
#convert to cartesian and back
x = cos(deg2rad(lon))*cos(deg2rad(lat))
y = sin(deg2rad(lon))*cos(deg2rad(lat))
z = sin(deg2rad(lat))
r = sqrt(x**2 + y**2 + z**2)
lon = rad2deg(arctan2(y,x)) % 360
lat = rad2deg(arcsin(z/r))
elif lon < 0 or lon > 360:
lon = lon % 360
return lat,lon
@staticmethod
def topo_pos(t,lat,lon,elev,temp,press,dt):
"""compute RA,dec,H, all in degrees"""
lat,lon = _sp.norm_lat_lon(lat,lon)
jd = _sp.julian_day(t)
RA, dec, H = _sp.sun_topo_ra_decl_hour(lat, lon, elev, jd, dt)
return RA, dec, H
@staticmethod
def pos(t,lat,lon,elev,temp,press,dt):
"""Compute azimute,zenith,RA,dec,H all in degrees"""
lat,lon = _sp.norm_lat_lon(lat,lon)
jd = _sp.julian_day(t)
RA, dec, H = _sp.sun_topo_ra_decl_hour(lat, lon, elev, jd, dt)
azimuth, zenith = _sp.sun_topo_azimuth_zenith(lat, dec, H, temp, press)
return azimuth,zenith,RA,dec,H
def julian_day(dt):
"""Convert UTC datetimes or UTC timestamps to Julian days
Parameters
----------
dt : array_like
UTC datetime objects or UTC timestamps (as per datetime.utcfromtimestamp)
Returns
-------
jd : ndarray
datetimes converted to fractional Julian days
"""
dts = np.array(dt)
if len(dts.shape) == 0:
return _sp.julian_day(dt)
jds = np.empty(dts.shape)
for i,d in enumerate(dts.flat):
jds.flat[i] = _sp.julian_day(d)
return jds
def arcdist(p0,p1,radians=False):
"""Angular distance between azimuth,zenith pairs
Parameters
----------
p0 : array_like, shape (..., 2)
p1 : array_like, shape (..., 2)
p[...,0] = azimuth angles, p[...,1] = zenith angles
radians : boolean (default False)
If False, angles are in degrees, otherwise in radians
Returns
-------
ad : array_like, shape is broadcast(p0,p1).shape
Arcdistances between corresponding pairs in p0,p1
In degrees by default, in radians if radians=True
"""
#formula comes from translating points into cartesian coordinates
#taking the dot product to get the cosine between the two vectors
#then arccos to return to angle, and simplify everything assuming real inputs
p0,p1 = np.array(p0), np.array(p1)
if not radians:
p0,p1 = np.deg2rad(p0), np.deg2rad(p1)
a0,z0 = p0[...,0], p0[...,1]
a1,z1 = p1[...,0], p1[...,1]
d = np.arccos(np.cos(z0)*np.cos(z1)+np.cos(a0-a1)*np.sin(z0)*np.sin(z1))
if radians:
return d
else:
return np.rad2deg(d)
def observed_sunpos(dt, latitude, longitude, elevation, temperature=None, pressure=None, delta_t=0, radians=False):
"""Compute the observed coordinates of the sun as viewed at the given time and location.
Parameters
----------
dt : array_like of datetime or float
UTC datetime objects or UTC timestamps (as per datetime.utcfromtimestamp) representing the times of observations
latitude, longitude : array_like of float
decimal degrees, positive for north of the equator and east of Greenwich
elevation : array_like of float
meters, relative to the WGS-84 ellipsoid
temperature : None or array_like of float, optional
celcius, default is 14.6 (global average in 2013)
pressure : None or array_like of float, optional
millibar, default is 1013 (global average in ??)
delta_t : array_like of float, optional
seconds, default is 0, difference between the earth's rotation time (TT) and universal time (UT)
radians : bool, optional
return results in radians if True, degrees if False (default)
Returns
-------
coords : ndarray, (...,2)
The shape of the array is parameters broadcast together, plus a final dimension for the coordinates.
coords[...,0] = observed azimuth angle, measured eastward from north
coords[...,1] = observed zenith angle, measured down from vertical
"""
if temperature is None:
temperature = 14.6
if pressure is None:
pressure = 1013
#6367444 = radius of earth
#numpy broadcasting
b = np.broadcast(dt,latitude,longitude,elevation,temperature,pressure,delta_t)
res = np.empty(b.shape+(2,))
res_vec = res.reshape((-1,2))
for i,x in enumerate(b):
res_vec[i] = _sp.pos(*x)[:2]
if radians:
res = np.deg2rad(res)
return res
def topocentric_sunpos(dt, latitude, longitude, temperature=None, pressure=None, delta_t=0, radians=False):
"""Compute the topocentric coordinates of the sun as viewed at the given time and location.
Parameters
----------
dt : array_like of datetime or float
UTC datetime objects or UTC timestamps (as per datetime.utcfromtimestamp) representing the times of observations
latitude, longitude : array_like of float
decimal degrees, positive for north of the equator and east of Greenwich
elevation : array_like of float
meters, relative to the WGS-84 ellipsoid
temperature : None or array_like of float, optional
celcius, default is 14.6 (global average in 2013)
pressure : None or array_like of float, optional
millibar, default is 1013 (global average in ??)
delta_t : array_like of float, optional
seconds, default is 0, difference between the earth's rotation time (TT) and universal time (UT)
radians : bool, optional
return results in radians if True, degrees if False (default)
Returns
-------
coords : ndarray, (...,3)
The shape of the array is parameters broadcast together, plus a final dimension for the coordinates.
coords[...,0] = topocentric right ascension
coords[...,1] = topocentric declination
coords[...,2] = topocentric hour angle
"""
if temperature is None:
temperature = 14.6
if pressure is None:
pressure = 1013
#6367444 = radius of earth
#numpy broadcasting
b = np.broadcast(dt,latitude,longitude,elevation,temperature,pressure,delta_t)
res = np.empty(b.shape+(2,))
res_vec = res.reshape((-1,2))
for i,x in enumerate(b):
res_vec[i] = _sp.topo_pos(*x)
if radians:
res = np.deg2rad(res)
return res
def sunpos(dt, latitude, longitude, elevation, temperature=None, pressure=None, delta_t=0, radians=False):
"""Compute the observed and topocentric coordinates of the sun as viewed at the given time and location.
Parameters
----------
dt : array_like of datetime or float
UTC datetime objects or UTC timestamps (as per datetime.utcfromtimestamp) representing the times of observations
latitude, longitude : array_like of float
decimal degrees, positive for north of the equator and east of Greenwich
elevation : array_like of float
meters, relative to the WGS-84 ellipsoid
temperature : None or array_like of float, optional
celcius, default is 14.6 (global average in 2013)
pressure : None or array_like of float, optional
millibar, default is 1013 (global average in ??)
delta_t : array_like of float, optional
seconds, default is 0, difference between the earth's rotation time (TT) and universal time (UT)
radians : bool, optional
return results in radians if True, degrees if False (default)
Returns
-------
coords : ndarray, (...,5)
The shape of the array is parameters broadcast together, plus a final dimension for the coordinates.
coords[...,0] = observed azimuth angle, measured eastward from north
coords[...,1] = observed zenith angle, measured down from vertical
coords[...,2] = topocentric right ascension
coords[...,3] = topocentric declination
coords[...,4] = topocentric hour angle
"""
if temperature is None:
temperature = 14.6
if pressure is None:
pressure = 1013
#6367444 = radius of earth
#numpy broadcasting
b = np.broadcast(dt,latitude,longitude,elevation,temperature,pressure,delta_t)
res = np.empty(b.shape+(5,))
res_vec = res.reshape((-1,5))
for i,x in enumerate(b):
res_vec[i] = _sp.pos(*x)
if radians:
res = np.deg2rad(res)
return res
def main(args):
az, zen, ra, dec, h = sunpos(args.t, args.lat, args.lon, args.elev, args.temp, args.p, args.dt, args.rad)
if args.csv:
#machine readable
print('{t}, {dt}, {lat}, {lon}, {elev}, {temp}, {p}, {az}, {zen}, {ra}, {dec}, {h}'.format(t=args.t, dt=args.dt, lat=args.lat, lon=args.lon, elev=args.elev,temp=args.temp, p=args.p,az=az, zen=zen, ra=ra, dec=dec, h=h))
else:
dr='deg'
if args.rad:
dr='rad'
print("Computing sun position at T = {t} + {dt} s".format(t=args.t, dt=args.dt))
print("Lat, Lon, Elev = {lat} deg, {lon} deg, {elev} m".format(lat=args.lat, lon=args.lon, elev=args.elev))
print("T, P = {temp} C, {press} mbar".format(temp=args.temp, press=args.p))
print("Results:")
print("Azimuth, zenith = {az} {dr}, {zen} {dr}".format(az=az,zen=zen,dr=dr))
print("RA, dec, H = {ra} {dr}, {dec} {dr}, {h} {dr}".format(ra=ra, dec=dec, h=h, dr=dr))
if __name__ == '__main__':
from argparse import ArgumentParser
import datetime, sys
parser = ArgumentParser(prog='sunposition',description='Compute sun position parameters given the time and location')
parser.add_argument('--version',action='version',version='%(prog)s 1.0')
parser.add_argument('--citation',dest='cite',action='store_true',help='Print citation information')
parser.add_argument('-t,--time',dest='t',type=str,default='now',help='"now" or date and time (UTC) in "YYYY-MM-DD hh:mm:ss.ssssss" format or a (UTC) POSIX timestamp')
parser.add_argument('-lat,--latitude',dest='lat',type=float,default=51.48,help='latitude, in decimal degrees, positive for north')
parser.add_argument('-lon,--longitude',dest='lon',type=float,default=0.0,help='longitude, in decimal degrees, positive for east')
parser.add_argument('-e,--elevation',dest='elev',type=float,default=0,help='elevation, in meters')
parser.add_argument('-T,--temperature',dest='temp',type=float,default=14.6,help='temperature, in degrees celcius')
parser.add_argument('-p,--pressure',dest='p',type=float,default=1013.0,help='atmospheric pressure, in millibar')
parser.add_argument('-dt',type=float,default=0.0,help='difference between earth\'s rotation time (TT) and universal time (UT1)')
parser.add_argument('-r,--radians',dest='rad',action='store_true',help='Output in radians instead of degrees')
parser.add_argument('--csv',dest='csv',action='store_true',help='Comma separated values (time,dt,lat,lon,elev,temp,pressure,az,zen,RA,dec,H)')
args = parser.parse_args()
if args.cite:
print("Implementation: Samuel Bear Powell, 2016")
print("Algorithm:")
print("Ibrahim Reda, Afshin Andreas, \"Solar position algorithm for solar radiation applications\", Solar Energy, Volume 76, Issue 5, 2004, Pages 577-589, ISSN 0038-092X, doi:10.1016/j.solener.2003.12.003")
sys.exit(0)
if args.t == "now":
args.t = datetime.datetime.utcnow()
elif ":" in args.t and "-" in args.t:
try:
args.t = datetime.datetime.strptime(args.t,'%Y-%m-%d %H:%M:%S.%f') #with microseconds
except:
try:
args.t = datetime.datetime.strptime(args.t,'%Y-%m-%d %H:%M:%S.') #without microseconds
except:
args.t = datetime.datetime.strptime(args.t,'%Y-%m-%d %H:%M:%S')
else:
args.t = datetime.datetime.utcfromtimestamp(int(args.t))
main(args)

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>sunPosition</name>
<message>
<source>Latitude</source>
<extracomment>The name of the ParamType (ThingClass: sunPosition, Type: thing, ID: {cc321533-787a-4fba-9abe-7b73e588ac5d})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>Longitude</source>
<extracomment>The name of the ParamType (ThingClass: sunPosition, Type: thing, ID: {1b0bbf53-634a-42ed-b3b3-c058ef1e015c})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>Sun Position</source>
<extracomment>The name of the ThingClass ({a79bc8cb-89bc-4227-9b7c-70130d26ad34})
----------
The name of the plugin sunPosition ({30ff7e8f-4c67-4a8a-8b49-94c64bcbbe8a})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>Sun angle</source>
<extracomment>The name of the ParamType (ThingClass: sunPosition, EventType: angle, ID: {5c2f5149-5152-4e0e-b372-3d3edd302306})
----------
The name of the StateType ({5c2f5149-5152-4e0e-b372-3d3edd302306}) of ThingClass sunPosition</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>Sun angle changed</source>
<extracomment>The name of the EventType ({5c2f5149-5152-4e0e-b372-3d3edd302306}) of ThingClass sunPosition</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>nymea GmbH</source>
<extracomment>The name of the vendor ({2062d64d-3232-433c-88bc-0d33c0ba2ba6})</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
</TS>