<commit_before>package Debrief.Wrappers.Track;

import java.util.Enumeration;
import java.util.Vector;

import Debrief.Wrappers.FixWrapper;
import Debrief.Wrappers.SensorContactWrapper;
import Debrief.Wrappers.SensorWrapper;
import Debrief.Wrappers.TrackWrapper;
import MWC.GUI.Editable;
import MWC.GenericData.HiResDate;
import MWC.GenericData.WorldDistance;
import MWC.GenericData.WorldLocation;
import MWC.GenericData.WorldVector;
import MWC.GenericData.WorldDistance.ArrayLength;
import MWC.TacticalData.Fix;

/**
 * class to calculate offset position of sensor back from host location by
 * travelling back along host track for specified distance
 * 
 * @author ianmayo
 * 
 */
public class WormInHoleOffset
{

	/**
	 * 
	 * @param track
	 * @param dtg
	 * @param arrayOffset
	 * @return
	 */
	public static WorldLocation getWormOffsetFor(TrackWrapper track,
			HiResDate dtg, WorldDistance arrayOffset)
	{
		WorldLocation res = null;
		double offsetM = -arrayOffset.getValueIn(WorldDistance.METRES);
		// double offsetM = Math.abs(arrayOffset.getValueIn(WorldDistance.METRES));

		// check we're in period
		if (dtg.lessThan(track.getStartDTG()) || dtg.greaterThan(track.getEndDTG()))
			return res;

		// start off by bracketing the time. work back along the track legs until we
		// find the two positions either side
		// of the time we're looking for
		Enumeration<Editable> enumer = track.getPositions();

		Vector<FixWrapper> backTrack = new Vector<FixWrapper>();
		FixWrapper nextPoint = null;

		while (enumer.hasMoreElements())
		{
			FixWrapper thisP = (FixWrapper) enumer.nextElement();

			if ((backTrack.size() == 0) && (thisP.getDateTimeGroup().equals(dtg)))
			{
				// right, this is the first point, and we've matched it already. Just
				// produce
				// a vector back down ownship path
				res = new WorldLocation(thisP.getLocation().add(
						new WorldVector(thisP.getCourse(), MWC.Algorithms.Conversions
								.m2Degs(-offsetM), 0d)));
				return res;
			}
			else if (thisP.getDateTimeGroup().lessThan(dtg))
			{
				backTrack.add(thisP);
			}
			else
			{
				// we've passed the point
				nextPoint = thisP;
				break;
			}
		}

		// right, we have a back track. double-check we have our next point
		if (backTrack.size() > 0)
		{
			// yup, we've bracketed the point. work out where ownship is at this DTG
			nextPoint = FixWrapper.interpolateFix(backTrack.lastElement(), nextPoint,
					dtg);

			for (int i = backTrack.size() - 1; i >= 0; i--)
			{
				FixWrapper thisI = backTrack.elementAt(i);

				double thisLen = nextPoint.getLocation()
						.subtract(thisI.getFixLocation()).getRange();
				double thisLenM = MWC.Algorithms.Conversions.Degs2m(thisLen);

				// is this less than our stub?
				if (thisLenM > offsetM)
				{
					// so just interpolate along the path
					double posDelta = offsetM / thisLenM;
					double timeDelta = (nextPoint.getDTG().getMicros() - thisI.getDTG()
							.getMicros()) * posDelta;
					FixWrapper newMidFix = FixWrapper.interpolateFix(thisI, nextPoint,
							new HiResDate(0,
									(long) (nextPoint.getDTG().getMicros() - timeDelta)));
					res = newMidFix.getLocation();
					offsetM = 0;
					break;
				}
				else
				{
					offsetM -= thisLenM;
					nextPoint = thisI;
				}
			}
		}

		// do we have any array left to consume?
		if (offsetM > 0)
		{
			// are we on the first data point?
			if (nextPoint != null)
			{
				// yup, just use that one
				res = nextPoint.getLocation();

				// offset by the array length along the heading
				res = new WorldLocation(res.add(new WorldVector(nextPoint.getCourse(),
						MWC.Algorithms.Conversions.m2Degs(-offsetM), 0d)));
			}
		}

		return res;
	}

