// Created by plusminus on 11:57:59 PM - Mar 30, 2009 package org.androad.adt.voice; import static org.androad.nav.util.Util.inBounds; import org.androad.adt.UnitSystem; import org.androad.sys.ors.adt.rs.RouteInstruction; import org.androad.util.constants.Constants; public class AudibleTurnCommandManager { // =========================================================== // Constants // =========================================================== /** Influence of the speed in seconds. 1 means that the sound will happen 1 second before the position has been reached. */ private static final float SPEED_INFLUENCE_SECONDS = 2; private static final int MARK_25000 = 25600; private static final int MARK_10000 = 10400; private static final int MARK_5000 = 5090; private static final int MARK_2000 = 2090; private static final int MARK_1000 = 1060; private static final int MARK_500 = 550; private static final int MARK_200 = 250; private static final int MARK_100 = 125; private static final int MARK_50 = 65; private static final int MIN_ANGLE_BEAR_LEFT = 20; private static final int MIN_ANGLE_LEFT = 40; private static final int MIN_ANGLE_SHARP_LEFT = 65; private static final int MIN_ANGLE_BEAR_RIGHT = MIN_ANGLE_BEAR_LEFT; private static final int MIN_ANGLE_RIGHT = MIN_ANGLE_LEFT; private static final int MIN_ANGLE_SHARP_RIGHT = MIN_ANGLE_SHARP_LEFT; // =========================================================== // Fields // =========================================================== private SimpleAudibleTurnCommand LASTAUDIBLETURNCOMMAND; private RouteInstruction LASTROUTEINSTRUCTION; // =========================================================== // Constructors // =========================================================== public AudibleTurnCommandManager() { } // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods from SuperClass/Interfaces // =========================================================== // =========================================================== // Methods // =========================================================== /** * @param pUS The {@Link UnitSystem} is used to scale up/down the distances (<code>oldDist</code> and <code>newDist</code>) between to determine * @param pRouteInstruction the Route-Instruction to extract the information from. * @param pOldDistMeters in METERS. * @param pNewDistMeters in METERS. * @param pCurrentSpeedMetersPerSecond * @param pUpperNextRouteInstruction * @return If neccessary a new {@link AudibleTurnCommand}, otherwise <code>null</code>. */ public AudibleTurnCommand createIfNeccessary(final UnitSystem pUS, final RouteInstruction pRouteInstruction, final int pOldDistMeters, final int pNewDistMeters, final float pCurrentSpeedMetersPerSecond, final RouteInstruction pUpperNextRouteInstruction) { final float pNewDistMetersSpeedAdjusted; if(pCurrentSpeedMetersPerSecond == Constants.NOT_SET){ pNewDistMetersSpeedAdjusted = pNewDistMeters; }else{ pNewDistMetersSpeedAdjusted = pNewDistMeters - SPEED_INFLUENCE_SECONDS * pCurrentSpeedMetersPerSecond; } final int oldDistKilometerScale = (int)(pOldDistMeters * pUS.mScaleToKilometers); final int oldDistMeterScale = (int)(pOldDistMeters * pUS.mScaleToMeters); final int newDistKilometerScale = (int)(pNewDistMetersSpeedAdjusted * pUS.mScaleToKilometers); final int newDistMeterScale = (int)(pNewDistMetersSpeedAdjusted * pUS.mScaleToMeters); final DistanceVoiceElement dve = getDistanceVoiceElementFromDistances(oldDistKilometerScale, oldDistMeterScale, newDistKilometerScale, newDistMeterScale); /* We didn't pass any of the 'gates' so nothing needs to be said. */ if(dve == null) { return null; } final int turnAngle = (int)pRouteInstruction.getAngle(); final TurnVoiceElement tve = getTurnVoiceElementFromAngle(turnAngle); final AudibleTurnCommand response = new AudibleTurnCommand(dve, tve, pRouteInstruction.getDescription(), turnAngle, pNewDistMeters); if(pUpperNextRouteInstruction != null && pRouteInstruction.getLengthMeters() * pUS.mScaleToMeters < 200 && newDistMeterScale < MARK_1000){ final TurnVoiceElement turnDistance = getTurnVoiceElementFromAngle((int)pUpperNextRouteInstruction.getAngle()); final DistanceVoiceElement voiceDistance = getCloserDistanceVoiceElement((int)(pRouteInstruction.getLengthMeters() * pUS.mScaleToMeters)); response.setThenCommand(new SimpleAudibleTurnCommand(voiceDistance, turnDistance)); } /* Check if the same thing has been said before. */ if(this.LASTAUDIBLETURNCOMMAND != null && this.LASTAUDIBLETURNCOMMAND.equals(response) && this.LASTROUTEINSTRUCTION != null && this.LASTROUTEINSTRUCTION.equals(pRouteInstruction) ){ this.LASTAUDIBLETURNCOMMAND = response; this.LASTROUTEINSTRUCTION = pRouteInstruction; return null; }else{ this.LASTAUDIBLETURNCOMMAND = response; this.LASTROUTEINSTRUCTION = pRouteInstruction; return response; } } public static DistanceVoiceElement getDistanceVoiceElementFromDistances(final int oldDistKilometerScale, final int oldDistMeterScale, final int newDistKilometerScale, final int newDistMeterScale) { if (inBounds(newDistKilometerScale, MARK_25000, oldDistKilometerScale)) { return DistanceVoiceElement.KM_TWENTY_FIVE; } else if (inBounds(newDistKilometerScale, MARK_10000, oldDistKilometerScale)) { return DistanceVoiceElement.KM_TEN; } else if (inBounds(newDistKilometerScale, MARK_5000, oldDistKilometerScale)) { return DistanceVoiceElement.KM_FIVE; } else if (inBounds(newDistKilometerScale, MARK_2000, oldDistKilometerScale)) { return DistanceVoiceElement.KM_TWO; } else if (inBounds(newDistKilometerScale, MARK_1000, oldDistKilometerScale)) { return DistanceVoiceElement.KM_ONE; } else if (inBounds(newDistMeterScale, MARK_500, oldDistMeterScale)) { return DistanceVoiceElement.M_500; } else if (inBounds(newDistMeterScale, MARK_200, oldDistMeterScale)) { return DistanceVoiceElement.M_200; } else if (inBounds(newDistMeterScale, MARK_100, oldDistMeterScale)) { return DistanceVoiceElement.M_100; } else if (inBounds(newDistMeterScale, MARK_50, oldDistMeterScale)) { return DistanceVoiceElement.M_50; } else { /* We didn't pass any of the 'gates' so nothing needs to be said. */ return null; } } public static DistanceVoiceElement getCloserDistanceVoiceElement(final int pDistance) { if(pDistance < MARK_50) { return null; } else if(pDistance < MARK_100) { return DistanceVoiceElement.M_50; } else if(pDistance < MARK_200) { return DistanceVoiceElement.M_100; } else if(pDistance < MARK_500) { return DistanceVoiceElement.M_200; } else { throw new IllegalArgumentException("getCloserDistanceVoiceElement() should not be called for pDistance > MARK_500 (was: " + pDistance + ")"); } } public static TurnVoiceElement getTurnVoiceElementFromAngle(final int pTurnAngle) { final TurnVoiceElement tve; if (pTurnAngle > MIN_ANGLE_SHARP_LEFT) { tve = TurnVoiceElement.LEFT_SHARP; } else if (pTurnAngle > MIN_ANGLE_LEFT) { tve = TurnVoiceElement.LEFT; } else if (pTurnAngle > MIN_ANGLE_BEAR_LEFT) { tve = TurnVoiceElement.LEFT_BEAR; } else if (pTurnAngle <= MIN_ANGLE_BEAR_LEFT && pTurnAngle >= -MIN_ANGLE_BEAR_RIGHT) { tve = TurnVoiceElement.STRAIGHT_ON; } else if (pTurnAngle > -MIN_ANGLE_RIGHT) { tve = TurnVoiceElement.RIGHT_BEAR; } else if (pTurnAngle > -MIN_ANGLE_SHARP_RIGHT) { tve = TurnVoiceElement.RIGHT; } else { tve = TurnVoiceElement.RIGHT_SHARP; } return tve; } // =========================================================== // Inner and Anonymous Classes // =========================================================== }