Skip to content

Instantly share code, notes, and snippets.

@pukpr
Created September 7, 2025 04:13
Show Gist options
  • Save pukpr/ef590090364bd3de76b7a9d551fdda18 to your computer and use it in GitHub Desktop.
Save pukpr/ef590090364bd3de76b7a9d551fdda18 to your computer and use it in GitHub Desktop.
Animation of QBO, opens window in Browser
from vpython import *
Running = True
def exit_app(evt):
global Running
if evt.key == 'q': # Press 'q' to quit
print("Exiting simulation...")
Running = False
scene.bind('keydown', exit_app)
scene.width = 1920
scene.height = 1080
scene.range = 100
earth = sphere(pos=vector(0,0,0), radius=5, color=color.blue)
ring = ring(pos=earth.pos, axis=vector(0,1,0), radius=7, thickness=0.5, color=color.red)
# Arrow position: place it on the edge of the ring
arrow_pos = vector(0, 0, 10)
# Create the directional arrow
arrow_obj = arrow(pos=arrow_pos, axis=vector(0, -1, 0), shaftwidth=0.5, color=color.white)
sun = sphere(radius=2, color=color.yellow, make_trail=True, trail_radius=0.2, retain=30)
moon = sphere(radius=1, color=color.white, make_trail=True, trail_radius=0.1, retain=10)
timer_label = label(pos=vector(0,15,0), text='t = 0.0 yr', height=10, box=False, color=color.white)
annual_label = label(pos=vector(0,20,0), text='declination', height=10, box=False, color=color.white)
obliquity = radians(23.44)
moon_incl = radians(5.145*3)
year = 365.2422
draconic = 27.2122
t = 0
dt = 0.1
# Interval in days
interval = 1.185 * year
# toggle = True
while Running:
rate(600)
sun.pos = vector(50*cos(2*pi*t/year),
50*sin(2*pi*t/year)*sin(obliquity),
50*sin(2*pi*t/year)*cos(obliquity))
moon.pos = vector(10*cos(2*pi*t/draconic),
10*sin(2*pi*t/draconic)*sin(moon_incl),
10*sin(2*pi*t/draconic)*cos(moon_incl))
# Color toggle every 1.185 years
cycle = int(t // interval)
if cycle % 2 == 0:
ring.color = color.green
arrow_obj.axis = vector(5, 0, 0) # CW: tangent points downward
else:
ring.color = color.red
arrow_obj.axis = vector(-5, 0, 0) # CW: tangent points upward
annual_label.color = ring.color
if abs((t/year) % 1) < dt/year:
# This block runs only when t is approximately an integer multiple of 'year'
print(f"Year marker reached: {t/year:.0f}")
annual_label.text = f'decl = {5*cos(2*pi*t/draconic):.2f}°'
# annual_label.axis = vector(cos(5*cos(2*pi*t/draconic)*2*pi/180), sin(5*cos(2*pi*t/draconic)*2*pi/180), 0)
t += dt
timer_label.text = f'time = {t/year:.2f} yr'
@pukpr
Copy link
Author

pukpr commented Sep 7, 2025

[

Screen.Recording.2025-09-06.232242.mp4

](url)

When lunar declination is positive during an annual synching impulse, the QBO shifts to the GREEN direction, and when the lunar declination is negative, the QBO reverses to the opposite RED direction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment