|
| 1 | +# ISC License |
| 2 | +# |
| 3 | +# Copyright (c) 2024, University of Colorado at Boulder |
| 4 | +# |
| 5 | +# Permission to use, copy, modify, and/or distribute this software for any |
| 6 | +# purpose with or without fee is hereby granted, provided that the above |
| 7 | +# copyright notice and this permission notice appear in all copies. |
| 8 | +# |
| 9 | +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 10 | +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 11 | +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 12 | +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 13 | +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 14 | +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 15 | +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 16 | + |
| 17 | +import tempfile, sys |
| 18 | +from Basilisk import __path__ |
| 19 | +bskPath = __path__[0] |
| 20 | +sys.path.append(bskPath + "/../../src/utilities/vizProtobuffer/") |
| 21 | +import cielimMessage_pb2 |
| 22 | +import delimited_protobuf |
| 23 | +import numpy as np |
| 24 | +import pytest |
| 25 | +from Basilisk.architecture import messaging |
| 26 | +from Basilisk.utilities import SimulationBaseClass |
| 27 | + |
| 28 | +importErr = False |
| 29 | +reasonErr = "" |
| 30 | +try: |
| 31 | + from Basilisk.simulation import cielimInterface |
| 32 | +except ImportError: |
| 33 | + importErr = True |
| 34 | + reasonErr = "\nCielim Interface not built ---check build option" |
| 35 | + |
| 36 | +@pytest.mark.skipif(importErr, reason=reasonErr) |
| 37 | +def test_interface(): |
| 38 | + read_write_test() |
| 39 | + |
| 40 | +def read_write_test(): |
| 41 | + tmpdir = tempfile.TemporaryDirectory() |
| 42 | + unit_task_name = "unitTask" |
| 43 | + unit_process_name = "TestProcess" |
| 44 | + unit_test_sim = SimulationBaseClass.SimBaseClass() |
| 45 | + |
| 46 | + # Create test thread |
| 47 | + test_process_rate = int(1E9) # update process rate update time |
| 48 | + test_process = unit_test_sim.CreateNewProcess(unit_process_name) |
| 49 | + test_process.addTask(unit_test_sim.CreateNewTask(unit_task_name, test_process_rate)) |
| 50 | + |
| 51 | + module = cielimInterface.CielimInterface() |
| 52 | + module.ModelTag = "cielim_interface" |
| 53 | + module.setOpNavMode(cielimInterface.ClosedLoopMode_OPEN_LOOP) |
| 54 | + module.setSaveFile(tmpdir.name + "/test_proto") |
| 55 | + |
| 56 | + unit_test_sim.AddModelToTask(unit_task_name, module) |
| 57 | + |
| 58 | + # Create epoch message |
| 59 | + epoch_payload = messaging.EpochMsgPayload() |
| 60 | + epoch_payload.year = 2050 |
| 61 | + epoch_payload.month = 2 |
| 62 | + epoch_payload.day = 15 |
| 63 | + epoch_payload.hours = 22 |
| 64 | + epoch_payload.minutes = 8 |
| 65 | + epoch_payload.seconds = 58 |
| 66 | + epoch_message = messaging.EpochMsg().write(epoch_payload) |
| 67 | + module.epochMessage.subscribeTo(epoch_message) |
| 68 | + |
| 69 | + # Create camera rendering message |
| 70 | + rendering_payload = messaging.CameraRenderingMsgPayload() |
| 71 | + rendering_payload.cameraId = 1 |
| 72 | + rendering_payload.cosmicRayStdDeviation = 0.1 |
| 73 | + rendering_payload.enableStrayLight = True |
| 74 | + rendering_payload.starField = True |
| 75 | + rendering_payload.rendering = "Lumen" |
| 76 | + rendering_payload.smear = True |
| 77 | + rendering_message = messaging.CameraRenderingMsg().write(rendering_payload) |
| 78 | + module.cameraRenderingMessage.subscribeTo(rendering_message) |
| 79 | + |
| 80 | + # Create asteroid parameter message |
| 81 | + asteroid_parameter_payload = messaging.CelestialBodyParametersMsgPayload() |
| 82 | + asteroid_parameter_payload.bodyName = "asteroid_b612" |
| 83 | + asteroid_parameter_payload.shapeModel = "Bennu" |
| 84 | + asteroid_parameter_payload.perlinNoise = 0.5 |
| 85 | + asteroid_parameter_payload.proceduralRocks = 1.5 |
| 86 | + asteroid_parameter_payload.brdf = "Lambertian" |
| 87 | + asteroid_parameter_payload.reflectanceParameters = [0.5] |
| 88 | + asteroid_parameter_payload.meanRadius = 50000 |
| 89 | + asteroid_parameter_payload.principalAxisDistortion = [10, 5, 10] |
| 90 | + asteroid_parameter_message = messaging.CelestialBodyParametersMsg().write(asteroid_parameter_payload) |
| 91 | + module.celestialParametersMessage.subscribeTo(asteroid_parameter_message) |
| 92 | + |
| 93 | + # Create camera message |
| 94 | + camera_payload = messaging.CameraModelMsgPayload() |
| 95 | + camera_payload.timeTag = int(0.5*1E9) |
| 96 | + camera_payload.cameraId = 1 |
| 97 | + camera_payload.parentName = "cielim_sat" |
| 98 | + camera_payload.fieldOfView = [20*np.pi/180, 15*np.pi/180] |
| 99 | + camera_payload.bodyToCameraMrp = [1/3,-1/3,-1/3] |
| 100 | + camera_payload.cameraBodyFramePosition = [1,1,1] |
| 101 | + camera_payload.resolution = [4000, 3000] |
| 102 | + camera_payload.renderRate = 10000 |
| 103 | + camera_payload.focalLength = 0.1 |
| 104 | + camera_payload.readNoise = 1 |
| 105 | + camera_payload.systemGain = 0.5 |
| 106 | + camera_payload.gaussianPointSpreadFunction = 2 |
| 107 | + camera_payload.exposureTime = 0.1 |
| 108 | + camera_message = messaging.CameraModelMsg().write(camera_payload) |
| 109 | + module.cameraModelMessage.subscribeTo(camera_message) |
| 110 | + |
| 111 | + # Create spacecraft message |
| 112 | + spacecraft_payload = messaging.SCStatesMsgPayload() |
| 113 | + spacecraft_payload.r_BN_N = [0,0,-5E4] |
| 114 | + spacecraft_payload.v_BN_N = [4, 5, 6] |
| 115 | + spacecraft_payload.sigma_BN = [np.sqrt(2)/2, 0, -np.sqrt(2)/2] |
| 116 | + spacecraft_message = messaging.SCStatesMsg().write(spacecraft_payload) |
| 117 | + module.spacecraftMessage.subscribeTo(spacecraft_message) |
| 118 | + |
| 119 | + # Create spice body messages using a similar structure to the factory (for ease of implementation) |
| 120 | + class GravBodies: |
| 121 | + planetName = "" |
| 122 | + isCentralBody = False |
| 123 | + planetBodyInMsg = messaging.SpicePlanetStateMsg() |
| 124 | + |
| 125 | + grav_bodies = [] |
| 126 | + bodies_message_list = [] |
| 127 | + sun = GravBodies() |
| 128 | + sun.planetName = "sun_planet_data" |
| 129 | + sun_data = messaging.SpicePlanetStateMsgPayload() |
| 130 | + sun_data.PositionVector = [10E10,0,0] |
| 131 | + sun_data.VelocityVector = [4, 5, 6] |
| 132 | + sun_data.J20002Pfix = np.eye(3) |
| 133 | + sun.planetBodyInMsg = messaging.SpicePlanetStateMsg().write(sun_data) |
| 134 | + grav_bodies.append(sun) |
| 135 | + bodies_message_list.append(sun_data) |
| 136 | + |
| 137 | + asteroid_b612 = GravBodies() |
| 138 | + asteroid_b612.planetName = "asteroid_b612" |
| 139 | + asteroid_b612.isCentralBody = True |
| 140 | + asteroid_data = messaging.SpicePlanetStateMsgPayload() |
| 141 | + asteroid_data.PositionVector = [0,0,0] |
| 142 | + asteroid_data.VelocityVector = [4, 5, 6] |
| 143 | + asteroid_data.J20002Pfix = -np.eye(3) |
| 144 | + asteroid_b612.planetBodyInMsg = messaging.SpicePlanetStateMsg().write(asteroid_data) |
| 145 | + grav_bodies.append(asteroid_b612) |
| 146 | + bodies_message_list.append(asteroid_data) |
| 147 | + |
| 148 | + for gravBody in grav_bodies: |
| 149 | + body = cielimInterface.SpiceBody() |
| 150 | + body.name = gravBody.planetName |
| 151 | + body.isCentralBody = gravBody.isCentralBody |
| 152 | + body.spiceStateMessage.subscribeTo(gravBody.planetBodyInMsg) |
| 153 | + module.addCelestialBody(body) |
| 154 | + |
| 155 | + # Run block |
| 156 | + unit_test_sim.InitializeSimulation() |
| 157 | + unit_test_sim.ConfigureStopTime(int(1e9)) |
| 158 | + unit_test_sim.ExecuteSimulation() |
| 159 | + module.closeProtobufFile() |
| 160 | + |
| 161 | + # Read the existing address book. |
| 162 | + file_handle = open(module.getSaveFilename(), "rb") |
| 163 | + cielim_message = delimited_protobuf.read(file_handle, cielimMessage_pb2.CielimMessage) |
| 164 | + |
| 165 | + np.testing.assert_equal(cielim_message.currentTime.frameNumber, 1) |
| 166 | + np.testing.assert_equal(cielim_message.currentTime.simTimeElapsed, test_process_rate) |
| 167 | + |
| 168 | + np.testing.assert_equal(cielim_message.epoch.year, epoch_payload.year) |
| 169 | + np.testing.assert_equal(cielim_message.epoch.month, epoch_payload.month) |
| 170 | + np.testing.assert_equal(cielim_message.epoch.day, epoch_payload.day) |
| 171 | + np.testing.assert_equal(cielim_message.epoch.hours, epoch_payload.hours) |
| 172 | + np.testing.assert_equal(cielim_message.epoch.minutes, epoch_payload.minutes) |
| 173 | + np.testing.assert_equal(cielim_message.epoch.seconds, epoch_payload.seconds) |
| 174 | + |
| 175 | + np.testing.assert_equal(cielim_message.spacecraft.spacecraftName, "cielim_sat") |
| 176 | + np.testing.assert_equal(cielim_message.spacecraft.position, spacecraft_payload.r_BN_N) |
| 177 | + np.testing.assert_equal(cielim_message.spacecraft.velocity, spacecraft_payload.v_BN_N) |
| 178 | + np.testing.assert_equal(cielim_message.spacecraft.attitude, spacecraft_payload.sigma_BN) |
| 179 | + |
| 180 | + np.testing.assert_equal(cielim_message.camera.cameraId, camera_payload.cameraId) |
| 181 | + np.testing.assert_equal(cielim_message.camera.parentName, "cielim_sat") |
| 182 | + np.testing.assert_equal(cielim_message.camera.fieldOfView,camera_payload.fieldOfView) |
| 183 | + np.testing.assert_equal(cielim_message.camera.resolution, camera_payload.resolution) |
| 184 | + np.testing.assert_equal(cielim_message.camera.cameraPositionInBody, camera_payload.cameraBodyFramePosition) |
| 185 | + np.testing.assert_equal(cielim_message.camera.bodyFrameToCameraMrp, camera_payload.bodyToCameraMrp) |
| 186 | + np.testing.assert_equal(cielim_message.camera.renderRate, camera_payload.renderRate) |
| 187 | + np.testing.assert_equal(cielim_message.camera.focalLength, camera_payload.focalLength) |
| 188 | + np.testing.assert_equal(cielim_message.camera.exposureTime, camera_payload.exposureTime) |
| 189 | + np.testing.assert_equal(cielim_message.camera.pointSpreadFunction, camera_payload.gaussianPointSpreadFunction) |
| 190 | + np.testing.assert_equal(cielim_message.camera.systemGain, camera_payload.systemGain) |
| 191 | + np.testing.assert_equal(cielim_message.camera.readNoise, camera_payload.readNoise) |
| 192 | + |
| 193 | + np.testing.assert_equal(cielim_message.camera.renderParameters.cosmicRayStdDeviation, rendering_payload.cosmicRayStdDeviation) |
| 194 | + np.testing.assert_equal(cielim_message.camera.renderParameters.enableStrayLight, rendering_payload.enableStrayLight) |
| 195 | + np.testing.assert_equal(cielim_message.camera.renderParameters.starField, rendering_payload.starField) |
| 196 | + np.testing.assert_equal(cielim_message.camera.renderParameters.rendering, rendering_payload.rendering) |
| 197 | + np.testing.assert_equal(cielim_message.camera.renderParameters.enableSmear, rendering_payload.smear) |
| 198 | + |
| 199 | + i = 0 |
| 200 | + for message, body in zip(bodies_message_list, grav_bodies): |
| 201 | + name = body.planetName |
| 202 | + central = body.isCentralBody |
| 203 | + np.testing.assert_equal(cielim_message.celestialBodies[i].bodyName, name) |
| 204 | + np.testing.assert_equal(cielim_message.celestialBodies[i].position, message.PositionVector) |
| 205 | + np.testing.assert_equal(cielim_message.celestialBodies[i].velocity, message.VelocityVector) |
| 206 | + np.testing.assert_equal(cielim_message.celestialBodies[i].attitude, np.array(message.J20002Pfix).flatten()) |
| 207 | + np.testing.assert_equal(cielim_message.celestialBodies[i].centralBody, central) |
| 208 | + if (name == asteroid_parameter_payload.bodyName): |
| 209 | + np.testing.assert_equal(cielim_message.celestialBodies[i].models.shapeModel, asteroid_parameter_payload.shapeModel) |
| 210 | + np.testing.assert_equal(cielim_message.celestialBodies[i].models.perlinNoiseStdDeviation, asteroid_parameter_payload.perlinNoise) |
| 211 | + np.testing.assert_equal(cielim_message.celestialBodies[i].models.proceduralRocks, asteroid_parameter_payload.proceduralRocks) |
| 212 | + np.testing.assert_equal(cielim_message.celestialBodies[i].models.brdfModel, asteroid_parameter_payload.brdf) |
| 213 | + np.testing.assert_equal(cielim_message.celestialBodies[i].models.reflectanceParameters, asteroid_parameter_payload.reflectanceParameters) |
| 214 | + np.testing.assert_equal(cielim_message.celestialBodies[i].models.meanRadius, asteroid_parameter_payload.meanRadius) |
| 215 | + np.testing.assert_equal(cielim_message.celestialBodies[i].models.principalAxisDistortion, asteroid_parameter_payload.principalAxisDistortion) |
| 216 | + i += 1 |
| 217 | + |
| 218 | +if __name__ == "__main__": |
| 219 | + test_interface() |
0 commit comments