Source code for test_unitPinholeCamera

# ISC License
#
# Copyright (c) 2023, Autonomous Vehicle Systems Lab, University of Colorado at 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.

import os, inspect
import numpy as np
import pytest
from matplotlib import pyplot as plt

from Basilisk.utilities import SimulationBaseClass
from Basilisk.utilities import macros
from Basilisk.utilities import RigidBodyKinematics as rbk
from Basilisk.utilities import orbitalMotion
from Basilisk.utilities import simIncludeGravBody
from Basilisk.architecture import messaging
from Basilisk.simulation import pinholeCamera
from Basilisk.simulation import spacecraft
from Basilisk.utilities import unitTestSupport
from Basilisk import __path__

filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))
bskName = 'Basilisk'
splitPath = path.split(bskName)
bskPath = __path__[0]


[docs]def test_visibility(): """ Tests whether pinholeCamera: 1. Computes correctly pixels for center and corners. 2. Detects correctly that some landmarks are not within field of view. :return: """ simTime = 1. simTaskName = "simTask" simProcessName = "simProcess" scSim = SimulationBaseClass.SimBaseClass() dynProcess = scSim.CreateNewProcess(simProcessName) simulationTime = macros.sec2nano(simTime) simulationTimeStep = macros.sec2nano(1.) dynProcess.addTask(scSim.CreateNewTask(simTaskName, simulationTimeStep)) # Set the landmarks distribution to be tested # The first five landmarks shall correspond to center and corners of the image (for the prescribed situation) # The last five landmarks shall be detected as not within FOV FOVx = 38.17124015837933 * np.pi/180 # this is the horizontal FOV, check camera.FOVx FOVy = 29.094758030219015 * np.pi/180 # this is the vertical FOV, check camera.FOVy n_lmk = 10 pos_lmk = np.array([[-16*1e3, 0, 0], # Image center [-16*1e3, 18*1e3 * np.tan(FOVy/2), 0], # Image corner (18 km = horizontal distance sc-lmk) [-16*1e3, -18*1e3*np.tan(FOVy/2), 0], # Image corner [-16*1e3, 0, 18*1e3*np.tan(FOVx/2)], # Image corner [-16*1e3, 0, -18*1e3*np.tan(FOVx/2)], # Image corner [16*1e3, 0, 0], # Not visible [0, 16*1e3, 0], # Not visible [0, -16*1e3, 0], # Not visible [0, 0, 16*1e3], # Not visible [0, 0, -16*1e3]]) # Not visible normal_lmk = np.array([[-1, 0, 0], [-1, 0, 0], # Mock to ensure visibility [-1, 0, 0], # Mock to ensure visibility [-1, 0, 0], # Mock to ensure visibility [-1, 0, 0], # Mock to ensure visibility [1, 0, 0], [0, 1, 0], [0, -1, 0], [0, 0, 1], [0, 0, -1]]) # Set the pinhole camera module camera = pinholeCamera.PinholeCamera() camera.f = 25*1e-3 camera.nxPixel = 2048 camera.nyPixel = 1536 camera.wPixel = (17.3*1e-3) / 2048 dcm_CB = np.array([[0, 0, -1], [0, 1, 0], [1, 0, 0]]) camera.dcm_CB = dcm_CB.tolist() for i in range(n_lmk): camera.addLandmark(pos_lmk[i, 0:3], normal_lmk[i, 0:3]) scSim.AddModelToTask(simTaskName, camera) # Write out mock planet ephemeris message planet_message = messaging.EphemerisMsgPayload() aP = 1.4583 * 149597870.7*1e3 r_PN_N = np.array([aP, 0, 0]) v_PN_N = np.array([0, np.sqrt(orbitalMotion.MU_SUN/aP), 0]) dcm_PN = np.identity(3) mrp_PN = rbk.C2MRP(dcm_PN) planet_message.r_BdyZero_N = r_PN_N planet_message.v_BdyZero_N = v_PN_N planet_message.sigma_BN = mrp_PN planetMsg = messaging.EphemerisMsg().write(planet_message) camera.ephemerisInMsg.subscribeTo(planetMsg) # Write out mock spacecraft message (pointing towards planet shall be ensured) a = 34 * 1e3 mu = 4.4631 * 1e5 r_BP_P = np.array([-a, 0, 0]) v_BP_P = np.array([0, -np.sqrt(mu/a), 0]) r_BN_N = r_BP_P + r_PN_N v_BN_N = v_BP_P + v_PN_N ir = r_BP_P / np.linalg.norm(r_BP_P) ih = np.cross(ir, v_BP_P/np.linalg.norm(v_BP_P)) it = np.cross(ir, ih) dcm_BP = np.zeros((3, 3)) dcm_BP[0:3, 0] = -ir dcm_BP[0:3, 1] = ih dcm_BP[0:3, 2] = it dcm_BN = np.matmul(dcm_BP, dcm_PN) mrp_BN = rbk.C2MRP(dcm_BN) sc1_message = messaging.SCStatesMsgPayload() sc1_message.r_BN_N = r_BN_N sc1_message.v_BN_N = v_BN_N sc1_message.sigma_BN = mrp_BN scMsg = messaging.SCStatesMsg().write(sc1_message) camera.scStateInMsg.subscribeTo(scMsg) # Log the landmark messages numDataPoints = 2 samplingTime = unitTestSupport.samplingTime(simulationTime, simulationTimeStep, numDataPoints) dataLog = [] for i in range(len(pos_lmk)): dataLog.append(camera.landmarkOutMsgs[i].recorder(samplingTime)) scSim.AddModelToTask(simTaskName, dataLog[i]) # Run the sim scSim.InitializeSimulation() scSim.ConfigureStopTime(simulationTime) scSim.ExecuteSimulation() # Get the logged data isvisibleLmk = np.zeros((2, n_lmk)) pixelLmk = np.zeros((2, n_lmk, 2)) for i in range(n_lmk): isvisibleLmk[:, i] = dataLog[i].isVisible pixelLmk[:, i, 0:2] = dataLog[i].pL # Define expected values # The corners shall correspond to the maximum pixel resolution # The default behaviour for a point lying in the origin is +1 accuracy = 1e-8 ref_pixel = np.array([[1, 1], [1, 1536/2], [1, -1536/2], [-2048/2, 1], [2048/2, 1], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]) ref_isvisible = np.array([1, 1, 1, 1, 1, 0, 0, 0, 0, 0]) # Compare to expected values pixel_worked = pixelLmk[0, :, :] == pytest.approx(ref_pixel, accuracy) isvisible_worked = isvisibleLmk[0, :] == pytest.approx(ref_isvisible, accuracy) assert (pixel_worked and isvisible_worked)
if __name__ == '__main__': test_visibility()