# # ISC License# # Copyright (c) 2021, Autonomous Vehicle Systems Lab, University of Colorado Boulder# # Permission to use, copy, modify, and/or distribute this software for any# purpose with or without fee is hereby granted, provided that the above# copyright notice and this permission notice appear in all copies.# # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.# # importosimportmatplotlib.pyplotaspltimportnumpyasnpimportpytest# The path to the location of Basilisk# Used to get the location of supporting data.fromBasiliskimport__path__fromBasilisk.architectureimportmessagingfromBasilisk.simulationimportMtbEffectorfromBasilisk.simulationimportspacecraft,magneticFieldWMMfromBasilisk.utilitiesimportSimulationBaseClass,simIncludeGravBody,orbitalMotion,RigidBodyKinematicsfromBasilisk.utilitiesimportmacrosfromBasilisk.utilitiesimportunitTestSupportbskPath=__path__[0]fileName=os.path.basename(os.path.splitext(__file__)[0])deftruthMagneticTorque(bField_N,sigmaBN,mtbCmds,GtMatrix,numMTB,maxDipole):temp=np.asarray(GtMatrix[0:3*numMTB])GtMatrix=np.reshape(temp,(3,numMTB))bField_N=np.asarray(bField_N)BN=RigidBodyKinematics.MRP2C(sigmaBN)bField_B=BN@bField_Nforiinrange(len(mtbCmds)):ifmtbCmds[i]>maxDipole:mtbCmds[i]=maxDipoleifmtbCmds[i]<-maxDipole:mtbCmds[i]=-maxDipolemtbCmds=np.asarray(mtbCmds[0:numMTB])bTilde=np.asarray(RigidBodyKinematics.v3Tilde(bField_B))tauNet=-bTilde@GtMatrix@mtbCmdsreturntauNet
[docs]@pytest.mark.parametrize("accuracy",[1e-12])@pytest.mark.parametrize("maxDipole",[10.,0.1])deftest_MtbEffector(show_plots,accuracy,maxDipole):r""" **Validation Test Description** Compose a general description of what is being tested in this unit test script. **Test Parameters** Discuss the test parameters used. Args: param1 (int): Dummy test parameter for this parameterized unit test param2 (int): Dummy test parameter for this parameterized unit test accuracy (float): absolute accuracy value used in the validation tests **Description of Variables Being Tested** Here discuss what variables and states are being checked. """[testResults,testMessage]=MtbEffectorTestFunction(show_plots,accuracy,maxDipole)asserttestResults<1,testMessage
[docs]defMtbEffectorTestFunction(show_plots,accuracy,maxDipole):"""Call this routine directly to run the unit test."""testFailCount=0# zero unit test result countertestMessages=[]# create empty array to store test log messages# create simulation variable namessimTaskName="simTask"simProcessName="simProcess"# create a sim module as an empty containerscSim=SimulationBaseClass.SimBaseClass()# create the simulation processdynProcess=scSim.CreateNewProcess(simProcessName)# create the dynamics task and specify the integration update timesimulationTimeStep=macros.sec2nano(1.)dynProcess.addTask(scSim.CreateNewTask(simTaskName,simulationTimeStep))simTime=3.simulationTime=macros.sec2nano(simTime)# create Earth Gravity BodygravFactory=simIncludeGravBody.gravBodyFactory()earth=gravFactory.createEarth()earth.isCentralBody=True# ensure this is the central gravitational bodymu=earth.mu# initialize spacecraft object and set propertiesscObject=spacecraft.Spacecraft()scObject.ModelTag="bskTestSat"I=[10.5,0.,0.,0.,8.,0.,0.,0.,6.75]scObject.hub.mHub=10.0# kg - spacecraft mass (arbitrary)scObject.hub.r_BcB_B=[[0.0],[0.0],[0.0]]# m - position vector of body-fixed point B relative to CMscObject.hub.IHubPntBc_B=unitTestSupport.np2EigenMatrix3d(I)oe=orbitalMotion.ClassicElements()oe.a=6778.14*1000.# metersoe.e=0.0oe.i=45.*macros.D2Roe.Omega=60.*macros.D2Roe.omega=0.*macros.D2Roe.f=0.*macros.D2RrN,vN=orbitalMotion.elem2rv(mu,oe)scObject.hub.r_CN_NInit=rN# m - r_CN_NscObject.hub.v_CN_NInit=vN# m/s - v_CN_NscObject.hub.sigma_BNInit=[[0.0],[0.0],[0.0]]# sigma_CN_BscObject.hub.omega_BN_BInit=[[0.0],[0.0],[0.0]]# rad/s - omega_CN_B# add spacecraft objectscSim.AddModelToTask(simTaskName,scObject)scObject.gravField.gravBodies=spacecraft.GravBodyVector(list(gravFactory.gravBodies.values()))# add magnetic field modulemagModule=magneticFieldWMM.MagneticFieldWMM()magModule.ModelTag="WMM"magModule.dataPath=bskPath+'/supportData/MagneticField/'epochMsg=unitTestSupport.timeStringToGregorianUTCMsg('2020 May 12, 00:00:0.0 (UTC)')magModule.epochInMsg.subscribeTo(epochMsg)magModule.addSpacecraftToModel(scObject.scStateOutMsg)# this command can be repeated if multiplescSim.AddModelToTask(simTaskName,magModule)# add magnetic torque bar effectormtbEff=MtbEffector.MtbEffector()mtbEff.ModelTag="MtbEff"scObject.addDynamicEffector(mtbEff)scSim.AddModelToTask(simTaskName,mtbEff)# mtbConfigData messagemtbConfigParams=messaging.MTBArrayConfigMsgPayload()mtbConfigParams.numMTB=3# row major toque bar alignmentsmtbConfigParams.GtMatrix_B=[1.,0.,0.,0.,1.,0.,0.,0.,1.]mtbConfigParams.maxMtbDipoles=[maxDipole]*4mtbParamsInMsg=messaging.MTBArrayConfigMsg().write(mtbConfigParams)# MTBCmdMsgPayload, mtbCmdInMsgmtbCmdInMsgContainer=messaging.MTBCmdMsgPayload()mtbCmdInMsgContainer.mtbDipoleCmds=[0.2,0.2,0.2]mtbCmdInMsg=messaging.MTBCmdMsg().write(mtbCmdInMsgContainer)# subscribe to mesaages mtbEff.mtbCmdInMsg.subscribeTo(mtbCmdInMsg)mtbEff.mtbParamsInMsg.subscribeTo(mtbParamsInMsg)mtbEff.magInMsg.subscribeTo(magModule.envOutMsgs[0])# Setup data logging before the simulation is initializednumDataPoints=3600samplingTime=unitTestSupport.samplingTime(simulationTime,simulationTimeStep,numDataPoints)dataLog=scObject.scStateOutMsg.recorder(samplingTime)dataLogMag=magModule.envOutMsgs[0].recorder(samplingTime)dataLogMTB=mtbEff.mtbOutMsg.recorder(samplingTime)scSim.AddModelToTask(simTaskName,dataLogMTB)scSim.AddModelToTask(simTaskName,dataLogMag)scSim.AddModelToTask(simTaskName,dataLog)# initialize SimulationscSim.InitializeSimulation()# configure a simulation stop time and execute the simulation runscSim.ConfigureStopTime(simulationTime)scSim.ExecuteSimulation()# retrieve the logged dataattData=dataLog.sigma_BNmtbData=dataLogMTB.mtbNetTorque_BdataMagField=dataLogMag.magField_Nnp.set_printoptions(precision=16)# plot net mtb torqueifshow_plots:plt.close("all")# clears out plots from earlier test runsplt.figure(1)foridxinrange(0,3):plt.plot(dataLogMTB.times()*macros.NANO2SEC,mtbData[:,idx],color=unitTestSupport.getLineColor(idx,3),label=r'$\tau_'+str(idx)+'$')plt.legend(loc='lower right')plt.xlabel('Time [s]')plt.ylabel(r'MTB Net Torque $\tau_{B}$ [Nm]')# plot magnetic field data in the Inertial frameplt.figure(2)foridxinrange(3):plt.plot(dataLogMag.times()*macros.NANO2SEC,dataMagField[:,idx]*1e9,color=unitTestSupport.getLineColor(idx,3),label=r'$B\_N_{'+str(idx)+'}$')plt.legend(loc='lower right')plt.xlabel('Time [s]')plt.ylabel('Magnetic Field [nT]')# plot the Body relative to Inertial attitudeplt.figure(3)foridxinrange(0,3):plt.plot(dataLog.times()*macros.NANO2SEC,attData[:,idx],color=unitTestSupport.getLineColor(idx,3),label=r'$\sigma_'+str(idx)+'$')plt.legend(loc='lower right')plt.xlabel('Time [s]')plt.ylabel(r'MRP Attitude $\sigma_{B/N}$')plt.show()# compare gravity gradient torque vector to the truth# use mtbData[1:, :] since mtbData is the torque from prev iterationsforsV,mtbTauV,bVinzip(attData[1:,:],mtbData[1:,:],dataMagField):mtbTauTruth=truthMagneticTorque(bV,sV,mtbCmdInMsgContainer.mtbDipoleCmds,mtbConfigParams.GtMatrix_B,mtbConfigParams.numMTB,maxDipole)testFailCount,testMessages=unitTestSupport.compareVector(mtbTauV,mtbTauTruth,accuracy,"mtbTorque_B",testFailCount,testMessages)iftestFailCount==0:print("PASSED: Mtb Effector")else:print("Failed: Mtb Effector")returntestFailCount,testMessages