I want to calculate the solar radiation on walls which are oriented towards north, south ect. I have the solar radiation for the location and I have tried the following equation:
S_module = S_incident * (math.cos(alpha)*math.sin(beta)*math.cos(psi-theta) + math.sin(alpha)*math.cos(beta))
with
alpha = 24.86 #sun elevation angle
theta = 81.58 #sun azimuth angle
beta = 90 #wall tilt angle (vertical module beta=90)
psi = 135 #surface orientation: psi=0 == facing north, psi=180 == facing south; is the azimuth angle that the module faces
S_incident = 663 [W]
this is for 2019-6-21 6h timezone=utc
I tried the python package pysolar and the equation above was found here: https://www.pveducation.org/pvcdrom/properties-of-sunlight/arbitrary-orientation-and-tilt
My problem is that I did not get correct results, e.g. for the above properties the result is -495 [W]. This could not be true because in the morning there must be at least a small positive value for the radiation.
Thanks for any hints!
Read the docs: trig functions work in radians; you fed it degrees. Your 90-radian wall doesn't match reality. :-)
import math
alpha = 24.86 #sun elevation angle
theta = 81.58 #sun azimuth angle
beta = 90 #wall tilt angle (vertical module beta=90)
psi = 135 #surface orientation: psi=0 == facing north, psi=180 == facing south; is the azimuth angle that the module faces
S_incident = 663 # Watts
S_module = S_incident * (math.cos(alpha)*math.sin(beta)*math.cos(psi-theta) +
math.sin(alpha)*math.cos(beta))
print(math.cos(alpha), math.sin(alpha))
print(math.cos(beta), math.sin(beta))
Output:
0.9630361043608977 -0.26937234768510715
-0.4480736161291701 0.8939966636005579
These are obviously not the values you expected.
Convert with the factor math.pi / 180.
Related
I would like to make some kind of solar system in pygame. I've managed to do a fixed one but I thought it would be more interesting to do one with planets moving around the sun and moons around planets etc. Is there a way I could do that (using pygame if possible)?
What I would like is :
Sun = pygame.draw.circle(...)
planet1 = pygame.draw.circle(...)
etc.
a = [planet1, planet2, ...]
for p in a:
move p[2] to pos(x, y)
That is what I think would work but I'm not sure how to do it. Also, I've thought about deleting the ancient planet and drawing a new one right next to it, but problem is I'm using random features (like colours, distance to the sun, number of planets in the system etc.) and it would have to keep these same features. Any ideas?
Thanks in advance!
You can implement gravity with Newton's Law of Universal Gravitation and Newton's Second Law to get the accelerations of the planets. Give each planet an initial position, velocity and mass. Acceleration is change in velocity a = v * dt, velocity is change in position v = r * dt, so we can integrate to find velocity and position.
Universal gravitation: F = G * m1 * m2 / r ** 2 where F is the magnitude of the force on the object, G is the gravitational constant, m1 and m2 are the masses of the objects and r is the distance between the two objects.
Newton's Second Law: F = m1 * a where a is the acceleration.
dt = 0.01 # size of time step
G = 100 # gravitational constant
def calcGravity(sun, planet):
'Returns acceleration of planet with respect to the sun'
diff_x = sun.x - planet.x
diff_y = sun.y - planet.y
acceleration = G * sun.mass / (diff_x ** 2 + diff_y ** 2)
accel_x = acceleration * diff_x / (diff_x ** 2 + diff_y ** 2)
accel_y = acceleration * diff_y / (diff_x ** 2 + diff_y ** 2)
return accel_x, accel_y
while True:
# update position based on velocity
planet.x += planet.vel_x * dt
planet.y += planet.vel_y * dt
# update velocity based on acceleration
accel_x, accel_y = calcGravity(sun, planet)
planet.vel_x += accel_x * dt
planet.vel_y += accel_y * dt
This can produce circular and elliptical orbits. Creating an orbiting moon requires a very small timestep (dt) for the numeric integration.
Note: this approach is subtly inaccurate due to the limits of numeric integration.
Sample implementation in pygame here, including three planets revolving around a sun, a moon, and a basic orbital transfer.
https://github.com/c2huc2hu/orbital_mechanics
Coordinates of a planet rotated about the Sun through some angle with respect to the X-axis are , where r is the distance to the Sun, theta is that angle, and (a, b) are the coordinates of the sun. Draw your circle centered at (x, y).
EDIT:
General elliptical orbit:
Where
r0 is the radius of a circular orbit with the same angular momentum, and e is the "eccentricity" of the ellipse
I have the following code. This code is simulation of orbiting objects around other objects, E.g. Solar system. As you run it, the objects orbit in circular trajectory.
import math
from vpython import *
lamp = local_light(pos=vector(0,0,0), color=color.yellow)
# Data in units according to the International System of Units
G = 6.67 * math.pow(10,-11)
# Mass of the Earth
ME = 5.973 * math.pow(10,24)
# Mass of the Moon
MM = 7.347 * math.pow(10,22)
# Mass of the Mars
MMa = 6.39 * math.pow(10,23)
# Mass of the Sun
MS = 1.989 * math.pow(10,30)
# Radius Earth-Moon
REM = 384400000
# Radius Sun-Earth
RSE = 149600000000
RMS = 227900000000
# Force Earth-Moon
FEM = G*(ME*MM)/math.pow(REM,2)
# Force Earth-Sun
FES = G*(MS*ME)/math.pow(RSE,2)
# Force Mars-Sun
FEMa = G*(MMa*MS)/math.pow(RMS,2)
# Angular velocity of the Moon with respect to the Earth (rad/s)
wM = math.sqrt(FEM/(MM * REM))
# Velocity v of the Moon (m/s)
vM = wM * REM
print("Angular velocity of the Moon with respect to the Earth: ",wM," rad/s")
print("Velocity v of the Moon: ",vM/1000," km/s")
# Angular velocity of the Earth with respect to the Sun(rad/s)
wE = math.sqrt(FES/(ME * RSE))
# Angular velocity of the Mars with respect to the Sun(rad/s)
wMa = math.sqrt(FEMa/(MMa * RMS))
# Velocity v of the Earth (m/s)
vE = wE * RSE
# Velocity v of the Earth (m/s)
vMa = wMa * RMS
print("Angular velocity of the Earth with respect to the Sun: ",wE," rad/s")
print("Velocity v of the Earth: ",vE/1000," km/s")
# Initial angular position
theta0 = 0
# Position at each time
def positionMoon(t):
theta = theta0 + wM * t
return theta
def positionMars(t):
theta = theta0 + wMa * t
return theta
def positionEarth(t):
theta = theta0 + wE * t
return theta
def fromDaysToS(d):
s = d*24*60*60
return s
def fromStoDays(s):
d = s/60/60/24
return d
def fromDaysToh(d):
h = d * 24
return h
# Graphical parameters
print("\nSimulation Earth-Moon-Sun motion\n")
days = 365
seconds = fromDaysToS(days)
print("Days: ",days)
print("Seconds: ",seconds)
v = vector(384,0,0)
E = sphere(pos = vector(1500,0,0), color = color.blue, radius = 60, make_trail=True)
Ma = sphere(pos = vector(2300,0,0), color = color.orange, radius = 30, make_trail=True)
M = sphere(pos = E.pos + v, color = color.white,radius = 10, make_trail=True)
S = sphere(pos = vector(0,0,0), color = color.yellow, radius=700)
t = 0
thetaTerra1 = 0
dt = 5000
dthetaE = positionEarth(t+dt)- positionEarth(t)
dthetaM = positionMoon(t+dt) - positionMoon(t)
dthetaMa = positionMars(t+dt) - positionMars(t)
print("delta t:",dt,"seconds. Days:",fromStoDays(dt),"hours:",fromDaysToh(fromStoDays(dt)),sep=" ")
print("Variation angular position of the Earth:",dthetaE,"rad/s that's to say",degrees(dthetaE),"degrees",sep=" ")
print("Variation angular position of the Moon:",dthetaM,"rad/s that's to say",degrees(dthetaM),"degrees",sep=" ")
while t < seconds:
rate(500)
thetaEarth = positionEarth(t+dt)- positionEarth(t)
thetaMoon = positionMoon(t+dt) - positionMoon(t)
thetaMars = positionMars(t+dt) - positionMars(t)
# Rotation only around z axis (0,0,1)
E.pos = rotate(E.pos,angle=thetaEarth,axis=vector(0,1,0))
Ma.pos = rotate(Ma.pos,angle=thetaMars,axis=vector(0,1,0))
v = rotate(v,angle=thetaMoon,axis=vector(0,1,0))
M.pos = E.pos + v
t += dt
I am wondering How to change the path of orbit to elliptical?
I have tried several ways but I could not manage to find any solution.
Thank you.
Thank you
This seems like more of a physics issue as opposed to a programming issue. The problem is that you are assuming that each of the orbits are circular when calculating velocity and integrating position linearly (e.g v * dt). This is not how you would go about calculating the trajectory of an orbiting body.
For the case of simplicity, we will assume all the masses are point masses so there aren't any weird gravity gradients or attitude dynamics to account for.
From there, you can refer to this MIT page. (http://web.mit.edu/12.004/TheLastHandout/PastHandouts/Chap03.Orbital.Dynamics.pdf) on orbit dynamics. On the 7th page, there is an equation relating the radial position from your centerbody as a function of a multitude of orbital parameters. It seems like you have every parameter except the eccentricity of the orbit. You can either look that up online or calculate it if you have detailed ephemeral data or apoapsis/periapsis information.
From that equation, you will see a phi - phi_0 term in the denominator. That is colloquially known as the true anomaly of the satellite. Instead of time, you would iterate on this true anomaly parameter from 0 to 360 to find your radial distance, and from true anomaly, inclination, right angle to the ascending node, and the argument of periapses, you can find the 3D cartesian coordinates at a specific true anomaly.
Going from true anomaly is a little less trivial. You will need to find the eccentric anomaly and then the mean anomaly at each eccentric anomaly step. You now have mean anomaly as a function of time. You can linearly interpolate between "nodes" at which you calculate the position with v * dt. You can calculate the velocity from using the vis-viva equation and dt would be the difference between the calculated time steps.
At each time step you can update the satellite's position in your python program and it will properly draw your trajectories.
For more information of the true anomaly, wikipedia has a good description of it: https://en.wikipedia.org/wiki/True_anomaly
For more information about orbital elements (which are needed to convert from radial position to cartesian coordinates): https://en.wikipedia.org/wiki/Orbital_elements
How would I calculate the area below an EarthSatellite so that I can plot the swath of land covered as the satellite passes over?
Is there anything in Skyfield that would facilitate that?
Edit: Just thought I'd clarify what I mean by area below the satellite. I need to plot the maximum area below the satellite possible to observe given that the Earth is a spheroid. I know how to plot the satellite path, but now I need to plot some lines to represent the area visible by that satellite as it flies over the earth.
Your edit made it clear what you want. The visible area from a satellite can be easily calculated (when the earth is seen as a sphere). A good source to get some background on the visible portion can be found here. To calculate the visible area when the earth is seen as an oblate spheroid will be a lot harder (and maybe even impossible). I think it's better to reform that part of the question and post it on Mathematics.
If you want to calculate the visible area when the earth is seen as a sphere we need to make some adjustments in Skyfield. With a satellite loaded using the TLE api you can easily get a sub point with the position on earth. The library is calling this the Geocentric position, but actually it's the Geodetic position (where the earth is seen as an oblate spheroid). To correct this we need to adjust subpoint of the Geocentric class to use the calculation for the Geocentric position and not the Geodetic position. Due to a bug and missing information in the reverse_terra function we also need to replace that function. And we need to be able to retrieve the earth radius. This results in the following:
from skyfield import api
from skyfield.positionlib import ICRF, Geocentric
from skyfield.constants import (AU_M, ERAD, DEG2RAD,
IERS_2010_INVERSE_EARTH_FLATTENING, tau)
from skyfield.units import Angle
from numpy import einsum, sqrt, arctan2, pi, cos, sin
def reverse_terra(xyz_au, gast, iterations=3):
"""Convert a geocentric (x,y,z) at time `t` to latitude and longitude.
Returns a tuple of latitude, longitude, and elevation whose units
are radians and meters. Based on Dr. T.S. Kelso's quite helpful
article "Orbital Coordinate Systems, Part III":
https://www.celestrak.com/columns/v02n03/
"""
x, y, z = xyz_au
R = sqrt(x*x + y*y)
lon = (arctan2(y, x) - 15 * DEG2RAD * gast - pi) % tau - pi
lat = arctan2(z, R)
a = ERAD / AU_M
f = 1.0 / IERS_2010_INVERSE_EARTH_FLATTENING
e2 = 2.0*f - f*f
i = 0
C = 1.0
while i < iterations:
i += 1
C = 1.0 / sqrt(1.0 - e2 * (sin(lat) ** 2.0))
lat = arctan2(z + a * C * e2 * sin(lat), R)
elevation_m = ((R / cos(lat)) - a * C) * AU_M
earth_R = (a*C)*AU_M
return lat, lon, elevation_m, earth_R
def subpoint(self, iterations):
"""Return the latitude an longitude directly beneath this position.
Returns a :class:`~skyfield.toposlib.Topos` whose ``longitude``
and ``latitude`` are those of the point on the Earth's surface
directly beneath this position (according to the center of the
earth), and whose ``elevation`` is the height of this position
above the Earth's center.
"""
if self.center != 399: # TODO: should an __init__() check this?
raise ValueError("you can only ask for the geographic subpoint"
" of a position measured from Earth's center")
t = self.t
xyz_au = einsum('ij...,j...->i...', t.M, self.position.au)
lat, lon, elevation_m, self.earth_R = reverse_terra(xyz_au, t.gast, iterations)
from skyfield.toposlib import Topos
return Topos(latitude=Angle(radians=lat),
longitude=Angle(radians=lon),
elevation_m=elevation_m)
def earth_radius(self):
return self.earth_R
def satellite_visiable_area(earth_radius, satellite_elevation):
"""Returns the visible area from a satellite in square meters.
Formula is in the form is 2piR^2h/R+h where:
R = earth radius
h = satellite elevation from center of earth
"""
return ((2 * pi * ( earth_radius ** 2 ) *
( earth_radius + satellite_elevation)) /
(earth_radius + earth_radius + satellite_elevation))
stations_url = 'http://celestrak.com/NORAD/elements/stations.txt'
satellites = api.load.tle(stations_url)
satellite = satellites['ISS (ZARYA)']
print(satellite)
ts = api.load.timescale()
t = ts.now()
geocentric = satellite.at(t)
geocentric.subpoint = subpoint.__get__(geocentric, Geocentric)
geocentric.earth_radius = earth_radius.__get__(geocentric, Geocentric)
geodetic_sub = geocentric.subpoint(3)
print('Geodetic latitude:', geodetic_sub.latitude)
print('Geodetic longitude:', geodetic_sub.longitude)
print('Geodetic elevation (m)', int(geodetic_sub.elevation.m))
print('Geodetic earth radius (m)', int(geocentric.earth_radius()))
geocentric_sub = geocentric.subpoint(0)
print('Geocentric latitude:', geocentric_sub.latitude)
print('Geocentric longitude:', geocentric_sub.longitude)
print('Geocentric elevation (m)', int(geocentric_sub.elevation.m))
print('Geocentric earth radius (m)', int(geocentric.earth_radius()))
print('Visible area (m^2)', satellite_visiable_area(geocentric.earth_radius(),
geocentric_sub.elevation.m))
I'd like to write Python code to specify the orbit of a satellite with Keplerian elements, specify a point on the Earth with latitude, longitude, and altitude, specify a time, and compute the angle between two vectors:
- the vector from the satellite to the specified point on the Earth
- the vector from the satellite to the center of the Earth.
I know I can use poliastro to define the orbit and propagate it to the specified time. The hard part is representing the satellite and the Earth point in the same coordinate system.
poliastro currently doesn't specify a coordinate system. Someone in their chat room told me that Earth orbits are in GCRS. astropy can convert GCRS to ITRS, which is an Earth-centered Earth-fixed frame:
import math
import numpy as np
from astropy import units as u
from astropy.time import Time
from poliastro.bodies import Earth
from poliastro.twobody import Orbit
from astropy.coordinates import SkyCoord
def lla2ecef(lat, lon, alt):
""" Convert lat/lon/alt to cartesian position in ECEF frame.
Origin is center of Earth. +x axis goes through lat/lon (0, 0).
+y axis goes through lat/lon (0, 90). +z axis goes through North Pole.
lat: number, geodetic latitude in degrees
lon: number, longitude in degrees
alt: number, altitude above WGS84 ellipsoid, in km
Returns: tuple (x, y, z) coordinates, in km.
Source: "Department of Defense World Geodetic System 1984"
Page 4-4
National Imagery and Mapping Agency
Last updated June, 2004
NIMA TR8350.2
"""
lon = lon * math.pi/180.0 # Convert to radians
lat = lat * math.pi/180.0 # Convert to radians
# WGS84 ellipsoid constants:
a = 6378.137 #equatorial radius, in km
e = 8.1819190842622e-2
# intermediate calculation: prime vertical radius of curvature
N = a/math.sqrt(1 - e**2*math.sin(lat)**2)
#results
x = (N + alt)*math.cos(lat)*math.cos(lon)
y = (N + alt)*math.cos(lat)*math.sin(lon)
z = ((1 - e**2)*N + alt)*math.sin(lat)
return (x, y, z)
epoch = Time(2018, format='decimalyear', scale='tai') #01-01-2018 00:00:00, in TAI
propagation_time = 9000 #seconds
semi_major_axis = 10000 #km
eccentricity = 0.1
inclination = 50 #deg
raan = 70 #deg
arg_perigee = 60 #deg
true_anomaly = 80 #deg
orbit = Orbit.from_classical(
Earth,
semi_major_axis*u.km,
eccentricity*u.one,
inclination*u.deg, raan*u.deg,
arg_perigee*u.deg,
true_anomaly*u.deg,
epoch)
propagated_orbit = orbit.propagate(propagation_time*u.s)
pos_gcrs = propagated_orbit.state.r
sky_gcrs = SkyCoord(
representation_type='cartesian',
x=pos_gcrs[0], y=pos_gcrs[1], z=pos_gcrs[2],
frame='gcrs',
obstime=(epoch + propagation_time*u.s))
pos_ecef = sky_gcrs.transform_to('itrs')
pos_s = np.array((pos_ecef.x.to(u.km).value,
pos_ecef.y.to(u.km).value,
pos_ecef.z.to(u.km).value))
lat = 40 #deg
lon = 50 #deg
alt = 0.06 #km
pos_t = np.array(lla2ecef(lat, lon, alt))
#Compute angle at satellite between target and center of earth
v1 = pos_t - pos_s
v2 = -pos_s
angle = math.acos(np.dot(v1, v2)/(np.linalg.norm(v1)*np.linalg.norm(v2)))
#convert to degrees
angle = angle*180.0/math.pi
I am trying to create a rotating turret. The turret rotates correctly the problem is when I make the turret shoot with the space bar the bullet isn't the same size or shape at every angle. I tried using the angle that the turret is facing to do some trig calculations and find the two corner points needed to create the bullet (which is a circle). Nothing I have tried will work. Here is the code:
Barrel = [260,210,270,210,270,170,260,170]
def RotateBarrel():
global angle
angleChange = 2
mountCenterX = 265
mountCenterY = 215
#Rotate Barrel
cycle = 1
while cycle < len(Barrel):
x = Barrel[cycle-1]-mountCenterX
y = Barrel[cycle]-mountCenterY
Barrel[cycle-1] = (x*math.cos(angleChange*math.pi/180)-y*math.sin(angleChange*math.pi/180))+mountCenterX
Barrel[cycle] = (x*math.sin(angleChange*math.pi/180)+y*math.cos(angleChange*math.pi/180))+mountCenterY
cycle += 2
angle += angleChange
if angle == 360: angle = 0
canvas.coords(barrel,Barrel)
self.after(1,RotateBarrel)
def SpinningShoot(event):
global angle
speed = 10
shotXpos = Barrel[6]+10*(math.cos(angle*math.pi/180))
shotYpos = Barrel[7]-10*(math.sin(angle*math.pi/180))
cornerX = Barrel[6]+10*(math.cos((90-angle)*math.pi/180))
cornerY = Barrel[7]-10*(math.sin((90-angle)*math.pi/180))
shot = canvas.create_oval(shotXpos,shotYpos,cornerX,cornerY,fill="white")
Xmotion = speed*math.cos(angle*math.pi/180)
Ymotion = speed*math.sin(angle*math.pi/180)
Shots.append(shot)
ShotsPos.append(shotXpos)
ShotsPos.append(shotYpos)
ShotsMotion.append(Xmotion)
ShotsMotion.append(Ymotion)
It looks to me that your shot is not going to be centered on the "barrel", but the calculation using (90-angle) is going to give you an angular width for the shot of
angle - ( 90 - angle )
which is 2 * angle - 90
(ie the shot will be wider depending on the size of the angle).
I would have thought use (angle - 45 ) and (angle + 45 ) so your shot is always the same angular width and centered on the barrel.
You would also need to increase the "radius" for the second corner. Im confused about the +10 and -10, not quite sure what they would do.
Probably a better approach is to calculate the "centre" of the bullet and then from that just draw the circle centered on it. Im sure there would be a function that takes the centre and radius. So have
radius=10
centrex= radius * cos ( angle * math.pi /180 )
centrey= radius * sin ( angle * math. pi / 180 )
and then pass those two and a radius to a function that does a circle
One other little thing, I would suggest changing the line
if angle == 360: angle = 0
to
if angle >= 360: angle = angle-360
as if angle was initialized as anything other than an even number or you changed the angle step you could "miss" the 360 and then never wrap back around.