Closed MtnBiker closed 2 months ago
Hi, thank you for suggesting this feature, this is an interesting one indeed. I have multiple things in mind about this problem.
The first is a small frustration from myself, because I don't know how to calculate this time directly. astronoby
implements methods that return times for some events, but I have to be honest and say I'm only converting math formulas from the books I read into Ruby, without actually understanding the math/trigonometry behind. It is something I would like to fix, but I don't know if I'll ultimately be able to. It would maybe be similar to finding the transit time, so I'll try this angle first.
Speaking of transit time, my second point is that, luckily for you, 190° is very close to 180°, which is South and also exactly the azimuth angle when the solar transit happens. The solar transit is when the Sun's the highest in the sky, which happens when it crosses the local meridian (the imaginary plane that goes through the the observer and the North-South axis). Therefore, your exact alignment should be a few minutes later than the solar transit:
observer = Astronoby::Observer.new(
latitude: Astronoby::Angle.from_degrees(38.5816),
longitude: Astronoby::Angle.from_degrees(-121.4944)
)
sun = Astronoby::Sun.new(time: Time.now)
observation_events = sun.observation_events(observer: observer)
observation_events.transit_time.localtime("-07:00")
# => 2024-05-03 13:02:46 -0700
observation_events.transit_altitude.degrees
# => 67.4190973684892
My third point is that there is still a way to get a pretty precise time, from an (inefficient) iteration method. Basically finding the hour when the Sun will cross a defined azimuth angle, then finding the minute. This is absolutely inefficient, but I believe it would work if efficiency is not a requirement for your program. Here is an (even more inefficient) example:
aligned_azimuth_angle = Astronoby::Angle.from_degrees(190)
hour = (0..23).to_a.map do |hour|
[
hour,
sun
.apparent_ecliptic_coordinates
.to_apparent_equatorial(epoch: Astronoby::Epoch.from_time(Time.now))
.to_horizontal(
time: Time.new(2024, 5, 3, hour, 0, 0, "-07:00"),
latitude: observer.latitude,
longitude: observer.longitude
)
.azimuth
]
end.to_h.sort_by { _2 }.select { _1.last < aligned_azimuth_angle }.last.first
minute = (0..59).to_a.map do |minute|
[
minute,
sun
.apparent_ecliptic_coordinates
.to_apparent_equatorial(epoch: Astronoby::Epoch.from_time(Time.now))
.to_horizontal(
time: Time.new(2024, 5, 3, hour, minute, 0, "-07:00"),
latitude: observer.latitude,
longitude: observer.longitude
)
.azimuth
]
end.to_h.sort_by { _2 }.select { _1.last < aligned_azimuth_angle }.last.first
Time.new(2024, 5, 3, hour, minute, 0, "-07:00")
# => 2024-05-03 13:17:00 -0700
# Check Sun's azimuth angle for this time, for good measure
# Also get the altitude
horizontal_coordinates = sun
.apparent_ecliptic_coordinates
.to_apparent_equatorial(epoch: Astronoby::Epoch.from_time(Time.now))
.to_horizontal(time: Time.new(2024, 5, 3, 13, 17, 0, "-07:00"),latitude: observer.latitude,longitude: observer.longitude)
horizontal_coordinates.azimuth.degrees
# => 189.9152435999221
horizontal_coordinates.altitude.degrees
# => 67.01430525975873
As we can see, this is only 15 minutes later than the transit time, but it depends on how accurate you need this time to be. You can also implement the same logic for the second if you want to find the exact instant.
I am not proud of this code, but it can technically answer your problem while a more efficient method is implemented out of the box.
If anyone else reads this issue and has the right trigonometry formulas for this problem, I'll be happy to have a look and eventually implement a new method on Astronoby::Events::ObservationEvent
.
Merci. I didn't Ruby well enough to work out what you did for the iteration easily, so I did it manually for one case, but now you gave me a better way.
You're up against one very complicated problem which is how the earth moves. And the other one is time and how Ruby handles it. It's daylight saving time here (which we now know is pretty useless but in the US politically difficult to change) which adds another complication.
puts "#{time} #{time.localtime}"
gives a different answer than puts "#{time.localtime} #{time}"
.
I'm happy to help write a better code than the one from my examples, they're not optimised. 😅
We have the same problem in France, we change time zone every 6 months, between CET (UTC+01:00) and CEST (UTC+02:00) which sounds very weird. Indeed in my examples I used a fixed UTC offset but in general we use time zone objects.
Either if we already know it:
require "tzinfo"
tz = TZInfo::Timezone.get("America/Los_Angeles")
time = Time.new(2024, 5, 4, 12, 0, 0, tz)
# => 2024-05-04 12:00:00 -0700
now = tz.now
# => 2024-05-03 08:37:21.876598 -0700
Or from coordinates with gems like wheretz
:
require "wheretz"
require "tzinfo"
tz = WhereTZ.get(38.5816, -121.4944)
time = Time.new(2024, 5, 4, 12, 0, 0, tz)
# => 2024-05-04 12:00:00 -0700
now = tz.now
# => 2024-05-03 08:37:21.876598 -0700
In Astronoby, times are always given in UTC so that the conversion is handled by the program using the library, at the end of all calculations. But it accepts any Time
object, not necessarily in UTC.
At 9:27 am, PDT
tz = TZInfo::Timezone.get("America/Los_Angeles")
time = Time.new(2024, 5, 4, 12, 0, 0, tz)
# => 2024-05-04 12:00:00 -0700
puts now = tz.now
# => 2024-05-03 08:37:21.876598 -0700
puts Time.now
# => 2024-05-03 09:27:27 -0700
2024-05-03 08:37:21.876598 -0700 which is one of the Ruby confusions.
Oh yes I forgot to mention that. Without a time zone argument, Time.new
defaults to the machine's timezone.
For me:
Time.new
# => 2024-05-03 22:02:56.117456 +0200
With now arguments, Time.new
is an alias for Time.now
.
I agree this is confusing, that's why we usually only deal with times in UTC, and ultimately convert them to a local time zone at the very end.
Closing for inactivity.
Problem this feature will solve
Not a big problem, but curious when my solar panels are aligned with the sun. In my case they are oriented approx. 190°
Desired solution
For a given azimuth, get time and altitude
Alternatives considered
Trial and error
Thank you Rémy, this is a great resource