	// ////////////////////////////////////////////////////////////////////////////////////////////////
	// testing for this class
	// ////////////////////////////////////////////////////////////////////////////////////////////////
	static public final class testMe extends junit.framework.TestCase
	{
		public void testData() throws InterruptedException
		{
			TrackWrapper track = getDummyTrack();

			assertNotNull("track generated", track);
			assertEquals("correct points", 5, track.numFixes());

			// get the sensor
			SensorWrapper sw = (SensorWrapper) track.getSensors().elements()
					.nextElement();

			// find the position of the last fix
			SensorContactWrapper scw = (SensorContactWrapper) sw.getNearestTo(sw
					.getEndDTG())[0];

			assertNotNull("fix found", scw);

			outputSensorTrack(sw);
			Thread.sleep(100);

			outputTrack(track);

			Thread.sleep(100);
			sw.setWormInHole(true);
			outputSensorTrack(sw);

		}

		private void outputTrack(TrackWrapper track)
		{
			Enumeration<Editable> numer = track.getPositions();
			while (numer.hasMoreElements())
			{
				FixWrapper thisF = (FixWrapper) numer.nextElement();
				System.err.println(MWC.Algorithms.Conversions.Degs2m(thisF
						.getLocation().getLong())
						+ ", "
						+ MWC.Algorithms.Conversions.Degs2m(thisF.getLocation().getLat()));
			}
		}

