Yaw Steering for Solar Power Optimisation

Demonstrates the effect of yaw steering on solar power generation.

Scenario

  • 450 km circular orbit, 50° inclination, nadir-pointed

  • Single deployed solar panel: 5 m² active area, normal direction (1, 0, −1) in body frame

  • 30% solar-to-DC efficiency

  • Comparison: fixed nadir attitude vs yaw steering

[1]:
import numpy as np
import matplotlib.pyplot as plt

from missiontools import Spacecraft, FixedAttitudeLaw, NormalVectorSolarConfig

1. Spacecraft and solar panel setup

[2]:
EPOCH = np.datetime64('2025-03-20T12:00:00', 'us')

PANEL_AREA    = 5.0    # m² gross
FILL_FACTOR   = 0.95
EFFICIENCY    = 0.30
PANEL_NORMAL  = [1, 0, -1]  # body frame (normalised internally)

active_area = PANEL_AREA * FILL_FACTOR

print(f"Panel gross area  : {PANEL_AREA:.1f} m\u00b2")
print(f"Active cell area  : {active_area:.2f} m\u00b2")
print(f"Panel normal      : {PANEL_NORMAL}")
print(f"Efficiency        : {EFFICIENCY:.0%}")
Panel gross area  : 5.0 m²
Active cell area  : 4.75 m²
Panel normal      : [1, 0, -1]
Efficiency        : 30%
[3]:
sc_static = Spacecraft(
    a     = (6_371_000 + 450_000),  # 450 km altitude
    e     = 0.0,
    i     = np.radians(50.0),
    raan  = 0.0,
    arg_p = 0.0,
    ma    = 0.0,
    epoch = EPOCH,
)

cfg_static = NormalVectorSolarConfig(
    normal_vecs=[PANEL_NORMAL],
    areas=[active_area],
    efficiency=EFFICIENCY,
)
sc_static.add_solar_config(cfg_static)

mu = sc_static.central_body_mu
period_s = 2 * np.pi * np.sqrt(sc_static.a**3 / mu)
period   = np.timedelta64(int(period_s * 1e6), 'us')

print(f"Altitude          : 450 km")
print(f"Inclination       : 50.0\u00b0")
print(f"Orbital period    : {period_s / 60:.1f} min")
Altitude          : 450 km
Inclination       : 50.0°
Orbital period    : 93.4 min

2. Static attitude — power over one orbit

[4]:
result_static = cfg_static.generation(EPOCH, EPOCH + period, np.timedelta64(10, 's'))
oap_static    = cfg_static.oap()

print(f"OAP (static)      : {oap_static:.2f} W")
print(f"Peak power        : {result_static['power'].max():.2f} W")
OAP (static)      : 596.12 W
Peak power        : 1946.52 W

3. Yaw steering — power over one orbit

[5]:
# Build a second spacecraft with yaw steering enabled
sc_yaw = Spacecraft(
    a     = sc_static.a,
    e     = 0.0,
    i     = np.radians(50.0),
    raan  = 0.0,
    arg_p = 0.0,
    ma    = 0.0,
    epoch = EPOCH,
)

cfg_yaw = NormalVectorSolarConfig(
    normal_vecs=[PANEL_NORMAL],
    areas=[active_area],
    efficiency=EFFICIENCY,
)
sc_yaw.add_solar_config(cfg_yaw)
sc_yaw.attitude_law.yaw_steering(cfg_yaw)

result_yaw = cfg_yaw.generation(EPOCH, EPOCH + period, np.timedelta64(10, 's'))
oap_yaw    = cfg_yaw.oap()

print(f"OAP (yaw steering): {oap_yaw:.2f} W")
print(f"Peak power        : {result_yaw['power'].max():.2f} W")
OAP (yaw steering): 1004.05 W
Peak power        : 1946.55 W

4. Comparison plots

[6]:
elapsed_static = (result_static['t'] - result_static['t'][0]) / np.timedelta64(1, 'm')
elapsed_yaw    = (result_yaw['t']    - result_yaw['t'][0])    / np.timedelta64(1, 'm')

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 7), sharex=True)

# --- Individual curves ---
ax1.plot(elapsed_static, result_static['power'], linewidth=0.8, color='tab:blue', label='Static nadir')
ax1.axhline(oap_static, color='tab:blue', linestyle='--', linewidth=1, alpha=0.7,
            label=f'OAP = {oap_static:.2f} W')
ax1.plot(elapsed_yaw, result_yaw['power'], linewidth=0.8, color='tab:orange', label='Yaw steering')
ax1.axhline(oap_yaw, color='tab:orange', linestyle='--', linewidth=1, alpha=0.7,
            label=f'OAP = {oap_yaw:.2f} W')
ax1.set_ylabel('Power (W)')
ax1.set_title('Solar power generation \u2014 static vs yaw steering')
ax1.legend(loc='upper right')
ax1.set_ylim(bottom=0)
ax1.grid(True, alpha=0.3)

# --- Power gain ---
gain = result_yaw['power'] - result_static['power']
ax2.fill_between(elapsed_yaw, gain, alpha=0.4, color='tab:green', label='Power gain')
ax2.axhline(0, color='grey', linewidth=0.5)
ax2.set_xlabel('Time (minutes)')
ax2.set_ylabel('\u0394 Power (W)')
ax2.set_title('Power gain from yaw steering')
ax2.legend()
ax2.set_xlim(0, elapsed_yaw[-1])
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()
../_images/examples_yaw_steering_10_0.png

5. Summary

[7]:
improvement = (oap_yaw / oap_static - 1) * 100

print('=' * 50)
print('Yaw steering summary')
print('=' * 50)
print(f"{'':25} {'Static':>12} {'Yaw':>12}")
print('-' * 50)
print(f"{'OAP (W)':25} {oap_static:>12.2f} {oap_yaw:>12.2f}")
print(f"{'Peak power (W)':25} {result_static['power'].max():>12.2f} {result_yaw['power'].max():>12.2f}")
print(f"{'OAP improvement':25} {'+' + f'{improvement:.1f}':>12}%")
==================================================
Yaw steering summary
==================================================
                                Static          Yaw
--------------------------------------------------
OAP (W)                         596.12      1004.05
Peak power (W)                 1946.52      1946.55
OAP improvement                  +68.4%