package com.momega.spacesimulator.service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.momega.spacesimulator.model.CartesianState;
import com.momega.spacesimulator.model.CelestialBody;
import com.momega.spacesimulator.model.KeplerianElements;
import com.momega.spacesimulator.model.KeplerianOrbit;
import com.momega.spacesimulator.model.Line;
import com.momega.spacesimulator.model.MovingObject;
import com.momega.spacesimulator.model.OrbitIntersection;
import com.momega.spacesimulator.model.Plane;
import com.momega.spacesimulator.model.Spacecraft;
import com.momega.spacesimulator.model.Target;
import com.momega.spacesimulator.model.Timestamp;
import com.momega.spacesimulator.model.Vector3d;
import com.momega.spacesimulator.utils.VectorUtils;
/**
* The target service contains several methods for manipulating with the {@link com.momega.spacesimulator.model.Target} and computing
* the spacecraft points related to the target such as {@link com.momega.spacesimulator.model.OrbitIntersection}.
* Created by martin on 10/19/14.
*/
@Component
public class TargetService {
private static final Logger logger = LoggerFactory.getLogger(TargetService.class);
/**
* Creates the target
* @param spacecraft the spacecraft
* @param celestialBody the target body, if null the target will be removed
* @return new instance of the target or null
*/
public Target createTarget(Spacecraft spacecraft, CelestialBody celestialBody) {
Assert.notNull(spacecraft);
Target target;
if (celestialBody == null) {
target = new Target();
logger.debug("remove the target");
} else if (!ObjectUtils.nullSafeEquals(spacecraft.getTarget(), celestialBody)) {
target = new Target();
target.setTargetBody(celestialBody);
logger.info("set target body {} for {}", celestialBody.getName(), spacecraft.getName());
} else {
target = spacecraft.getTarget();
logger.debug("no change in target");
}
spacecraft.setTarget(target);
return target;
}
public void computeTargetParameters(MovingObject spacecraft, CelestialBody targetBody, Target target) {
if (targetBody == null) {
return;
}
if (targetBody.isStatic()) {
return;
}
Plane spacecraftPlane = createOrbitalPlane(spacecraft);
Plane targetBodyPlane = createOrbitalPlane(targetBody);
double angle = spacecraftPlane.angleBetween(targetBodyPlane);
target.setAngle(Double.valueOf(angle));
target.setDistance(spacecraft.getPosition().subtract(targetBody.getPosition()).length());
}
/**
* Computes the intersection points
* @param spacecraft
* @param newTimestamp
*/
public void computeOrbitIntersection(Spacecraft spacecraft, Timestamp newTimestamp) {
Target target = spacecraft.getTarget();
Assert.notNull(target);
CelestialBody targetBody = target.getTargetBody();
Assert.notNull(targetBody);
Plane spacecraftPlane = createOrbitalPlane(spacecraft);
Plane targetBodyPlane = createOrbitalPlane(targetBody);
if (targetBodyPlane == null) {
target.setOrbitIntersections(new ArrayList<OrbitIntersection>());
target.setAngle(null);
target.setDistance(null);
return;
}
double angle = spacecraftPlane.angleBetween(targetBodyPlane);
target.setAngle(angle);
target.setDistance(spacecraft.getPosition().subtract(targetBody.getPosition()).length());
logger.debug("planesAngle = {}", Math.toDegrees(angle));
KeplerianOrbit orbit = spacecraft.getKeplerianElements().getKeplerianOrbit();
Line intersectionLine;
try {
intersectionLine = spacecraftPlane.intersection(targetBodyPlane, orbit.getReferenceFrame().getPosition());
} catch (IllegalStateException e) {
logger.warn("almost colinear planes, planesAngle = {}", Math.toDegrees(angle));
target.setOrbitIntersections(new ArrayList<OrbitIntersection>());
return;
}
// now transform to 2D to compute intersections
Vector3d intersectionLinePoint = intersectionLine.getOrigin().subtract(orbit.getReferenceFrame().getPosition());
intersectionLinePoint = VectorUtils.transform(orbit, intersectionLinePoint);
Vector3d intersectionLineVector = VectorUtils.transform(orbit, intersectionLine.getDirection()).normalize();
intersectionLine = new Line(intersectionLinePoint, intersectionLineVector);
intersectionLine = intersectionLine.move(new Vector3d(orbit.getSemimajorAxis() * orbit.getEccentricity(), 0, 0));
Double[] angles = orbit.lineIntersection(intersectionLine);
if (target.getOrbitIntersections().size() != angles.length ) {
target.getOrbitIntersections().clear();
}
// create intersection, if there are not
List<OrbitIntersection> intersections = target.getOrbitIntersections();
if (intersections.isEmpty()) {
for(int i=0; i<angles.length; i++) {
OrbitIntersection intersection = new OrbitIntersection();
intersections.add(intersection);
KeplerianElements keplerianElements = new KeplerianElements();
intersection.setKeplerianElements(keplerianElements);
intersection.setName(spacecraft.getName() +"/" + targetBody.getName() + " Intersection " + i);
intersection.setTargetObject(targetBody);
intersection.setVisible(true);
intersection.setMovingObject(spacecraft);
}
}
// update orbital intersection
for(int i=0; i<intersections.size(); i++) {
double eta = angles[i];
double theta = KeplerianElements.solveTheta(eta, orbit.getEccentricity());
OrbitIntersection intersection = intersections.get(i);
KeplerianElements keplerianElements = intersection.getKeplerianElements();
keplerianElements.setKeplerianOrbit(orbit);
keplerianElements.setTrueAnomaly(theta);
if (orbit.isHyperbolic()) {
keplerianElements.setHyperbolicAnomaly(eta);
} else {
keplerianElements.setEccentricAnomaly(eta);
}
Vector3d vector = orbit.getCartesianPosition(theta);
intersection.setPosition(vector);
intersection.setTimestamp(spacecraft.getKeplerianElements().timeToAngle(newTimestamp, theta, true));
}
}
public List<OrbitIntersection> getOrbitIntersections(Spacecraft spacecraft) {
if (spacecraft.getTarget() != null) {
return spacecraft.getTarget().getOrbitIntersections();
}
return Collections.emptyList();
}
protected Plane createOrbitalPlane(MovingObject movingObject) {
CartesianState relative = VectorUtils.relativeCartesianState(movingObject);
Vector3d normal = relative.getAngularMomentum();
Vector3d origin = movingObject.getKeplerianElements().getKeplerianOrbit().getReferenceFrame().getPosition();
return new Plane(origin, normal);
}
public void clear(Spacecraft spacecraft) {
Target target = spacecraft.getTarget();
if (target != null) {
target.getOrbitIntersections().clear();
target.setKeplerianElements(null);
}
}
}