-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
229 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
using LinearAlgebra | ||
using JTools | ||
using Quaternions | ||
using SpacecraftBuilder | ||
using GLMakie | ||
|
||
function mainSc() | ||
|
||
cb = SpacecraftElement( | ||
ID="CB", | ||
mass=6900.0, | ||
posEG_E=[25/1000, 3/1000, 1556/1000], | ||
geometry=Cuboid(2.5, 2.5, 1556*2/1000), | ||
inertiaG_E=[4285.0 -77 -311; -77 5070.0 102; -311 102 1795.0] | ||
) | ||
|
||
# Solar arrays | ||
θ = 90π/180 | ||
mass = 212.0 | ||
geom = Cuboid(6.648, 14.64, 0.015) | ||
inertiaG_E = [4730 -4.33 -0.026; -4.33 728 28; -0.026 28 5460] | ||
posEG_E = [0.0; 9.95; 0.02] | ||
|
||
sa1 = SpacecraftElement( | ||
ID="SA (+Y)", | ||
mass=mass, | ||
geometry=geom, | ||
inertiaG_E=inertiaG_E, | ||
posEG_E=posEG_E, | ||
posOE_O=[0.0; 1.25; 1], | ||
R_OE=dcm_fromAxisAngle(2, θ), | ||
) | ||
|
||
sa2 = SpacecraftElement( | ||
ID="SA (-Y)", | ||
mass=mass, | ||
geometry=geom, | ||
inertiaG_E=inertiaG_E, | ||
posEG_E=posEG_E, | ||
posOE_O=[0.0; -1.25; 1], | ||
R_OE=dcm_fromAxisAngle(3, π)*dcm_fromAxisAngle(2, -θ), | ||
) | ||
|
||
sc, model = build([cb; sa1; sa2]; plotModel=true) | ||
|
||
return rayTracingSrp(model, [-1.0; 0.0; 0.0]; Nrays=100_000) | ||
end | ||
mainSc() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
mutable struct Ray | ||
origin::Vector | ||
dir::Vector | ||
t::Real | ||
idxFace::Int | ||
end | ||
|
||
Ray(origin, dir) = Ray(origin, normalize(dir), Inf, 0) | ||
|
||
const EPS_PARALLEL = 1e-8 | ||
const ZERO_WITH_TOL = -1e-8 | ||
const ONE_WITH_TOL = 1.0 + 1e-8 | ||
const EPS_MIN_DIST = 1e-4 | ||
const RAY_SHIFT = 1e-2 | ||
const DEFAULT_NRAYS = 1_000_000 | ||
|
||
@views function intersect!(ray::Ray, tri, idx::Int) | ||
E12 = tri[2] - tri[1] | ||
E13 = tri[3] - tri[1] | ||
P = ray.dir × E13 | ||
detM = dot(P, E12) | ||
|
||
if abs(detM) ≤ EPS_PARALLEL; return; end | ||
|
||
T = ray.origin - tri[1] | ||
u = dot(P, T)/detM | ||
if u < ZERO_WITH_TOL || u > ONE_WITH_TOL; return; end | ||
|
||
Q = T × E12 | ||
v = dot(Q, ray.dir)/detM | ||
if v < ZERO_WITH_TOL || u + v > ONE_WITH_TOL; return; end | ||
|
||
t = dot(E13, Q)/detM | ||
if t > EPS_MIN_DIST && t < ray.t | ||
ray.t = t | ||
ray.idxFace = idx | ||
end | ||
end | ||
|
||
@views function intersect!(ray::Ray, bboxMin, bboxMax) | ||
t1 = (bboxMin - ray.origin)./ray.dir | ||
t2 = (bboxMax - ray.origin)./ray.dir | ||
tmin = min(t1[1], t2[1]) | ||
tmax = max(t1[1], t2[1]) | ||
|
||
tmin = max(tmin, min(t1[2], t2[2])) | ||
tmax = min(tmax, max(t1[2], t2[2])) | ||
|
||
tmin = max(0.0, max(tmin, min(t1[3], t2[3]))) | ||
tmax = min(tmax, max(t1[3], t2[3])) | ||
return tmax ≥ tmin | ||
end | ||
|
||
# Qdyn = 1/2ρV^2 | ||
function rayTracingDrag(model::GeometryBasics.Mesh, dirVel::Vector{Float64}, Qdyn=1.0, Cx=1.0; Nrays::Int=DEFAULT_NRAYS) | ||
S, posCoP = rayTracingSrp(model, dirVel; Nrays=Nrays, Nrec=1, mode=:drag) | ||
forceDrag = -Qdyn*Cx*S*normalize(dirVel) | ||
torqueDrag = posCoP × forceDrag | ||
return forceDrag, torqueDrag | ||
end | ||
|
||
function rayTracingSurface(model::GeometryBasics.Mesh, dirObs::Vector{Float64}; Nrays::Int=DEFAULT_NRAYS) | ||
return rayTracingSrp(model, dirObs; Nrays=Nrays, Nrec=1, mode=:surf)[1] | ||
end | ||
|
||
function rayTracingSrp(model::GeometryBasics.Mesh, dirSun::Vector{Float64}, Psrp=1.0; Nrays::Int=DEFAULT_NRAYS, Nrec=3, mode=:srp) | ||
|
||
anyIntersection = mode != :srp | ||
α = 0.7; rd = 0.1; rs = 0.2 | ||
|
||
# Model box | ||
bboxMin = [minimum(getindex.(model.position, i)) for i in 1:3] | ||
bboxMax = [maximum(getindex.(model.position, i)) for i in 1:3] | ||
X0 = (bboxMin + bboxMax)/2 | ||
R = maximum(bboxMax - X0) | ||
|
||
# Creates rays matrix | ||
coords = 1.1*R*range(-1, 1, round(Int, √Nrays)) | ||
dirRay = -normalize(dirSun) | ||
R_IS = genOrthogonalAxes(dirSun) | ||
Ap = (coords[2] - coords[1])^2 | ||
|
||
rays = Ray[] | ||
for x in coords, y in coords | ||
push!(rays, Ray(X0 + R_IS*[x; y; 3R], dirRay)) | ||
end | ||
|
||
# Intersect model | ||
surf = 0.0; posCoP = zeros(3) | ||
forceSrp = zeros(3); torqueSrp = zeros(3) | ||
for ray in rays | ||
# Check if the ray intersects the object's bounding box | ||
if intersect!(ray, bboxMin, bboxMax) | ||
psrp = Psrp | ||
# Initialize ray recursion | ||
for n in 1:Nrec | ||
# Intersect ray with entire model (closest intersection) | ||
for k in eachindex(model) | ||
intersect!(ray, model[k], k) | ||
if anyIntersection && ray.t < Inf; break; end | ||
end | ||
|
||
# Check if any intersection has been found | ||
if ray.t == Inf | ||
break | ||
elseif mode == :srp | ||
# Compute SRP | ||
N, cosθ, newDir = rayReflection(model[ray.idxFace], ray) | ||
frc, trq, psrp, posHit = srpFormula(ray, psrp, Ap, N, α, rd, rs, cosθ) | ||
forceSrp += frc | ||
torqueSrp += trq | ||
|
||
# Update ray for next reflection | ||
if n < Nrec | ||
ray.t = Inf | ||
ray.dir = newDir | ||
ray.origin = posHit + RAY_SHIFT*newDir | ||
end | ||
else | ||
# Compute surface and center of pressure | ||
surf += Ap | ||
posCoP .+= ray.origin*Ap | ||
end | ||
end | ||
end | ||
end | ||
|
||
# Output | ||
if mode == :srp | ||
return forceSrp, torqueSrp | ||
else | ||
return surf, posCoP./surf | ||
end | ||
end | ||
|
||
function rayReflection(face, ray) | ||
E1 = face[2] - face[1] | ||
E2 = face[3] - face[2] | ||
N = normalize(E1 × E2) | ||
cosθ = -N'*ray.dir | ||
|
||
# Fix normal direction of current face on the basis of ray | ||
# intersection result (the dot product between minus ray direction | ||
# and face normal must be positive) | ||
if cosθ < 0.0 | ||
N = -N | ||
cosθ = -cosθ | ||
end | ||
|
||
return N, cosθ, ray.dir + 2cosθ*N | ||
end | ||
|
||
function srpFormula(ray, Psrp, Ap, N, α, rd, rs, cosθ) | ||
|
||
# Compute forces generated by each single ray intersecting the object | ||
forceSrp = -Psrp*Ap*(-(α + rd)*ray.dir + 2(rs*cosθ + rd/3)*N) | ||
|
||
# Compute total torque wrt the origin of the model | ||
# (i.e., origin of the coordinates of the vertex of the model) | ||
posHit = ray.origin + ray.t*ray.dir | ||
torqueSrp = posHit × forceSrp | ||
|
||
# Update SRP flux for next batch of reflected rays | ||
return forceSrp, torqueSrp, Psrp*rs, posHit | ||
end | ||
|
||
function genOrthogonalAxes(zB_A) | ||
nu = norm(zB_A) | ||
if nu > 0.0 | ||
zB_A = zB_A./nu | ||
VB_A = [zB_A × [i==1; i==2; i==3] for i in 1:3] | ||
vNorm = norm.(VB_A) | ||
iMax = findfirst(vNorm .≥ 0.2) | ||
vB_A = VB_A[iMax]./vNorm[iMax] | ||
return [(vB_A × zB_A) vB_A zB_A] | ||
end | ||
return Matrix(1.0I, 3, 3) | ||
end |