Dieses Dokument wurde erstellt durch Stefan Kühnel stefan.kuehnel@hm.edu und Konrad Moron k.moron@hm.edu
1. Abstrakt 2. Annahmen 3. Mathematisches Modell 3.1. Implementierung der Gleichungen für die Sonnenbahn 3.2. Beschreibung einer allgemeinen Ursprungsebene durch Normalenvektor 3.3. Berechnung der x,y-Koordinaten der Schattenspitze auf der Hauswand 4. Anwendung und Veranschaulichung 4.1. Veranschaulichung der Implementierung des Sonnenschattens 4.2. Interaktive Grafik zur Veranschaulichung der Linien des Ziffernblattes zu unterschiedlichen Tageszeiten 4.3. Ausgabe der Koordinaten der Schattenspitze 4.4. Ausgabe des Normalen-, Schatten-, Sonnen- und Zeigervektors 4.5. Ausgabe des Ziffernblattes der Sonnenuhr 5. Literaturverzeichnis 6. Appendix 6.1. Herleitung der Formeln für die Vektordarstellung des Schattenvektors 6.2. Aufgabenverteilung unter den Teammitgliedern
Sonnenuhren fangen durch einen fest verankerten Stab Sonnenstrahlen ein und ermöglichen durch die Projektion des Schattens auf am Boden befindliche Zeitlinien, eine präzise Bestimmung der aktuellen Uhrzeit. Doch obwohl das Konzept von Sonnenuhren schon zu Zeiten der alten Ägypter bekannt gewesen ist [Ref: 1], so handelt es sich bei ihrem Aufbau auch heute noch um eine nicht ganz triviale Aufgabe. Ziel des nachfolgenden Papers wird es deshalb sein, eine universelle Sonnenuhr auf Basis der aktuellen Positions-/ und Zeitzonendaten zu definieren, die, sollte sie an einer Südwand angebracht sein, die korrekte aktuelle Uhrzeit anzeigt.
Das nachfolgende mathematische Modell trifft einige idealisierte Annahmen:
Bei der Implementierung der Sonnenuhr kamen vermehrt Themen aus dem Themenkomplex der Linearen Algebra
und der Geometrischen, sowie Analytischen Geometrie zum Einsatz. Zuerst war es dabei notwendig, die
notwendigen Punkte Zeigerstab
, Sonnenposition
, Schattenvektor
und
Normalenvektor der Hauswand
über Kugelkoordinaten zu definieren. Im Anschluss wurde durch
über den Richtungsvektor der Sonne und den an der Hauswand angebrachten Zeigerstab ein Sonnenstrahl
gelegt, dessen Schnittpunkt auf der Wand, den Schattenvektor begrenzte. Durch eine anschließende Abbildung
des dreidimensionalen Schattenvektors auf eine zweidimensionale Fläche, war es möglich, die
x,y-Koordinaten des Zeigerstabs zu bestimmen, die im finalen Schritt zur Erstellung des Ziffernblattes der
Sonnenuhr zum Einsatz kamen.
Die Position der Sonne ist abhängig von einem gegebenen Ort und der dazugehörigen aktuellen Uhrzeit. Die
Implementierung der Sonnenbahngleichungen erfolgte gemäß den vorgegebenen Formeln in der deutschsprachigen
Wikipedia [Ref: 3].
Der Konstruktoraufruf erfordert die Angabe eines gregorianischen Datums und einer Position (Längen-/ und
Breitengrad) auf der Erde. Nach Abschluss aller Berechnungen, liefert die Methode getAngles()
ein Listenelement zurück, welches Azimut und Höhenwinkel enthält. Die zurückgegebene Ausgabe wurde darüber
hinaus durch das Portal Sun
Ephemeris Calculator von Jay Tanner verifiziert. Die marginale Messabweichung von maximal 0.01°
stellt für die Simulation und Berechnung der Sonnenuhr allerdings kein Problem dar.
Sollten jedoch Werte mit einer maximalen Abweichung von 0.0003° erforderlich sein, so kann der Algorithmus von Reda und Afshin [Ref: 4] angewendet werden, der für eine Zeitspanne von $-2000$ bis $+6000$ Jahren eine Gültigkeit besitzt.
class Sun():
def __init__(self, y, m, d, l, t):
"""
Konstruiert eine neue Sonne mit den angegebenen Datum und Koordinaten
y = gregorianisches Jahr, ganze Zahl
m = gregorianischer Monat, ganze Zahl
d = gregorianischer Tag
l = Längengrad in Dezimaldarstellung
t = Breitengrad in Dezimaldarstellung
"""
assert 0 < m < 13,"Der gregorianische Monat muss im Intervall [0,12] liegen."
assert m in ZZ,"Der gregorianische Monat muss einer ganzen Zahl entsprechen."
assert y in ZZ,"Das gregorianische Jahr muss einer ganzen Zahl entsprechen."
assert 0 < d <= 31,"Der gregorianische Tag muss im Intervall [1,31] liegen."
self.__year = y
self.__month = m
self.__day = d
self.__l = l
self.__t = t
angles = self.getAngles(0)
def getAngles(self, T):
"""
Berechnet den Azimut und Höhenwinkel der Sonne für die gegebene Uhrzeit.
T = Dezimaldarstellung der Uhrzeit
"""
assert 0 <= T < 24,"Die Uhrzeit muss der Dezimaldarstellung entsprechen und sich im Intervall [0,24] befinden."
s = radian(self.__stundenwinkel(T))
d = radian(self.__deklination(T))
t = radian(self.__t)
az = degree(arctan(sin(s)/(cos(s)*sin(t) - tan(d)*cos(t))))
if (cos(s)*sin(t) - tan(d)*cos(t)) < 0:
az = az + 180
hw = degree(arcsin(cos(d)*cos(s)*cos(t) + sin(d)*sin(t)))
sol = [az, hw]
return sol
def getLatitude(self):
"""
Liefert den Breitengrad des Objekts."""
return self.__t
def __stundenwinkel(self, T):
"""
Liefert den Stundenwinkel für die im Konstruktor angegebenen Daten
T = Dezimaldarstellung der Uhrzeit
"""
return self.__localStundenWinkel(T) - self.__rektaszension(T)
def __rektaszension (self, T):
"""
Liefert die Rektaszension für die im Konstruktor angegebenen Daten
T = Dezimaldarstellung der Uhrzeit
"""
n = (self.__JD(T=T) - 2451545)
e = 23.439 - 0.0000004*n
A = self.__eclipticposition(T)
if cos(radian(A)) > 0:
result = degree(arctan(cos(radian(e))*tan(radian(A))))
else:
result = degree(arctan(cos(radian(e))*tan(radian(A)))) + 4*degree(arctan(1))
if result < 0:
result = 360 + result
return result
def __deklination(self, T):
"""
Liefert die Deklination für die im Konstruktor angegebenen Daten
T = Dezimaldarstellung der Uhrzeit
"""
n = (self.__JD(T=T) - 2451545)
e = 23.439 - 0.0000004*n
A = self.__eclipticposition(T)
return degree(arcsin(sin(radian(e))*sin(radian(A))))
def __eclipticposition (self, T):
"""
Liefert die ekliptikale Länge für die im Konstruktor angegebenen Daten
T = Dezimaldarstellung der Uhrzeit
"""
n = (self.__JD(T=T) - 2451545)
L = 280.460 + 0.9856474*n
g = 357.528 + 0.9856003*n
return L + 1.915*sin(radian(g)) + 0.01997*sin(radian(2*g))
def __localStundenWinkel (self, T):
"""
Liefert den lokalen Stundenwinkel des Frühlingspunktes für die im Konstruktor angegebenen Daten.
T = Dezimaldarstellung der Uhrzeit
"""
tmp = 6.697376 + 2400.05134*self.__T0() + 1.002738*T
remHours = int(tmp)//24*24
return (6.697376 + 2400.05134*self.__T0() + 1.002738*T-remHours)*15 + self.__l
def __T0 (self):
"""
Liefert die bisher vergangenen Julianischen Jahrhunderte ab J2000
"""
return (self.__JD() - 2451545)/36525
def __JD (self, T=0):
"""
Liefert die julianische Tageszahl für das hinterlegte Datum und Uhrzeit
T = Dezimaldarstellung der Uhrzeit
"""
y = self.__year
m = self.__month
d = self.__day + T/24
if m <= 2:
y = y - 1
m = m + 12
return floor(365.25*(y+4716)) + floor(30.6001*(m+1)) + d + (2 - y//100 + y//400) - 1524.5
Zur Beschreibung der Ursprungsebene wird ein Normalenvektor $\vec{e}$ mit Hilfe von Kugelkoordinaten definiert. Gemäß Zwillinger (S. 589) [Ref: 2] ergibt sich zur Definition eines Vektors durch zwei Winkel folgende Vektordarstellung:
$\displaystyle \tilde{\vec{e}} = \begin{pmatrix}p\cos{\theta}\sin{\phi} \\ p\sin{\theta}\sin{\phi}\\p\cos{\phi}\end{pmatrix}$
Setzt man nun jedoch für den Höhenwinkel $\theta = 0$ und für den Azimuth $\phi = 0$ (für Süden) ein, so erkennt man sehr schnell durch die Ausrichtung des Normalenvektors in z-Richtung, dass diese Art der Darstellung ein wenig realitätsfremd ist, wenn man beachtet, dass eine Hauswand niemals gen Himmel gerichtet sein wird. Aus diesem Grund soll daher eine realistischere Vektordarstellung graphisch ermittelt werden.
Die nun ermittelte Vektordarstellung richtet den Normalenvektor für $\theta = 0$ und $\phi = 0$ gemäß realistischen Gegebenheiten zur x-Achse hin aus.
Beachte: Die Länge $r$ (Distanz zum Punkt P) des Normalenvektors spielt keine Rolle und wird deshalb auf 1 gesetzt.
$\displaystyle \vec{e} = r \cdot \begin{pmatrix} \cos{\theta}\cos{\phi} \\ \cos{\theta}\sin{\phi} \\ \sin{\theta} \end{pmatrix} = \begin{pmatrix} \cos{\theta}\cos{\phi} \\ \cos{\theta}\sin{\phi} \\ \sin{\theta} \end{pmatrix}$
Zur Berechnung der Schattenvektors $\vec{s}$ auf der Hauswand ist es erforderlich eine Gerade, beschrieben durch die Position der Sonne am Himmel $\vec{☉}$ und den Zeigerstab $\vec{p}$ zu definieren. Daraus ergibt sich folgende Formeldarstellung:
$\displaystyle \vec{s} = \vec{p} + \lambda\vec{☉}$
Da bekannt ist, dass der die Hauswand beschreibende Normalenvektor $\vec{e}$ und der dazugehörige Schattenvektor $\vec{s}$ zueinander orthogonal $\vec{s} \perp \vec{e}$ sind, folgt daraus, dass auch das Standardskalarprodukt $\langle \vec{s},\vec{e}\rangle = \vec{s} \cdot \vec{e} = 0$ sein muss. Diese Erkenntnis kann zum Auflösung der oben definierten Geradengleichung verwendet werden [Ref: 5].
$\displaystyle \underbrace{\vec{s} \cdot \vec{e}}_{= \, 0} = [\vec{p} + \lambda\vec{☉}] \cdot \vec{e}$
$\displaystyle \therefore \, \lambda = - \frac{\vec{p} \cdot \vec{e}}{\vec{☉} \cdot \vec{e}} = - \frac{p_{1} e_{1} + p_{2} e_{2} + p_{3} e_{3}}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}$
Durch Erweiterung, Faktorisierung und Substitution des Ausdrucks für $\lambda$ Appendix 1, kann für den Schattenvektor $\vec{s}$ folgende Vektordarstellung gefunden werden:
$\displaystyle \vec{s} = \frac{1}{e_{1} ☉_{1} + e_{2} ☉_{2} + e_{3} ☉_{3}} \cdot {\begin{bmatrix}p_{1}(☉_{2} e_{2} + ☉_{3} e_{3}) - ☉_{1} (p_{2} e_{2} + p_{3} e_{3})\\p_{2} (☉_{1} e_{1} + ☉_{3} e_{3}) - ☉_{2} (p_{1} e_{1} + p_{3} e_{3})\\p_{3} (☉_{1} e_{1} + ☉_{2} e_{2}) - ☉_{3} (p_{1} e_{1} + p_{2} e_{2})\\\end{bmatrix}}$
Um nun die x,y-Koordinaten der Schattenspitze auf der Hauswand zu ermitteln, ist es gemäß Ruhrländer (S. 101 - 109) [Ref: 6] erforderlich, die Ebene mit den daraufliegenden Schatten auf eine zweidimensionale Abbildung zu projizieren. Eine Erleichterung stellt hierbei die Annahme dar, dass die Hauswand immer einen Höhenwinkel $\theta = 0$ besitzen wird.
Durch die Hilfsmethode wall_projection_matrix()
wird eine Matrix $\boldsymbol {P}$ mit zwei
Richtungsvektoren $\vec{u}$ und $\vec{v}$ der Hauswand und dem dazugehörigen Normalenvektor $\vec{e}$
erzeugt.
$\boldsymbol {P} = \begin{bmatrix}\, \\ \vec{u} & \vec{v} & \vec{e} \\ \,\,\end{bmatrix} \quad \textrm{mit} \quad \vec{u} = \vec{v} \times \vec{e} \quad \textrm{und} \quad \vec{v} = \begin{pmatrix}0\\0\\1\\\end{pmatrix}$
Beachte: $\vec{v} \times \vec{e}$ entspricht dem Kreuzprodukt des zweiten Richtungsvektors $\vec{u}$ mit dem Normalenvektor $\vec{e}$.
Nun kann ein lineares Gleichungssystem aufgestellt werden, wobei $\vec{s}$ dem dreidimensionalen Schattenvektor entspricht.
$\boldsymbol {P} \cdot \vec{x} = \vec{s}$
Löst man dieses Gleichungssystem nun nach $\vec{x}$ auf, so erhält man den auf die Hauswand projizierten zweidimensionalen Schattenvektor $\vec{\hat s}$.
$\vec{\hat s} = \begin{pmatrix}x_{1}\\x_{2}\\\end{pmatrix}$
def radian (d):
return (d/180)*pi
def degree (d):
return (d/pi)*180
def carthesian_coordinate(distance, azimuth, elevation):
"""
Liefert einen Punkt im dreidimensionalen Raum in karthesischen Koordinaten.
distance: Die Länge des Vektors. Nicht negativ, nicht 0.
azimuth: Der horizontale Winkel (Azimut) des zu beschreibenden Objekts.
elevation: Der Höhenwinkel des zu beschreibenden Objekts.
"""
azimuth = -radian(azimuth)
elevation = radian(elevation)
assert (distance > 0), "Die Länge darf nicht negativ und nicht 0 betragen."
return vector([
distance*cos(elevation)*cos(azimuth),
distance*cos(elevation)*sin(azimuth),
distance*sin(elevation)
])
def wall_projection_matrix(normal):
"""
Gibt eine die Hauswand beschreibende Projektionsmatrix zurück, welche
den senkrecht auf der Hauswand stehenden Normalenvektor und zwei Richtungsvektoren
der Hauswand beinhaltet.
normal: Ein dreidimensionaler Normalenvektor der Hauswand mit Höhenwinkel = 0.
"""
elevation = normal[2]
degree_of_normal = normal.degree()
assert(elevation == 0), "Der Normalenvektor muss einen Höhenwinkel = 0 besitzen."
assert(degree_of_normal == 3), "Der Normalenvektor muss dreidimensional sein."
vertical_unit_vector = vector([0, 0, 1])
wall_direction_vector = vertical_unit_vector.cross_product(normal).normalized()
x = wall_direction_vector
y = vertical_unit_vector
z = normal
wall_projection_matrix = Matrix([x,y,z]).transpose()
return wall_projection_matrix
class Sundial():
def __init__(self, config):
"""
Initialisiert eine neue Sonnenuhr über ein Konfigurationsdictionary.
config["year"] = Das aktuelle Jahr in der Form YYYY.
config["month"] = Der aktuelle Monat in der Form MM im Intervall 1 bis 12.
config["day"] = Der aktuelle Tag in der Form DD im Intervall 1 bis 31.
config["longitude"] = Der Längengrad in Grad.
config["latitude"] = Der Breitengrad in Grad.
config["day_time"] = Die aktuelle Tageszeit im Intervall 0 bis 23
config["wall_azimuth"] = Der Azimuth der Wand im Intervall -pi/2 und +pi/2
config["pointer_length"] = Die Länge des Zeigerstabs > 0
"""
assert("year" in config), "Bitte übergeben Sie ein Jahr in der Form YYYY"
assert("month" in config), "Bitte übergeben Sie einen Monat in der Form MM im Intervall 0 bis 13"
assert("day" in config), "Bitte übergeben Sie einen Tag in der Form DD"
assert("longitude" in config), "Bitte übergeben Sie einen Breitengrad"
assert("latitude" in config), "Bitte übergeben Sie einen Längengrad"
assert("day_time" in config), "Bitte übergeben Sie eine Tageszeit im Intervall 0 bis 23"
assert("wall_azimuth" in config), "Bitte übergeben Sie den Azimut der Hauswand."
assert("pointer_length" in config), "Bitte definieren Sie die Länge des Zeigerstabs."
assert(config["year"] in ZZ), "Das Jahr entspricht nicht der Form YYYY"
assert(config["month"] in ZZ), "Der Monat entspricht nicht der Form MM"
assert(0 < config["month"] < 13), "Der Monat liegt nicht im Intervall 1 bis 12"
assert(config["day"] in ZZ), "Der Tag entspricht nicht der Form DD"
assert(0 < config["day"] < 32), "Der Tag muss im Intervall 1 bis 31 liegen."
assert(0 <= config["day_time"] <= 23),"Die aktuelle Uhrzeit muss im Intervall 0 bis 23 liegen."
assert(-pi/2<=radian(config["wall_azimuth"])<=pi/2),"Der Azimut der Südwand muss zwischen -pi/2 und +pi/2 liegen."
assert(config["pointer_length"] > 0), "Die Länge des Zeigerstabs darf nicht kleiner 0 sein."
self.year = config["year"]
self.month = config["month"]
self.day = config["day"]
self.longitude = config["longitude"]
self.latitude = config["latitude"]
self.day_time = config["day_time"]
self.wall_azimuth = config["wall_azimuth"]
self.pointer_length = config["pointer_length"]
self.SUN = Sun(self.year, self.month, self.day, self.longitude, self.latitude)
def update_day_time(self, day_time):
"""
Ermöglicht die dynamische Aktualisierung der Tageszeit.
day_time = Die aktualisierte Tageszeit in Stunden, die im Intervall 0 bis 23 liegen muss.
"""
assert(0 <= day_time < 24),"Die aktuelle Uhrzeit muss im Intervall 0 bis 23 liegen."
self.day_time = day_time
def get_normal(self):
"""
Liefert einen Normalenvektor, der die nach Süden ausgerichtete senkrechte Hauswand beschreibt.
"""
distance = 1
elevation = 0
return carthesian_coordinate(distance, self.wall_azimuth, elevation)
def get_pointer(self):
"""
Liefert einen nach Süden ausgerichteten Zeigerstab.
"""
azimuth = 0
elevation = -self.SUN.getLatitude()
return carthesian_coordinate(self.pointer_length, azimuth, elevation)
def get_sun_position(self):
"""
Liefert den Vektor der Sonne.
"""
sun = self.SUN
sun_angles = sun.getAngles(self.day_time)
distance = 1
azimuth = sun_angles[0]
elevation = sun_angles[1]
return carthesian_coordinate(distance, azimuth, elevation)
def get_shadow(self):
"""
Liefert den durch den Zeigerstab auf der Hauswand geworfenen Schatten zur entsprechenden Tageszeit.
"""
o = self.get_sun_position()
p = self.get_pointer()
e = self.get_normal()
day_time = self.day_time
sun_angles = self.SUN.getAngles(day_time)
elevation = sun_angles[1]
# Überprüfung, ob der Sonnenvektor nicht hinter der Hauswand liegt.
if (wall_projection_matrix(self.get_normal())\o)[2] <= 0:
return vector([0, 0, 0])
# Überprüfung, ob die Sonne noch sichtbar ist.
if (elevation < 0):
return vector([0, 0, 0])
p1 = p[0]
p2 = p[1]
p3 = p[2]
e1 = e[0]
e2 = e[1]
e3 = e[2]
o1 = o[0]
o2 = o[1]
o3 = o[2]
e1o1 = e1*o1
e2o2 = e2*o2
e3o3 = e3*o3
pf = 1/(e1o1 + e2o2 + e3o3)
s1 = p1*(e2o2 + e3o3) - o1*(p2*e2 + p3*e3)
s2 = p2*(e1o1 + e3o3) - o2*(p1*e1 + p3*e3)
s3 = p3*(e1o1 + e2o2) - o3*(p1*e1 + p2*e2)
return pf * vector([s1, s2, s3])
def get_shadow_on_wall(self):
"""
Gibt den dreidimensionalen auf die Hauswand projezierten zweidimensionalen Schattenvektor zurück.
"""
shadow = self.get_shadow().n()
normal = self.get_normal().n()
projection_matrix = wall_projection_matrix(normal)
dimension_of_projection_matrix = projection_matrix.dimensions()
degree_of_shadow = shadow.degree()
assert(dimension_of_projection_matrix == (3, 3)), "Die Projektionsmatrix der Hauswand muss dreidimensional sein."
assert(degree_of_shadow == 3), "Der Schattenvektor muss dreidimensional sein."
projected_shadow = projection_matrix\shadow
x = projected_shadow[0]
y = projected_shadow[1]
shadow_on_wall = vector([x, y])
return shadow_on_wall
def plot_dial(self):
"""
Erstellt ein zweidimensionales Ziffernblatt der Sonnenuhr.
"""
time_window_gmt = range(6, 18)
dial_shadows = []
for time in time_window_gmt:
self.update_day_time(time)
scale = 3
shadow_on_wall = self.get_shadow_on_wall().normalized()
time_indicator = line([(0, 0), [x for x in scale*shadow_on_wall]])
dial_shadows.append(time_indicator)
# Hinzufügen der Indexe zum Ablesen der Tageszeit für jeden Zeitabschnitt.
if shadow_on_wall.norm() > 0:
line_offset = 0.1
position_scalar = 2
position_x = position_scalar*shadow_on_wall[0] + line_offset
position_y = position_scalar*shadow_on_wall[1]
time_display = text(time, (position_x, position_y), color="red")
dial_shadows.append(time_display)
plot_dial_shadows = sum(dial_shadows)
plot_dial_shadows.show(ymin=-2, xmin=-2, xmax=2, aspect_ratio=1, figsize=10)
def model_plot3d(self):
"""
Erstellt eine grafische, dreidimensionale Veranschaulichung des Sonnenuhrenproblems.
"""
# Setzen des Plotbereichs
plot_domain = (-5*self.pointer_length, 5*self.pointer_length)
# Sammlung aller für den Plot erforderlichen Vektoren
normal = self.get_normal().n()
pointer = self.get_pointer().n()
sun_position = self.get_sun_position().n()
shadow = self.get_shadow().n()
# Setzen der Strahlengleichung
var('k')
sun_ray_equation = pointer + k*sun_position
# Grafik der südlichen Hauswand
wall_plot = implicit_plot3d(lambda x,y,z: normal[0]*x + normal[1]*y + normal[2]*z, plot_domain, plot_domain, plot_domain, color='gray', opacity=0.1)
# Grafik des Sonnenstrahls
sun_ray_plot = line3d([sun_ray_equation.substitute(k=plot_domain[0]), sun_ray_equation.substitute(k=plot_domain[1])], color='orange') # Plot des Sonnenstrahls durch die Zeigerspitze.
sun_ray_label = text3d("Sonnenstrahl", (sun_ray_equation.substitute(k=plot_domain[1])[0], sun_ray_equation.substitute(k=plot_domain[1])[1], sun_ray_equation.substitute(k=plot_domain[1])[2]))
# Grafik des Zeigerstabs
pointer_plot = line3d([(0, 0, 0), [x for x in pointer]], color='black') # Plot des Zeigers.
pointer_label = text3d("Zeigerstab", (pointer[0], pointer[1], pointer[2]))
# Grafik der Sonne
sun_position_plot = line3d([(0, 0, 0), [x for x in sun_position]], color='orange') # Plot der Sonne.
sun_position_label = text3d("Sonne", (sun_position[0], sun_position[1], sun_position[2]))
# Grafik des Schattens
shadow_plot = line3d([(0, 0, 0), [x for x in shadow]], color='black') # Plot des Schattens.
shadow_label = text3d("Schatten", (shadow[0], shadow[1], shadow[2]))
# Rückgabe des Plots
model_plot3d = pointer_plot + pointer_label + sun_position_plot + sun_position_label + shadow_plot + shadow_label + sun_ray_plot + sun_ray_label + wall_plot
model_plot3d.show(frame=False, axes=True)
Der nachfolgende Teilbereich wird abschließend auf die Verwendung der einzelnen Klassen, sowie verschiedene unterstützende Veranschaulichungen eingehen.
# Bereitstellung der Basiskonfiguration der Sonnenuhr
config = {
"year": 2020,
"month": 5,
"day": 5,
"longitude": 11.5451628,
"latitude": 48.1540728,
"day_time": 18,
"wall_azimuth": 72,
"pointer_length": 6.5
}
# Instanzierung der Sonnenuhr
sundial = Sundial(config)
# Dynamische Aktualisierung der Tageszeit
sundial.update_day_time(18)
Damit der Sonnenschatten angezeigt und berechnet werden kann, war es erforderlich eine nach Süden hin
ausgerichtete und senkrecht stehende Hauswand zu definieren. Diese Hauswand ist im nachfolgenden Plot
grau
eingefärbt. Der braun dargestellte Zeigerstab wird im nächsten Schritt vom parallel zum
Sonnenvektor verlaufenden orangen
Sonnenstrahl getroffen und markiert mit dem Schnittpunkt
der Hauswand das Ende des schwarz
eingefärbten Schattenvektors.
Beachte: Der grüne
, rote
und blaue
Vektor beschreibt die
x,y,z-Achse des Koordinatensystems.
sundial.model_plot3d()
Nachfolgend ist eine interaktive Grafik implementiert worden. Diese zeigt jeweils zur vollen Stunde eine neue Linie auf dem Ziffernblatt an. Schiebt man den Stundenzeiger wahlweise nach links oder rechts, so lässt sich die Veränderung des Schattens auf dem Ziffernblatt erkennen.
Beachte: Wenn sich die Sonne hinter der Hauswand befindet, so wird durch die
Methode sundial.get_shadow_on_wall()
ein Nullvektor zurückgegeben, was bedeutet, dass auf der
Wand durch die Abwesenheit der Sonne kein Schatten projiziert wird.
from ipywidgets import interact
import ipywidgets as widgets
def interactive_shadow(day_time):
sundial.update_day_time(day_time)
projected_shadow = sundial.get_shadow_on_wall()
plot_projected_shadow = plot(projected_shadow)
plot_projected_shadow.show(xmin=-3, xmax=3, ymin=-3, ymax=0, aspect_ratio=1, figsize=8)
interact(interactive_shadow, day_time=widgets.IntSlider(min=0, max=23, step=1, value=12))
Nachfolgend wird die Spitze des Schattens, welche durch den Zeigerstab und den Sonnenstrahl auf die senkrecht stehende Hauswand projiziert wird, berechnet.
shadow_on_wall = sundial.get_shadow_on_wall()
print("Schattenvektor: {0}".format(shadow_on_wall))
Nachfolgend finden sich die Befehle, um die wichtigesten für die Berechnung verwendeten Vektoren ausgeben zu können:
normal = sundial.get_normal().n()
sun_position = sundial.get_sun_position().n()
pointer = sundial.get_pointer().n()
shadow = sundial.get_shadow().n()
print("Normalenvektor: {0}".format(normal))
print("Sonnenvektor: {0}".format(sun_position))
print("Zeigerstab: {0}".format(pointer))
print("Schattenvektor: {0}".format(shadow))
Nachfolgend wird eine graphische Anzeige des Ziffernblattes generiert. Diese besteht aus allen durch den Zeigerstab auf die Wand geworfenen zweidimensionalen Schattenvektoren zur vollen Stunde. Wenn dieses Ziffernblatt ausgedruckt werden würde, so könnte man anhand der dargestellten Zeitlinien in Kombination mit dem durch den Zeiger auf die Hauswand geworfenen Schatten die aktuelle Tageszeit ablesen.
sundial.plot_dial()
[1] | N.N. (2009): A Walk Through Time - Early Clocks. National Institute of Standards and Technology. Online verfügbar unter https://www.nist.gov/pml/time-and-frequency-division/popular-links/walk-through-time/walk-through-time-early-clocks, zuletzt aktualisiert am 25.06.2019, zuletzt geprüft am 26.04.2020. |
[2] | Zwillinger, Daniel (2003): CRC Standard Mathematical Tables and Formulas, 31st Edition. 31st ed. Milton: CRC Press (Advances in Applied Mathematics). Online verfügbar unter https://ia802900.us.archive.org/16/items/StandardMathematicalTablesAndFormulae31stEditionZwillinger/Standard%20Mathematical%20Tables%20and%20Formulae%2031st%20Edition%20-%20Zwillinger.pdf, zuletzt geprüft am 26.04.2020. |
[3] | Sonnenstand (2020). Online verfügbar unter https://de.wikipedia.org/w/index.php?title=Sonnenstand&oldid=195707610, zuletzt aktualisiert am 11.01.2020, zuletzt geprüft am 26.04.2020. |
[4] | Ibrahim, Reda; Andreas, Afshin (2003): Solar Position Algorithm for Solar Radiation Applications (Revised). National Renewable Energy Laboratory. Online verfügbar unter https://www.nrel.gov/docs/fy08osti/34302.pdf, zuletzt aktualisiert am 23.01.2015, zuletzt geprüft am 26.04.2020. |
[5] | Smith, James (2017): Projection of a Vector upon a Plane from an Arbitrary Angle, via Geometric (Clifford) Algebra. Online verfügbar unter https://vixra.org/pdf/1712.0524v1.pdf, zuletzt geprüft am 02.05.2020. |
[6] | Ruhrländer, Michael (2017): Lineare Algebra für Naturwissenschaftler und Ingenieure. Lehr- und Übungsbuch mit MyMathLab, lineare Algebra. Hallbergmoos: Pearson (Mat - Mathematik). |
Die nachfolgende Herleitung erfolgt nach einer Idee von Smith. [Ref: 5]
$\displaystyle \vec{s} = {\begin{bmatrix} p_{1} + \lambda ☉_{1} \\ p_{2} + \lambda ☉_{2} \\ p_{3} + \lambda ☉_{3} \end{bmatrix}}$
$\displaystyle \lambda = - \frac{\vec{p} \cdot \vec{e}}{\vec{☉} \cdot \vec{e}} = - \frac{p_{1} e_{1} + p_{2} e_{2} + p_{3} e_{3}}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}$
Durch Ersetzen von $\lambda$ mit dem obigen Ausdruck erhält man:
$\displaystyle \vec{s} = {\begin{bmatrix} p_{1} - \frac{p_{1} e_{1} + p_{2} e_{2} + p_{3} e_{3}}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}} ☉_{1} \\ p_{2} - \frac{p_{1} e_{1} + p_{2} e_{2} + p_{3} e_{3}}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}} ☉_{2} \\ p_{3} - \frac{p_{1} e_{1} + p_{2} e_{2} + p_{3} e_{3}}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}} ☉_{3} \end{bmatrix}}$
x-Komponente:
$\displaystyle s_{1} = p_{1} - {\frac{p_{1} e_{1} + p_{2} e_{2} + p_{3} e_{3}}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}} ☉_{1}$
$\displaystyle \,\,\,\,\,\, = p_{1} - {\frac{(p_{1} e_{1} + p_{2} e_{2} + p_{3} e_{3})☉_{1}}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}}$
$\displaystyle \,\,\,\,\,\, = {\frac{p_{1}(☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}) - ☉_{1}(p_{1} e_{1} + p_{2} e_{2} + p_{3} e_{3})}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}}$
$\displaystyle \,\,\,\,\,\, = {\frac{\require{cancel}\cancel{p_{1} ☉_{1} e_{1}} + p_{1} ☉_{2} e_{2} + p_{1} ☉_{3} e_{3} - \cancel{p_{1} ☉_{1} e_{1}} + p_{2} ☉_{1} e_{2} + p_{3} ☉_{1} e_{3}}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}}$
$\displaystyle \,\,\,\,\,\, = {\frac{p_{1}(☉_{2} e_{2} + ☉_{3} e_{3}) - ☉_{1}(p_{2} e_{2} + p_{3} e_{3})}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}}$
y-Komponente:
$\displaystyle s_{2} = p_{2} - {\frac{p_{1} e_{1} + p_{2} e_{2} + p_{3} e_{3}}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}} ☉_{2}$
$\displaystyle \,\,\,\,\,\, = p_{2} - {\frac{(p_{1} e_{1} + p_{2} e_{2} + p_{3} e_{3})☉_{2}}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}}$
$\displaystyle \,\,\,\,\,\, = {\frac{p_{2}(☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}) - ☉_{2}(p_{1} e_{1} + p_{2} e_{2} + p_{3} e_{3})}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}}$
$\displaystyle \,\,\,\,\,\, = {\frac{p_{2} ☉_{1} e_{1} + \require{cancel}\cancel{p_{2} ☉_{2} e_{2}} + p_{2} ☉_{3} e_{3} - p_{1} ☉_{2} e_{1} + \require{cancel}\cancel{p_{2} ☉_{2} e_{2}} + p_{3} ☉_{2} e_{3}}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}}$
$\displaystyle \,\,\,\,\,\, = {\frac{p_{2}(☉_{1} e_{1} + ☉_{3} e_{3}) - ☉_{2}(p_{1} e_{1} + p_{3} e_{3})}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}}$
z-Komponente:
$\displaystyle s_{3} = p_{3} - {\frac{p_{1} e_{1} + p_{2} e_{2} + p_{3} e_{3}}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}} ☉_{3}$
$\displaystyle \,\,\,\,\,\, = p_{3} - {\frac{(p_{1} e_{1} + p_{2} e_{2} + p_{3} e_{3})☉_{3}}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}}$
$\displaystyle \,\,\,\,\,\, = {\frac{p_{3}(☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}) - ☉_{3}(p_{1} e_{1} + p_{2} e_{2} + p_{3} e_{3})}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}}$
$\displaystyle \,\,\,\,\,\, = {\frac{p_{3} ☉_{1} e_{1} + p_{3} ☉_{2} e_{2} + \require{cancel}\cancel{p_{3} ☉_{3} e_{3}} - p_{1} ☉_{3} e_{1} + p_{2} ☉_{3} e_{2} + \require{cancel}\cancel{p_{3} ☉_{3} e_{3}}}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}}$
$\displaystyle \,\,\,\,\,\, = {\frac{p_{3}(☉_{1} e_{1} + ☉_{2} e_{2}) - ☉_{3}(p_{1} e_{1} + p_{2} e_{2})}{☉_{1} e_{1} + ☉_{2} e_{2} + ☉_{3} e_{3}}}$
Die x,y,z-Komponenten $s_{1}$, $s_{2}$ und $s_{3}$ bilden nun den Schattenvektor $\vec{s}$, welcher wie folgt definiert wird:
$\displaystyle \vec{s} = \frac{1}{e_{1} ☉_{1} + e_{2} ☉_{2} + e_{3} ☉_{3}} \cdot {\begin{bmatrix}p_{1}(☉_{2} e_{2} + ☉_{3} e_{3}) - ☉_{1} (p_{2} e_{2} + p_{3} e_{3})\\p_{2} (☉_{1} e_{1} + ☉_{3} e_{3}) - ☉_{2} (p_{1} e_{1} + p_{3} e_{3})\\p_{3} (☉_{1} e_{1} + ☉_{2} e_{2}) - ☉_{3} (p_{1} e_{1} + p_{2} e_{2})\\\end{bmatrix}}$
Bearbeitung durch Konrad Moron
Bearbeitung durch Stefan Kühnel
Bearbeitung durch Stefan Kühnel
Bearbeitung durch Konrad Moron
Copyright (c) 2020 Stefan Kühnel; Konrad Moron, Alle Rechte vorbehalten, soweit nicht ausdrücklich anders vermerkt.