Demonstrate NanoSat attitude control reaction wheels.

The model includes 3 orthogonal reaction wheels. The satellite is intialized into an ISS orbit. The control law keeps the satellite aligned with LVLH. Power, thermal and link are simulated.

See also: CubeSatFaces, InertiaCubeSat, LatLonToR, DataRateOrbit, PID3Axis, PIDMIMO, QForm, BDipole, QLVLH, RK4, RHSCubeSat, TimeDisplay, GroundTrack, Plot2D, Figui -------------------------------------------------------------------------

Contents

%------------------------------------------------------------------------
%   Copyright (c) 2020 Princeton Satellite Systems, Inc.
%   All rights reserved.
%------------------------------------------------------------------------
%   Since version 2020.1
%------------------------------------------------------------------------

% In case these were already used in the workspace
clear g; clear u; clear p; clear d;

Constants

radToDeg        = 180/pi;
densitySilicon  = 2600;
densityAl       = 2700; % Aluminum
densityTungsten = 19300; % For the RWA disk

Simulation parameters

days  = 0.2;
tEnd  = days*86400;
dT    = 1;
nSim  = ceil(tEnd/dT);

NanoSat model

Initialize the data structure

d = RHSCubeSat;

% CubeSats are 1 kg per U
model = '3U';
[area,nFace,rFace] = CubeSatFaces( model, 1 );
d.mass    = 3; % kg
d.inertia = InertiaCubeSat( model, d.mass );
linkPower = 1;
rGS       = LatLonToR(33.9191667/radToDeg,-118.4155556/radToDeg);
dLink     = DataRateOrbit;

% Model data for our 3U satellite
d.surfData.area = area;
d.surfData.nFace = nFace;
d.surfData.rFace = rFace;
d.surfData.att.type = 'eci';

% Reaction wheel design
d.kWheels    = 14:16; % indices of wheel states in the state vector

% From the manufacturer
d.inertiaRWA = 2e-05; % Polar inertia kg-m^2

% Add power system model. Model as a list of areas and normals in the
% body frame
d.power.solarCellNormal    = [1 1 -1 -1 0 0 0 0;...
                              0 0 0 0 1 1 -1 -1;...
                              0 0 0 0 0 0  0  0];
d.power.solarCellEff       = 0.295;
d.power.effPowerConversion = 0.9;
d.power.solarCellArea      = 0.1*0.116*ones(1,8);
d.power.consumption        = 4;
d.power.batteryCapacity    = 36000;
DrawCubeSatSolarAreas(d.power)
ans = 
  Figure (PlotPSS) with properties:

      Number: 13
        Name: 'DrawCubeSatSolarAreas'
       Color: [0.94 0.94 0.94]
    Position: [560 528 560 420]
       Units: 'pixels'

  Use GET to show all properties

Initial state vector for an ISS orbit

x         = d.x0;
[el, jD0]	= ISSOrbit;
[r,v]     = El2RV(el);
x(1:3)    = r;
x(4:6)    = v;

Start Julian date

d.jD0   = jD0;

Design the PID Controller

Specify the z body axis for alignment with the chosen ECI vector

p   = PID3Axis;

% inr, zeta, omega, tauInt, omegaR, tSamp
% We set inr to 1 because our control will be angular acceleration
% PID3Axis will multiply this to produce a torque
[p.a, p.b, p.c, p.d] = PIDMIMO( 1, 1, 0.01, 200, 0.1, dT );

p.inertia            = d.inertia;
p.max_angle          = 0.01;
p.accel_sat          = [100;100;100];
p.mode               = 2; % Attitude quaternion mode
p.q_target_last      = x(7:10);

Atmosphere model data

d.atm = []; % Don't use J70

Initialize the plotting array to save time

qECIToBody   = x(7:10);
bField       = QForm( qECIToBody, BDipole( x(1:3), d.jD0 ) );
xPlot        = [[x;0;0;0;0;bField;0;0;0;0] zeros(length(x)+11,nSim)];

Initialize the time display

TimeDisplay( 'initialize', 'Nanosat Sim', nSim );

Run the simulation

t = 0;

for k = 1:nSim

  % Display the status message
  TimeDisplay('update');

  % Quaternion
  qECIToBody   = x(7:10);

  % Julian date
  jD       = d.jD0+t/86400;

  % Magnetic field - the magnetometer output is proportional to this
  bField 	 = QForm( qECIToBody, BDipole( x(1:3), jD ) );

  % Control system
  % LVLH is local vertical local horizontal and is a rotating frame with
  % +z pointing at the earth and +x in the velocity vector
  p.q_desired_state = QLVLH(x(1:3),x(4:6));
  [torque, p]       = PID3Axis( qECIToBody, p );
  d.tRWA            = -torque;

  % A time step with 4th order Runge-Kutta
  x	= RK4( @RHSCubeSat, x, dT, t, d );

  % Get the power
  [xDot, dist, power] = RHSCubeSat( x, t, d );

  % Data Rate
  dR = DataRateOrbit( x(1:3), rGS, linkPower, jD, dLink );

  % Update plotting and time
  hRWA         = x(14:16)*d.inertiaRWA;
  xPlot(:,k+1) = [x;power;torque;bField;hRWA;dR];
  t            = t + dT;

end
TimeDisplay( 'close' );

% Data to display
kP = 1:k+1;
t  = (0:k)*dT;

Graphics

GroundTrack( xPlot( 1: 3,:), t, d.jD0 );

Plotting

[t, tL] = TimeLabl( t );

% Y-axis labels
yL = [d.states(:)' {'Power (W)'} {'T_x (Nm)'} {'T_y (Nm)'} {'T_z (Nm)'}...
        {'B_x'} {'B_y'} {'B_z'}, {'H_x (Nms)'} {'H_y (Nms)'} {'H_z (Nms)'}...
        {'Data Rate (bps)'}];

Plot2D( t, xPlot( 1: 3,kP), tL, yL(  1: 3), 'Nanosat Orbit' );
Plot2D( t, xPlot( 7:10,kP), tL, yL(  7:10), 'Nanosat ECI To Body Quaternion' );
Plot2D( t, xPlot(11:13,kP), tL, yL( 11:13), 'Nanosat Attitude Rate (rad/s)' );
Plot2D( t, xPlot(14:16,kP), tL, yL( 14:16), 'Nanosat Reaction Wheel Rate (rad/s)' );
Plot2D( t, [xPlot(17,kP)/3600;xPlot(18,kP)], tL, yL( 17:18), 'Nanosat Power' );
Plot2D( t, xPlot(19:21,kP), tL, yL( 19:21), 'Nanosat Control Torque' );
Plot2D( t, xPlot(22:24,kP), tL, yL( 22:24), 'Nanosat Magnetic Field' );
Plot2D( t, xPlot(25:27,kP), tL, yL( 25:27), 'Nanosat RWA Momentum' );
Plot2D( t, xPlot(28,kP), tL, yL{28}, 'Nanosat Link' );

% This creates a plot navigation window
Figui;


%--------------------------------------