We just moved to a new place with large windows, and Marcy (our dog of unspecified breeding) has taken full advantage of the sunlight.
If she had one gripe though, it’s likely with having to balance body temperature (which increases the longer she basks in the sun) with comfort. Over the course of a Sunday, I’ve seen seen her move ~15 times to optimally position herself, taking these variables and more into consideration.
I can only conclude that in the limited set of neurons in her head, there’s some configuration that resembles a star tracker.
Dog Tracking
Given this, it should be possible to model some heuristics with which I can quickly figure out where in the apartment she currently is based on some score we assign each window.
julia
has a package (AstroLib
) with a function that can get us the Right Ascenscion (RA) and Declination of the Sun given a Julian date. You can think of them as coordinates on a celestial sphere, with Earth at the center.
A Julian date gives the number of days since the last Julian epoch. While I wish there was some historical significance to it, I found that Joseph Scalinger created the system in 1583 to get the least common multiple between three cycles - the indiction (Roman tax cycle of 15-years), the solar cycle (28-years) and the lunar cycle (19-year). The least common multiple is 7980 years, and so the Julian calendar starts at 01/01/4713BC.
It’s a simple system with the complexities of other calendars (like leap years in the Gregorian calendar) sidestepped, allowing for counting time continuously in days and fractions.
With these values in hand, we can convert to local horizon coordinates with a given latitude and longitude. If you’re curious about how that calculation works, I recommend giving NASA Goddard’s IDL Astronomy User Library a read.
struct RoomInfo
name::String
facing_azimuth::Float64
end
function GetSunlightScores(
timestamp::DateTime,
lat::Float64,
lon::Float64,
rooms::Vector{RoomInfo},
)::Dict{String, Float64}
jd = AstroLib.jdcnv(timestamp)
ra, dec, = AstroLib.sunpos(jd) # Assuming only jd needed
# alt/az/ha in degrees - az is measured East from North
alt, az, ha = AstroLib.eq2hor(ra, dec, jd, lat, lon)
room_scores = Dict{String, Float64}()
println("Timestamp: $timestamp, Sun Alt: $(round(alt, digits=1))°, Sun Az: $(round(az, digits=1))°")
for room in rooms
score = 0.0
is_sun_up = alt > 2.0
# Is the sun in front of the window?
delta_az = abs(room.facing_azimuth - az)
if delta_az > 180.0
delta_az = 360.0 - delta_az # Correct for wrap-around
end
is_azimuth_match = delta_az < 80.0
# --- Assign Score ---
if is_sun_up && is_azimuth_match
# cosd() used for directness of sunlight
# sind() used for intensity of sunlight
directness_factor = cosd(delta_az)
altitude_factor = sind(alt)
score = max(0.0, directness_factor * altitude_factor)
end
room_scores[room.name] = score
end
return room_scores
end
The final piece is a quick calculation, where we draw a triangle with:
- the vertex of the right angle triangle at my window,
- a line drawn from my window to the sun, and then
- back down to the horizon to complete it.
With this, we can add a simple score for intensity (how high the sun is) + “directness” (how close the sun is).
Putting it all together and plotting it per window in the apartment:
Not bad, but predictions are close enough because both windows face the same direction w/ very little (~10deg) difference in angles. One way to reconcile this is by penalizing for preference (Marcy preferring a couch over a bed, proximity to humans) or by factoring in inertia/switching costs. Once we start adding in controllable and tunable parameters like this, our model of Marcy starts to resemble a star tracker.
Star Trackers
Star Trackers on spacecraft operate with similar logic (ish). They’re devices that are usually included as part of the ADCS (Attitude Determination and Control Systems) suite on spacecraft. Like navigators of old, ephemeris are cross referenced with observations to determine position and orientation.
There’s a lot of interesting things going on behind star trackers. I’ll leave the trig behind them for a future post, so I’ll end all of this by talking about the Lost in Space (LIS) condition that can affect spacecraft.
Unlike sun sensors or magnetometers, all you need with a star tracker are two points in its field of vision (FOV) to correctly determine position. This allows the spacecraft to know where it is anywhere in space given it can calculate the angular separation between the stars in the FOV and then match them to a pre-loaded catalogue of angles between stars.
Since ID and accuracy are important here, errors make attitude determination a challenge. Thankfully some very smart people have been thinking about this problem for a while, and there’s a number of methods - such as using the magnitude of the stars, using a priori information, binning stars and more.
My ex-PI at SUNY Buffalo exploits geometric features of the triangle formed by identified stars in the FOV to make determination even faster. It’s worth a read!