		private void outputSensorTrack(SensorWrapper sensor)
		{<commit_msg>improve testing of worm in hole. overcome problem incurred with zero length offset<commit_after>package Debrief.Wrappers.Track;

import java.util.Enumeration;
import java.util.Vector;

import Debrief.Wrappers.FixWrapper;
import Debrief.Wrappers.SensorContactWrapper;
import Debrief.Wrappers.SensorWrapper;
import Debrief.Wrappers.TrackWrapper;
import MWC.GUI.Editable;
import MWC.GenericData.HiResDate;
import MWC.GenericData.WorldDistance;
import MWC.GenericData.WorldLocation;
import MWC.GenericData.WorldVector;
import MWC.GenericData.WorldDistance.ArrayLength;
import MWC.TacticalData.Fix;

/**
 * class to calculate offset position of sensor back from host location by
 * travelling back along host track for specified distance
 * 
 * @author ianmayo
 * 
 */
public class WormInHoleOffset
{

	/**
	 * 
	 * @param track
	 * @param dtg
	 * @param arrayOffset
	 * @return
	 */
	public static WorldLocation getWormOffsetFor(TrackWrapper track,
			HiResDate dtg, WorldDistance arrayOffset)
	{
		WorldLocation res = null;
		double offsetM = -arrayOffset.getValueIn(WorldDistance.METRES);
		// double offsetM = Math.abs(arrayOffset.getValueIn(WorldDistance.METRES));

		// check we're in period
		if (dtg.lessThan(track.getStartDTG()) || dtg.greaterThan(track.getEndDTG()))
			return res;

		// start off by bracketing the time. work back along the track legs until we
		// find the two positions either side
		// of the time we're looking for
		Enumeration<Editable> enumer = track.getPositions();

		Vector<FixWrapper> backTrack = new Vector<FixWrapper>();
		FixWrapper nextPoint = null;

		while (enumer.hasMoreElements())
		{
			FixWrapper thisP = (FixWrapper) enumer.nextElement();

			if ((backTrack.size() == 0) && (thisP.getDateTimeGroup().equals(dtg)))
			{
				// right, this is the first point, and we've matched it already. Just
				// produce
				// a vector back down ownship path
				res = new WorldLocation(thisP.getLocation().add(
						new WorldVector(thisP.getCourse(), MWC.Algorithms.Conversions
								.m2Degs(-offsetM), 0d)));
				return res;
			}
			else if (thisP.getDateTimeGroup().lessThan(dtg))
			{
				backTrack.add(thisP);
			}
			else
			{
				// we've passed the point
				nextPoint = thisP;
				break;
			}
		}

		// right, we have a back track. double-check we have our next point
		if (backTrack.size() > 0)
		{
			// yup, we've bracketed the point. work out where ownship is at this DTG
			nextPoint = FixWrapper.interpolateFix(backTrack.lastElement(), nextPoint,
					dtg);

			for (int i = backTrack.size() - 1; i >= 0; i--)
			{
				FixWrapper thisI = backTrack.elementAt(i);

				double thisLen = nextPoint.getLocation()
						.subtract(thisI.getFixLocation()).getRange();
				double thisLenM = MWC.Algorithms.Conversions.Degs2m(thisLen);

				// is this less than our stub?
				if (thisLenM >= offsetM)
				{
					// so just interpolate along the path
					double posDelta = offsetM / thisLenM;
					double timeDelta = (nextPoint.getDTG().getMicros() - thisI.getDTG()
							.getMicros()) * posDelta;
					FixWrapper newMidFix = FixWrapper.interpolateFix(thisI, nextPoint,
							new HiResDate(0,
									(long) (nextPoint.getDTG().getMicros() - timeDelta)));
					res = newMidFix.getLocation();
					offsetM = 0;
					break;
				}
				else
				{
					offsetM -= thisLenM;
					nextPoint = thisI;
				}
			}
		}

		// do we have any array left to consume?
		if (offsetM > 0)
		{
			// are we on the first data point?
			if (nextPoint != null)
			{
				// yup, just use that one
				res = nextPoint.getLocation();

				// offset by the array length along the heading
				res = new WorldLocation(res.add(new WorldVector(nextPoint.getCourse(),
						MWC.Algorithms.Conversions.m2Degs(-offsetM), 0d)));
			}
		}

		return res;
	}

	// ////////////////////////////////////////////////////////////////////////////////////////////////
	// testing for this class
	// ////////////////////////////////////////////////////////////////////////////////////////////////
	static public final class testMe extends junit.framework.TestCase
	{
		public void testData() throws InterruptedException
		{
			TrackWrapper track = getDummyTrack();

			assertNotNull("track generated", track);
			assertEquals("correct points", 5, track.numFixes());

			// get the sensor
			SensorWrapper sw = (SensorWrapper) track.getSensors().elements()
					.nextElement();

			// find the position of the last fix
			SensorContactWrapper scw = (SensorContactWrapper) sw.getNearestTo(sw
					.getEndDTG())[0];

			assertNotNull("fix found", scw);

			outputSensorTrack(sw);
			Thread.sleep(100);

			outputTrack(track);

			Thread.sleep(100);
			sw.setWormInHole(true);
			outputSensorTrack(sw);

			// get a location
			WorldLocation res = WormInHoleOffset.getWormOffsetFor(track,
					track.getEndDTG(), sw.getSensorOffset());
			assertNotNull("failed to find location");
			// give it another go, with a zero sensor offset
			sw.setSensorOffset(new ArrayLength(0d));

			res = WormInHoleOffset.getWormOffsetFor(track, track.getEndDTG(),
					sw.getSensorOffset());
			assertNotNull("failed to find location");

		}

		private void outputTrack(TrackWrapper track)
		{
			Enumeration<Editable> numer = track.getPositions();
			while (numer.hasMoreElements())
			{
				FixWrapper thisF = (FixWrapper) numer.nextElement();
				System.err.println(MWC.Algorithms.Conversions.Degs2m(thisF
						.getLocation().getLong())
						+ ", "
						+ MWC.Algorithms.Conversions.Degs2m(thisF.getLocation().getLat()));
			}
		}

		private void outputSensorTrack(SensorWrapper sensor)
		{
{'fraction_non_alphanumeric': 0.14695676638844604, 'fraction_numerical': 0.004220200693988559, 'mean_word_length': 2.8933917488134355, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5454511', 'n_tokens_mistral': 3936, 'n_tokens_neox': 3238, 'n_words': 817}