forked from organicmaps/organicmaps
Merge pull request #311 from bykoianko/the-turn-after-the-next-fix6
Displaying the turn after the next simultaneously with pronouncing the info about the turn after the next.
This commit is contained in:
commit
f948035a01
16 changed files with 709 additions and 561 deletions
|
@ -1262,14 +1262,14 @@ extern "C"
|
|||
}
|
||||
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_com_mapswithme_maps_Framework_nativeGenerateTurnSound(JNIEnv * env, jclass thiz)
|
||||
Java_com_mapswithme_maps_Framework_nativeGenerateTurnNotifications(JNIEnv * env, jclass thiz)
|
||||
{
|
||||
::Framework * fr = frm();
|
||||
if (!fr->IsRoutingActive())
|
||||
return nullptr;
|
||||
|
||||
vector<string> turnNotifications;
|
||||
fr->GenerateTurnSound(turnNotifications);
|
||||
fr->GenerateTurnNotifications(turnNotifications);
|
||||
if (turnNotifications.empty())
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -137,12 +137,12 @@ public class Framework
|
|||
public native static RoutingInfo nativeGetRouteFollowingInfo();
|
||||
|
||||
// When an end user is going to a turn he gets sound turn instructions.
|
||||
// If C++ part wants the client to pronounce an instruction nativeGenerateTurnSound returns
|
||||
// If C++ part wants the client to pronounce an instruction nativeGenerateTurnNotifications returns
|
||||
// an array of one of more strings. C++ part assumes that all these strings shall be pronounced by the client's TTS.
|
||||
// For example if C++ part wants the client to pronounce "Make a right turn." this method returns
|
||||
// an array with one string "Make a right turn.". The next call of the method returns nothing.
|
||||
// nativeGenerateTurnSound shall be called by the client when a new position is available.
|
||||
public native static String[] nativeGenerateTurnSound();
|
||||
// nativeGenerateTurnNotifications shall be called by the client when a new position is available.
|
||||
public native static String[] nativeGenerateTurnNotifications();
|
||||
|
||||
public native static void nativeSetRoutingListener(RoutingListener listener);
|
||||
|
||||
|
|
|
@ -142,11 +142,10 @@ public enum TtsPlayer
|
|||
|
||||
public void playTurnNotifications()
|
||||
{
|
||||
if (!isValid())
|
||||
return; // speak() is called while TTS is not ready or could not be initialized.
|
||||
|
||||
final String[] turnNotifications = Framework.nativeGenerateTurnSound();
|
||||
if (turnNotifications != null)
|
||||
// It's necessary to call Framework.nativeGenerateTurnNotifications() even if TtsPlayer is invalid.
|
||||
final String[] turnNotifications = Framework.nativeGenerateTurnNotifications();
|
||||
|
||||
if (turnNotifications != null && isValid())
|
||||
for (String textToSpeak : turnNotifications)
|
||||
speak(textToSpeak);
|
||||
}
|
||||
|
|
|
@ -128,16 +128,16 @@ extern NSString * const kMwmTextToSpeechDisable = @"MWMTEXTTOSPEECH_DISABLE";
|
|||
|
||||
- (void)playTurnNotifications
|
||||
{
|
||||
if (![self isValid])
|
||||
return;
|
||||
|
||||
Framework & frm = GetFramework();
|
||||
if (!frm.IsRoutingActive())
|
||||
return;
|
||||
|
||||
vector<string> notifications;
|
||||
frm.GenerateTurnSound(notifications);
|
||||
|
||||
frm.GenerateTurnNotifications(notifications);
|
||||
|
||||
if (![self isValid])
|
||||
return;
|
||||
|
||||
for (auto const & text : notifications)
|
||||
[self speakOneString:@(text.c_str())];
|
||||
}
|
||||
|
|
|
@ -613,13 +613,17 @@ public:
|
|||
/// If not, it returns an empty string.
|
||||
inline string GetTurnNotificationsLocale() const { return m_routingSession.GetTurnNotificationsLocale(); }
|
||||
/// \brief When an end user is going to a turn he gets sound turn instructions.
|
||||
/// If C++ part wants the client to pronounce an instruction GenerateTurnSound (in turnNotifications) returns
|
||||
/// an array of one of more strings. C++ part assumes that all these strings shall be pronounced by the client's TTS.
|
||||
/// If C++ part wants the client to pronounce an instruction GenerateTurnNotifications (in
|
||||
/// turnNotifications) returns
|
||||
/// an array of one of more strings. C++ part assumes that all these strings shall be pronounced
|
||||
/// by the client's TTS.
|
||||
/// For example if C++ part wants the client to pronounce "Make a right turn." this method returns
|
||||
/// an array with one string "Make a right turn.". The next call of the method returns nothing.
|
||||
/// GenerateTurnSound shall be called by the client when a new position is available.
|
||||
inline void GenerateTurnSound(vector<string> & turnNotifications)
|
||||
{ return m_routingSession.GenerateTurnSound(turnNotifications); }
|
||||
/// GenerateTurnNotifications shall be called by the client when a new position is available.
|
||||
inline void GenerateTurnNotifications(vector<string> & turnNotifications)
|
||||
{
|
||||
return m_routingSession.GenerateTurnNotifications(turnNotifications);
|
||||
}
|
||||
|
||||
private:
|
||||
void SetRouterImpl(routing::RouterType type);
|
||||
|
|
|
@ -39,7 +39,7 @@ SOURCES += \
|
|||
speed_camera.cpp \
|
||||
turns.cpp \
|
||||
turns_generator.cpp \
|
||||
turns_sound.cpp \
|
||||
turns_notification_manager.cpp \
|
||||
turns_sound_settings.cpp \
|
||||
turns_tts_text.cpp \
|
||||
vehicle_model.cpp \
|
||||
|
@ -75,7 +75,7 @@ HEADERS += \
|
|||
speed_camera.hpp \
|
||||
turns.hpp \
|
||||
turns_generator.hpp \
|
||||
turns_sound.hpp \
|
||||
turns_notification_manager.hpp \
|
||||
turns_sound_settings.hpp \
|
||||
turns_tts_text.hpp \
|
||||
vehicle_model.hpp \
|
||||
|
|
|
@ -21,10 +21,6 @@ int constexpr kOnRouteMissedCount = 5;
|
|||
|
||||
// @TODO(vbykoianko) The distance should depend on the current speed.
|
||||
double constexpr kShowLanesDistInMeters = 500.;
|
||||
// @TODO(vbykoianko) The distance should depend on the current speed.
|
||||
// The distance before the next turn in meters when notification
|
||||
// about the turn after the next one will be shown if available.
|
||||
double constexpr kShowTheTurnAfterTheNextM = 500.;
|
||||
|
||||
// @todo(kshalnev) The distance may depend on the current speed.
|
||||
double constexpr kShowPedestrianTurnInMeters = 5.;
|
||||
|
@ -110,7 +106,7 @@ void RoutingSession::RemoveRouteImpl()
|
|||
m_state = RoutingNotActive;
|
||||
m_lastDistance = 0.0;
|
||||
m_moveAwayCounter = 0;
|
||||
m_turnsSound.Reset();
|
||||
m_turnNotificationsMgr.Reset();
|
||||
|
||||
Route(string()).Swap(m_route);
|
||||
}
|
||||
|
@ -154,7 +150,7 @@ RoutingSession::State RoutingSession::OnLocationPositionChanged(m2::PointD const
|
|||
UNUSED_VALUE(guard);
|
||||
ASSERT(m_route.IsValid(), ());
|
||||
|
||||
m_turnsSound.SetSpeedMetersPerSecond(info.m_speed);
|
||||
m_turnNotificationsMgr.SetSpeedMetersPerSecond(info.m_speed);
|
||||
|
||||
if (m_route.MoveIterator(info))
|
||||
{
|
||||
|
@ -245,33 +241,17 @@ void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const
|
|||
formatDistFn(m_route.GetCurrentDistanceToEndMeters(), info.m_distToTarget, info.m_targetUnitsSuffix);
|
||||
|
||||
double distanceToTurnMeters = 0.;
|
||||
double distanceToNextTurnMeters = 0.;
|
||||
turns::TurnItem turn;
|
||||
turns::TurnItem nextTurn;
|
||||
m_route.GetCurrentTurn(distanceToTurnMeters, turn);
|
||||
formatDistFn(distanceToTurnMeters, info.m_distToTurn, info.m_turnUnitsSuffix);
|
||||
info.m_turn = turn.m_turn;
|
||||
|
||||
// The turn after the next one.
|
||||
if (m_route.GetNextTurn(distanceToNextTurnMeters, nextTurn))
|
||||
{
|
||||
double const distBetweenTurnsM = distanceToNextTurnMeters - distanceToTurnMeters;
|
||||
ASSERT_LESS_OR_EQUAL(0, distBetweenTurnsM, ());
|
||||
|
||||
if (m_routingSettings.m_showTurnAfterNext &&
|
||||
distanceToTurnMeters < kShowTheTurnAfterTheNextM && distBetweenTurnsM < turns::kMaxTurnDistM)
|
||||
{
|
||||
info.m_nextTurn = nextTurn.m_turn;
|
||||
}
|
||||
else
|
||||
{
|
||||
info.m_nextTurn = routing::turns::TurnDirection::NoTurn;
|
||||
}
|
||||
}
|
||||
if (m_routingSettings.m_showTurnAfterNext)
|
||||
info.m_nextTurn = m_turnNotificationsMgr.GetSecondTurnNotification();
|
||||
else
|
||||
{
|
||||
info.m_nextTurn = routing::turns::TurnDirection::NoTurn;
|
||||
}
|
||||
|
||||
info.m_exitNum = turn.m_exitNum;
|
||||
info.m_time = m_route.GetCurrentTimeToEndSec();
|
||||
info.m_sourceName = turn.m_sourceName;
|
||||
|
@ -309,7 +289,7 @@ void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const
|
|||
(distanceToTurnMeters < kShowPedestrianTurnInMeters) ? turn.m_pedestrianTurn : turns::PedestrianDirection::None;
|
||||
}
|
||||
|
||||
void RoutingSession::GenerateTurnSound(vector<string> & turnNotifications)
|
||||
void RoutingSession::GenerateTurnNotifications(vector<string> & turnNotifications)
|
||||
{
|
||||
turnNotifications.clear();
|
||||
|
||||
|
@ -324,7 +304,7 @@ void RoutingSession::GenerateTurnSound(vector<string> & turnNotifications)
|
|||
|
||||
vector<turns::TurnItemDist> turns;
|
||||
if (m_route.GetNextTurns(turns))
|
||||
m_turnsSound.GenerateTurnSound(turns, turnNotifications);
|
||||
m_turnNotificationsMgr.GenerateTurnNotifications(turns, turnNotifications);
|
||||
}
|
||||
|
||||
void RoutingSession::AssignRoute(Route & route, IRouter::ResultCode e)
|
||||
|
@ -393,21 +373,21 @@ void RoutingSession::EnableTurnNotifications(bool enable)
|
|||
{
|
||||
threads::MutexGuard guard(m_routeSessionMutex);
|
||||
UNUSED_VALUE(guard);
|
||||
m_turnsSound.Enable(enable);
|
||||
m_turnNotificationsMgr.Enable(enable);
|
||||
}
|
||||
|
||||
bool RoutingSession::AreTurnNotificationsEnabled() const
|
||||
{
|
||||
threads::MutexGuard guard(m_routeSessionMutex);
|
||||
UNUSED_VALUE(guard);
|
||||
return m_turnsSound.IsEnabled();
|
||||
return m_turnNotificationsMgr.IsEnabled();
|
||||
}
|
||||
|
||||
void RoutingSession::SetTurnNotificationsUnits(Settings::Units const units)
|
||||
{
|
||||
threads::MutexGuard guard(m_routeSessionMutex);
|
||||
UNUSED_VALUE(guard);
|
||||
m_turnsSound.SetLengthUnits(units);
|
||||
m_turnNotificationsMgr.SetLengthUnits(units);
|
||||
}
|
||||
|
||||
void RoutingSession::SetTurnNotificationsLocale(string const & locale)
|
||||
|
@ -415,14 +395,14 @@ void RoutingSession::SetTurnNotificationsLocale(string const & locale)
|
|||
LOG(LINFO, ("The language for turn notifications is", locale));
|
||||
threads::MutexGuard guard(m_routeSessionMutex);
|
||||
UNUSED_VALUE(guard);
|
||||
m_turnsSound.SetLocale(locale);
|
||||
m_turnNotificationsMgr.SetLocale(locale);
|
||||
}
|
||||
|
||||
string RoutingSession::GetTurnNotificationsLocale() const
|
||||
{
|
||||
threads::MutexGuard guard(m_routeSessionMutex);
|
||||
UNUSED_VALUE(guard);
|
||||
return m_turnsSound.GetLocale();
|
||||
return m_turnNotificationsMgr.GetLocale();
|
||||
}
|
||||
|
||||
double RoutingSession::GetDistanceToCurrentCamM(SpeedCameraRestriction & camera, Index const & index)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "routing/route.hpp"
|
||||
#include "routing/router.hpp"
|
||||
#include "routing/turns.hpp"
|
||||
#include "routing/turns_sound.hpp"
|
||||
#include "routing/turns_notification_manager.hpp"
|
||||
|
||||
#include "platform/location.hpp"
|
||||
|
||||
|
@ -106,7 +106,7 @@ public:
|
|||
void SetTurnNotificationsUnits(Settings::Units const units);
|
||||
void SetTurnNotificationsLocale(string const & locale);
|
||||
string GetTurnNotificationsLocale() const;
|
||||
void GenerateTurnSound(vector<string> & turnNotifications);
|
||||
void GenerateTurnNotifications(vector<string> & turnNotifications);
|
||||
|
||||
private:
|
||||
struct DoReadyCallback
|
||||
|
@ -154,7 +154,7 @@ private:
|
|||
m2::PointD m_lastGoodPosition;
|
||||
|
||||
// Sound turn notification parameters.
|
||||
turns::sound::TurnsSound m_turnsSound;
|
||||
turns::sound::NotificationManager m_turnNotificationsMgr;
|
||||
|
||||
RoutingSettings m_routingSettings;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "routing/turns_sound.hpp"
|
||||
#include "routing/turns_notification_manager.hpp"
|
||||
#include "routing/turns_sound_settings.hpp"
|
||||
|
||||
#include "platform/location.hpp"
|
||||
|
@ -9,8 +9,6 @@ namespace
|
|||
{
|
||||
// A error to compare two double after conversion feet to meters.
|
||||
double const kEps = 1.;
|
||||
// A error to compare two doubles which are almost equal.
|
||||
double const kSmallEps = .001;
|
||||
} // namespace
|
||||
|
||||
namespace routing
|
||||
|
@ -23,9 +21,10 @@ using namespace location;
|
|||
|
||||
UNIT_TEST(TurnNotificationSettingsMetersTest)
|
||||
{
|
||||
Settings const settings(20 /* notificationTimeSeconds */,
|
||||
200 /* minNotificationDistanceUnits */,
|
||||
700 /* maxNotificationDistanceUnits */,
|
||||
Settings const settings(20 /* notificationTimeSeconds */, 200 /* minNotificationDistanceUnits */,
|
||||
700 /* maxNotificationDistanceUnits */, 5 /* m_startBeforeSeconds */,
|
||||
25 /* m_minStartBeforeMeters */, 150 /* m_maxStartBeforeMeters */,
|
||||
170 /* m_minDistToSayNotificationMeters */,
|
||||
{100, 200, 300, 400, 500, 600, 700} /* soundedDistancesUnits */,
|
||||
::Settings::Metric /* lengthUnits */);
|
||||
|
||||
|
@ -38,18 +37,25 @@ UNIT_TEST(TurnNotificationSettingsMetersTest)
|
|||
TEST_EQUAL(settings.RoundByPresetSoundedDistancesUnits(300 /* distanceInUnits */), 300, ());
|
||||
TEST_EQUAL(settings.RoundByPresetSoundedDistancesUnits(0 /* distanceInUnits */), 100, ());
|
||||
|
||||
TEST(my::AlmostEqualAbs(settings.ComputeTurnDistance(0. /* distanceInUnits */), 200., kSmallEps), ());
|
||||
TEST(my::AlmostEqualAbs(settings.ComputeTurnDistance(10. /* distanceInUnits */), 200., kSmallEps), ());
|
||||
TEST(my::AlmostEqualAbs(settings.ComputeTurnDistance(20. /* distanceInUnits */), 400., kSmallEps), ());
|
||||
TEST(my::AlmostEqualAbs(settings.ComputeTurnDistance(35. /* distanceInUnits */), 700., kSmallEps), ());
|
||||
TEST(my::AlmostEqualAbs(settings.ComputeTurnDistance(200. /* distanceInUnits */), 700., kSmallEps), ());
|
||||
TEST_EQUAL(settings.ComputeTurnDistanceM(0. /* speedMetersPerSecond */), 200., ());
|
||||
TEST_EQUAL(settings.ComputeTurnDistanceM(10. /* speedMetersPerSecond */), 200., ());
|
||||
TEST_EQUAL(settings.ComputeTurnDistanceM(20. /* speedMetersPerSecond */), 400., ());
|
||||
TEST_EQUAL(settings.ComputeTurnDistanceM(35. /* speedMetersPerSecond */), 700., ());
|
||||
TEST_EQUAL(settings.ComputeTurnDistanceM(200. /* speedMetersPerSecond */), 700., ());
|
||||
|
||||
TEST_EQUAL(settings.ComputeDistToPronounceDistM(0. /* speedMetersPerSecond */), 25., ());
|
||||
TEST_EQUAL(settings.ComputeDistToPronounceDistM(10. /* speedMetersPerSecond */), 50., ());
|
||||
TEST_EQUAL(settings.ComputeDistToPronounceDistM(20. /* speedMetersPerSecond */), 100., ());
|
||||
TEST_EQUAL(settings.ComputeDistToPronounceDistM(35. /* speedMetersPerSecond */), 150., ());
|
||||
TEST_EQUAL(settings.ComputeDistToPronounceDistM(200. /* speedMetersPerSecond */), 150., ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TurnNotificationSettingsFeetTest)
|
||||
{
|
||||
Settings const settings(20 /* notificationTimeSeconds */,
|
||||
500 /* minNotificationDistanceUnits */,
|
||||
2000 /* maxNotificationDistanceUnits */,
|
||||
Settings const settings(20 /* notificationTimeSeconds */, 500 /* minNotificationDistanceUnits */,
|
||||
2000 /* maxNotificationDistanceUnits */, 5 /* m_startBeforeSeconds */,
|
||||
25 /* m_minStartBeforeMeters */, 150 /* m_maxStartBeforeMeters */,
|
||||
170 /* m_minDistToSayNotificationMeters */,
|
||||
{200, 400, 600, 800, 1000, 1500, 2000} /* soundedDistancesUnits */,
|
||||
::Settings::Foot /* lengthUnits */);
|
||||
|
||||
|
@ -61,62 +67,60 @@ UNIT_TEST(TurnNotificationSettingsFeetTest)
|
|||
TEST(my::AlmostEqualAbs(settings.ConvertUnitsToMeters(300. /* distanceInUnits */), 91., kEps), ());
|
||||
TEST_EQUAL(settings.RoundByPresetSoundedDistancesUnits(500 /* distanceInUnits */), 600, ());
|
||||
TEST_EQUAL(settings.RoundByPresetSoundedDistancesUnits(0 /* distanceInUnits */), 200, ());
|
||||
|
||||
TEST(my::AlmostEqualAbs(settings.ComputeTurnDistance(0. /* distanceInUnits */), 500., kSmallEps), ());
|
||||
TEST(my::AlmostEqualAbs(settings.ComputeTurnDistance(10. /* distanceInUnits */), 500., kSmallEps), ());
|
||||
TEST(my::AlmostEqualAbs(settings.ComputeTurnDistance(30. /* distanceInUnits */), 600., kSmallEps), ());
|
||||
TEST(my::AlmostEqualAbs(settings.ComputeTurnDistance(40. /* distanceInUnits */), 800., kSmallEps), ());
|
||||
TEST(my::AlmostEqualAbs(settings.ComputeTurnDistance(200. /* distanceInUnits */), 2000., kSmallEps), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TurnNotificationSettingsNotValidTest)
|
||||
{
|
||||
Settings settings1(20 /* notificationTimeSeconds */, 500 /* minNotificationDistanceUnits */,
|
||||
2000 /* maxNotificationDistanceUnits */,
|
||||
2000 /* maxNotificationDistanceUnits */, 5 /* m_startBeforeSeconds */,
|
||||
25 /* m_minStartBeforeMeters */, 150 /* m_maxStartBeforeMeters */,
|
||||
170 /* m_minDistToSayNotificationMeters */,
|
||||
{200, 400, 800, 600, 1000, 1500, 2000} /* soundedDistancesUnits */,
|
||||
::Settings::Foot /* lengthUnits */);
|
||||
TEST(!settings1.IsValid(), ());
|
||||
|
||||
Settings settings2(20 /* notificationTimeSeconds */, 5000 /* minNotificationDistanceUnits */,
|
||||
2000 /* maxNotificationDistanceUnits */,
|
||||
2000 /* maxNotificationDistanceUnits */, 5 /* m_startBeforeSeconds */,
|
||||
25 /* m_minStartBeforeMeters */, 150 /* m_maxStartBeforeMeters */,
|
||||
170 /* m_minDistToSayNotificationMeters */,
|
||||
{200, 400, 600, 800, 1000, 1500, 2000} /* soundedDistancesUnits */,
|
||||
::Settings::Metric /* lengthUnits */);
|
||||
TEST(!settings2.IsValid(), ());
|
||||
|
||||
Settings settings3;
|
||||
TEST(!settings3.IsValid(), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TurnsSoundMetersTest)
|
||||
{
|
||||
TurnsSound turnSound(5 /* startBeforeSeconds */, 10 /* minStartBeforeMeters */,
|
||||
100 /* maxStartBeforeMeters */, 100 /* minDistToSayNotificationMeters */);
|
||||
turnSound.Enable(true);
|
||||
turnSound.SetLengthUnits(::Settings::Metric);
|
||||
NotificationManager notificationManager(5 /* startBeforeSeconds */, 10 /* minStartBeforeMeters */,
|
||||
100 /* maxStartBeforeMeters */,
|
||||
100 /* minDistToSayNotificationMeters */);
|
||||
notificationManager.Enable(true);
|
||||
notificationManager.SetLengthUnits(::Settings::Metric);
|
||||
string const engShortJson =
|
||||
"\
|
||||
{\
|
||||
\"in_600_meters\":\"In 600 meters.\",\
|
||||
\"make_a_right_turn\":\"Make a right turn.\"\
|
||||
}";
|
||||
turnSound.m_getTtsText.ForTestingSetLocaleWithJson(engShortJson, "en");
|
||||
turnSound.m_settings.ForTestingSetNotificationTimeSecond(20);
|
||||
notificationManager.m_getTtsText.ForTestingSetLocaleWithJson(engShortJson, "en");
|
||||
notificationManager.m_settings.ForTestingSetNotificationTimeSecond(20);
|
||||
|
||||
turnSound.Reset();
|
||||
turnSound.SetSpeedMetersPerSecond(30.);
|
||||
notificationManager.Reset();
|
||||
notificationManager.SetSpeedMetersPerSecond(30.);
|
||||
|
||||
vector<TurnItemDist> turns = {{{5 /* idx */, TurnDirection::TurnRight}, 1000.}};
|
||||
vector<string> turnNotifications;
|
||||
|
||||
// Starting nearing the turnItem.
|
||||
// 1000 meters till the turn. No sound notifications is required.
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 700 meters till the turn. No sound notifications is required.
|
||||
turns.front().m_distMeters = 700.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 699 meters till the turn. It's time to pronounce the first voice notification.
|
||||
// Why? The current speed is 30 meters per seconds. According to correctSettingsMeters
|
||||
|
@ -125,49 +129,57 @@ UNIT_TEST(TurnsSoundMetersTest)
|
|||
// So we start playing the first notification when the distance till the turn is less
|
||||
// then 20 seconds * 30 meters per seconds + 100 meters = 700 meters.
|
||||
turns.front().m_distMeters = 699.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
vector<string> const expectedNotification1 = {{"In 600 meters. Make a right turn."}};
|
||||
TEST_EQUAL(turnNotifications, expectedNotification1, ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 650 meters till the turn. No sound notifications is required.
|
||||
turns.front().m_distMeters = 650.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
turnSound.SetSpeedMetersPerSecond(32.);
|
||||
notificationManager.SetSpeedMetersPerSecond(32.);
|
||||
|
||||
// 150 meters till the turn. No sound notifications is required.
|
||||
turns.front().m_distMeters = 150.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 100 meters till the turn. No sound notifications is required.
|
||||
turns.front().m_distMeters = 100.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 99 meters till the turn. It's time to pronounce the second voice notification.
|
||||
turns.front().m_distMeters = 99.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
vector<string> const expectedNotification2 = {{"Make a right turn."}};
|
||||
TEST_EQUAL(turnNotifications, expectedNotification2, ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 99 meters till the turn again. No sound notifications is required.
|
||||
turns.front().m_distMeters = 99.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 50 meters till the turn. No sound notifications is required.
|
||||
turns.front().m_distMeters = 50.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 0 meters till the turn. No sound notifications is required.
|
||||
turns.front().m_distMeters = 0.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
TEST(turnSound.IsEnabled(), ());
|
||||
TEST(notificationManager.IsEnabled(), ());
|
||||
}
|
||||
|
||||
// Test case:
|
||||
|
@ -176,10 +188,11 @@ UNIT_TEST(TurnsSoundMetersTest)
|
|||
// So the first notification of the second turn shall be skipped.
|
||||
UNIT_TEST(TurnsSoundMetersTwoTurnsTest)
|
||||
{
|
||||
TurnsSound turnSound(5 /* startBeforeSeconds */, 10 /* minStartBeforeMeters */,
|
||||
100 /* maxStartBeforeMeters */, 100 /* minDistToSayNotificationMeters */);
|
||||
turnSound.Enable(true);
|
||||
turnSound.SetLengthUnits(::Settings::Metric);
|
||||
NotificationManager notificationManager(5 /* startBeforeSeconds */, 10 /* minStartBeforeMeters */,
|
||||
100 /* maxStartBeforeMeters */,
|
||||
100 /* minDistToSayNotificationMeters */);
|
||||
notificationManager.Enable(true);
|
||||
notificationManager.SetLengthUnits(::Settings::Metric);
|
||||
string const engShortJson =
|
||||
"\
|
||||
{\
|
||||
|
@ -187,85 +200,87 @@ UNIT_TEST(TurnsSoundMetersTwoTurnsTest)
|
|||
\"make_a_sharp_right_turn\":\"Make a sharp right turn.\",\
|
||||
\"enter_the_roundabout\":\"Enter the roundabout.\"\
|
||||
}";
|
||||
turnSound.m_getTtsText.ForTestingSetLocaleWithJson(engShortJson, "en");
|
||||
turnSound.m_settings.ForTestingSetNotificationTimeSecond(20);
|
||||
notificationManager.m_getTtsText.ForTestingSetLocaleWithJson(engShortJson, "en");
|
||||
notificationManager.m_settings.ForTestingSetNotificationTimeSecond(20);
|
||||
|
||||
turnSound.Reset();
|
||||
turnSound.SetSpeedMetersPerSecond(35.);
|
||||
notificationManager.Reset();
|
||||
notificationManager.SetSpeedMetersPerSecond(35.);
|
||||
|
||||
vector<TurnItemDist> turns = {{{5 /* idx */, TurnDirection::TurnSharpRight}, 800.}};
|
||||
vector<string> turnNotifications;
|
||||
|
||||
// Starting nearing the first turn.
|
||||
// 800 meters till the turn. No sound notifications is required.
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
|
||||
// 700 meters till the turn. It's time to pronounce the first voice notification.
|
||||
// The speed is high.
|
||||
// The compensation of TurnsSound::m_startBeforeSeconds/TurnsSound::m_minStartBeforeMeters/
|
||||
// TurnsSound::m_maxStartBeforeMeters is not enough.
|
||||
// The compensation of
|
||||
// NotificationManager::m_startBeforeSeconds/NotificationManager::m_minStartBeforeMeters/
|
||||
// NotificationManager::m_maxStartBeforeMeters is not enough.
|
||||
// The user will be closer to the turn while pronouncing despite the compensation.
|
||||
// So it should be pronounced "In 600 meters."
|
||||
turns.front().m_distMeters = 700.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
vector<string> const expectedNotification1 = {{"In 600 meters. Make a sharp right turn."}};
|
||||
TEST_EQUAL(turnNotifications, expectedNotification1, ());
|
||||
|
||||
turnSound.SetSpeedMetersPerSecond(32.);
|
||||
notificationManager.SetSpeedMetersPerSecond(32.);
|
||||
|
||||
// 150 meters till the turn. No sound notifications is required.
|
||||
turns.front().m_distMeters = 150.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
|
||||
// 99 meters till the turn. It's time to pronounce the second voice notification.
|
||||
turns.front().m_distMeters = 99.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
vector<string> const expectedNotification2 = {{"Make a sharp right turn."}};
|
||||
TEST_EQUAL(turnNotifications, expectedNotification2, ());
|
||||
|
||||
turnSound.SetSpeedMetersPerSecond(10.);
|
||||
notificationManager.SetSpeedMetersPerSecond(10.);
|
||||
|
||||
// 0 meters till the turn. No sound notifications is required.
|
||||
turns.front().m_distMeters = 0.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
|
||||
vector<TurnItemDist> turns2 = {{{11 /* idx */, TurnDirection::EnterRoundAbout,
|
||||
2 /* exitNum */}, 60.}};
|
||||
|
||||
// Starting nearing the second turn.
|
||||
turnSound.GenerateTurnSound(turns2, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns2, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
|
||||
// 40 meters till the second turn. It's time to pronounce the second voice notification
|
||||
// without the first one.
|
||||
turns2.front().m_distMeters = 40.;
|
||||
turnSound.GenerateTurnSound(turns2, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns2, turnNotifications);
|
||||
vector<string> const expectedNotification3 = {{"Enter the roundabout."}};
|
||||
TEST_EQUAL(turnNotifications, expectedNotification3, ());
|
||||
|
||||
TEST(turnSound.IsEnabled(), ());
|
||||
TEST(notificationManager.IsEnabled(), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TurnsSoundFeetTest)
|
||||
{
|
||||
TurnsSound turnSound(5 /* startBeforeSeconds */, 10 /* minStartBeforeMeters */,
|
||||
100 /* maxStartBeforeMeters */, 100 /* minDistToSayNotificationMeters */);
|
||||
turnSound.Enable(true);
|
||||
turnSound.SetLengthUnits(::Settings::Foot);
|
||||
NotificationManager notificationManager(5 /* startBeforeSeconds */, 10 /* minStartBeforeMeters */,
|
||||
100 /* maxStartBeforeMeters */,
|
||||
100 /* minDistToSayNotificationMeters */);
|
||||
notificationManager.Enable(true);
|
||||
notificationManager.SetLengthUnits(::Settings::Foot);
|
||||
string const engShortJson =
|
||||
"\
|
||||
{\
|
||||
\"in_2000_feet\":\"In 2000 feet.\",\
|
||||
\"enter_the_roundabout\":\"Enter the roundabout.\"\
|
||||
}";
|
||||
turnSound.m_getTtsText.ForTestingSetLocaleWithJson(engShortJson, "en");
|
||||
turnSound.m_settings.ForTestingSetNotificationTimeSecond(20);
|
||||
notificationManager.m_getTtsText.ForTestingSetLocaleWithJson(engShortJson, "en");
|
||||
notificationManager.m_settings.ForTestingSetNotificationTimeSecond(20);
|
||||
|
||||
turnSound.Reset();
|
||||
turnSound.SetSpeedMetersPerSecond(30.);
|
||||
notificationManager.Reset();
|
||||
notificationManager.SetSpeedMetersPerSecond(30.);
|
||||
|
||||
vector<TurnItemDist> turns = {{{7 /* idx */, TurnDirection::EnterRoundAbout,
|
||||
3 /* exitNum */}, 1000.}};
|
||||
|
@ -273,12 +288,12 @@ UNIT_TEST(TurnsSoundFeetTest)
|
|||
|
||||
// Starting nearing the turnItem.
|
||||
// 1000 meters till the turn. No sound notifications is required.
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
|
||||
// 700 meters till the turn. No sound notifications is required.
|
||||
turns.front().m_distMeters = 700.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
|
||||
// 699 meters till the turn. It's time to pronounce the first voice notification.
|
||||
|
@ -288,55 +303,56 @@ UNIT_TEST(TurnsSoundFeetTest)
|
|||
// So we start playing the first notification when the distance till the turn is less
|
||||
// then 20 seconds * 30 meters per seconds + 100 meters = 700 meters.
|
||||
turns.front().m_distMeters = 699.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
vector<string> const expectedNotification1 = {{"In 2000 feet. Enter the roundabout."}};
|
||||
TEST_EQUAL(turnNotifications, expectedNotification1, ());
|
||||
|
||||
// 650 meters till the turn. No sound notifications is required.
|
||||
turns.front().m_distMeters = 650.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
|
||||
// 150 meters till the turn. No sound notifications is required.
|
||||
turns.front().m_distMeters = 150.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
|
||||
// 100 meters till the turn. No sound notifications is required.
|
||||
turns.front().m_distMeters = 100.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
|
||||
// 99 meters till the turn. It's time to pronounce the second voice notification.
|
||||
turns.front().m_distMeters = 99.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
vector<string> const expectedNotification2 = {{"Enter the roundabout."}};
|
||||
TEST_EQUAL(turnNotifications, expectedNotification2, ());
|
||||
|
||||
// 99 meters till the turn again. No sound notifications is required.
|
||||
turns.front().m_distMeters = 99.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
|
||||
// 50 meters till the turn. No sound notifications is required.
|
||||
turns.front().m_distMeters = 50.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
|
||||
// 0 meters till the turn. No sound notifications is required.
|
||||
turns.front().m_distMeters = 0.;
|
||||
turnSound.GenerateTurnSound(turns, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
|
||||
TEST(turnSound.IsEnabled(), ());
|
||||
TEST(notificationManager.IsEnabled(), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TurnsSoundComposedTurnTest)
|
||||
{
|
||||
TurnsSound turnSound(5 /* startBeforeSeconds */, 10 /* minStartBeforeMeters */,
|
||||
100 /* maxStartBeforeMeters */, 100 /* minDistToSayNotificationMeters */);
|
||||
turnSound.Enable(true);
|
||||
turnSound.SetLengthUnits(::Settings::Metric);
|
||||
NotificationManager notificationManager(5 /* startBeforeSeconds */, 10 /* minStartBeforeMeters */,
|
||||
100 /* maxStartBeforeMeters */,
|
||||
100 /* minDistToSayNotificationMeters */);
|
||||
notificationManager.Enable(true);
|
||||
notificationManager.SetLengthUnits(::Settings::Metric);
|
||||
string const engShortJson =
|
||||
"\
|
||||
{\
|
||||
|
@ -346,19 +362,20 @@ UNIT_TEST(TurnsSoundComposedTurnTest)
|
|||
\"then\":\"Then.\",\
|
||||
\"you_have_reached_the_destination\":\"You have reached the destination.\"\
|
||||
}";
|
||||
turnSound.m_getTtsText.ForTestingSetLocaleWithJson(engShortJson, "en");
|
||||
turnSound.m_settings.ForTestingSetNotificationTimeSecond(30);
|
||||
notificationManager.m_getTtsText.ForTestingSetLocaleWithJson(engShortJson, "en");
|
||||
notificationManager.m_settings.ForTestingSetNotificationTimeSecond(30);
|
||||
|
||||
turnSound.Reset();
|
||||
turnSound.SetSpeedMetersPerSecond(20.);
|
||||
notificationManager.Reset();
|
||||
notificationManager.SetSpeedMetersPerSecond(20.);
|
||||
vector<string> turnNotifications;
|
||||
|
||||
// Starting nearing the first turn.
|
||||
// 800 meters till the first turn.
|
||||
vector<TurnItemDist> const turns1 = {{{5 /* idx */, TurnDirection::TurnRight}, 800. /* m_distMeters */},
|
||||
{{10 /* idx */, TurnDirection::EnterRoundAbout}, 1000. /* m_distMeters */}};
|
||||
turnSound.GenerateTurnSound(turns1, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns1, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 620 meters till the first turn.
|
||||
turnNotifications.clear();
|
||||
|
@ -366,15 +383,17 @@ UNIT_TEST(TurnsSoundComposedTurnTest)
|
|||
{{10 /* idx */, TurnDirection::EnterRoundAbout}, 820. /* m_distMeters */}};
|
||||
vector<string> const expectedNotification2 = {{"In 600 meters. Turn right."},
|
||||
{"Then. Enter the roundabout."}};
|
||||
turnSound.GenerateTurnSound(turns2, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns2, turnNotifications);
|
||||
TEST_EQUAL(turnNotifications, expectedNotification2, ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::EnterRoundAbout, ());
|
||||
|
||||
// 300 meters till the first turn.
|
||||
turnNotifications.clear();
|
||||
vector<TurnItemDist> const turns3 = {{{5 /* idx */, TurnDirection::TurnRight}, 300. /* m_distMeters */},
|
||||
{{10 /* idx */, TurnDirection::EnterRoundAbout}, 500. /* m_distMeters */}};
|
||||
turnSound.GenerateTurnSound(turns3, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns3, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::EnterRoundAbout, ());
|
||||
|
||||
// 20 meters till the first turn.
|
||||
turnNotifications.clear();
|
||||
|
@ -382,31 +401,35 @@ UNIT_TEST(TurnsSoundComposedTurnTest)
|
|||
{{10 /* idx */, TurnDirection::EnterRoundAbout}, 220. /* m_distMeters */}};
|
||||
vector<string> const expectedNotification4 = {{"Turn right."},
|
||||
{"Then. Enter the roundabout."}};
|
||||
turnSound.GenerateTurnSound(turns4, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns4, turnNotifications);
|
||||
TEST_EQUAL(turnNotifications, expectedNotification4, ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::EnterRoundAbout, ());
|
||||
|
||||
// After the first turn.
|
||||
turnNotifications.clear();
|
||||
vector<TurnItemDist> const turns5 = {{{10 /* idx */, TurnDirection::EnterRoundAbout}, 180. /* m_distMeters */},
|
||||
{{15 /* idx */, TurnDirection::ReachedYourDestination}, 1180. /* m_distMeters */}};
|
||||
turnSound.GenerateTurnSound(turns5, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns5, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// Just before the second turn.
|
||||
turnNotifications.clear();
|
||||
vector<TurnItemDist> const turns6 = {{{10 /* idx */, TurnDirection::EnterRoundAbout}, 10. /* m_distMeters */},
|
||||
{{15 /* idx */, TurnDirection::ReachedYourDestination}, 1010. /* m_distMeters */}};
|
||||
vector<string> const expectedNotification6 = {{"Enter the roundabout."}};
|
||||
turnSound.GenerateTurnSound(turns6, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns6, turnNotifications);
|
||||
TEST_EQUAL(turnNotifications, expectedNotification6, ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(TurnsSoundRoundaboutTurnTest)
|
||||
{
|
||||
TurnsSound turnSound(5 /* startBeforeSeconds */, 10 /* minStartBeforeMeters */,
|
||||
100 /* maxStartBeforeMeters */, 100 /* minDistToSayNotificationMeters */);
|
||||
turnSound.Enable(true);
|
||||
turnSound.SetLengthUnits(::Settings::Metric);
|
||||
NotificationManager notificationManager(5 /* startBeforeSeconds */, 10 /* minStartBeforeMeters */,
|
||||
100 /* maxStartBeforeMeters */,
|
||||
100 /* minDistToSayNotificationMeters */);
|
||||
notificationManager.Enable(true);
|
||||
notificationManager.SetLengthUnits(::Settings::Metric);
|
||||
string const engShortJson =
|
||||
"\
|
||||
{\
|
||||
|
@ -418,11 +441,11 @@ UNIT_TEST(TurnsSoundRoundaboutTurnTest)
|
|||
\"in_600_meters\":\"In 600 meters.\",\
|
||||
\"then\":\"Then.\"\
|
||||
}";
|
||||
turnSound.m_getTtsText.ForTestingSetLocaleWithJson(engShortJson, "en");
|
||||
turnSound.m_settings.ForTestingSetNotificationTimeSecond(30);
|
||||
notificationManager.m_getTtsText.ForTestingSetLocaleWithJson(engShortJson, "en");
|
||||
notificationManager.m_settings.ForTestingSetNotificationTimeSecond(30);
|
||||
|
||||
turnSound.Reset();
|
||||
turnSound.SetSpeedMetersPerSecond(20.);
|
||||
notificationManager.Reset();
|
||||
notificationManager.SetSpeedMetersPerSecond(20.);
|
||||
vector<string> turnNotifications;
|
||||
|
||||
// Starting nearing the first turn.
|
||||
|
@ -431,8 +454,9 @@ UNIT_TEST(TurnsSoundRoundaboutTurnTest)
|
|||
1000. /* m_distMeters */},
|
||||
{{10 /* idx */, TurnDirection::LeaveRoundAbout, 2 /* m_exitNum */},
|
||||
2000. /* m_distMeters */}};
|
||||
turnSound.GenerateTurnSound(turns1, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns1, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 620 meters till the first turn.
|
||||
vector<TurnItemDist> const turns2 = {{{5 /* idx */, TurnDirection::EnterRoundAbout, 2 /* m_exitNum */},
|
||||
|
@ -441,8 +465,9 @@ UNIT_TEST(TurnsSoundRoundaboutTurnTest)
|
|||
1620. /* m_distMeters */}};
|
||||
vector<string> const expectedNotification2 = {{"In 600 meters. Enter the roundabout."},
|
||||
{"Then. Take the second exit."}};
|
||||
turnSound.GenerateTurnSound(turns2, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns2, turnNotifications);
|
||||
TEST_EQUAL(turnNotifications, expectedNotification2, ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 3 meters till the first turn.
|
||||
vector<TurnItemDist> const turns3 = {{{5 /* idx */, TurnDirection::EnterRoundAbout, 2 /* m_exitNum */},
|
||||
|
@ -451,24 +476,27 @@ UNIT_TEST(TurnsSoundRoundaboutTurnTest)
|
|||
1003. /* m_distMeters */}};
|
||||
vector<string> const expectedNotification3 = {{"Enter the roundabout."},
|
||||
{"Then. Take the second exit."}};
|
||||
turnSound.GenerateTurnSound(turns3, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns3, turnNotifications);
|
||||
TEST_EQUAL(turnNotifications, expectedNotification3, ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 900 meters till the second turn.
|
||||
vector<TurnItemDist> const turns4 = {{{10 /* idx */, TurnDirection::LeaveRoundAbout, 2 /* m_exitNum */},
|
||||
900. /* m_distMeters */},
|
||||
{{15 /* idx */, TurnDirection::EnterRoundAbout, 1 /* m_exitNum */},
|
||||
1900. /* m_distMeters */}};
|
||||
turnSound.GenerateTurnSound(turns4, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns4, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 300 meters till the second turn.
|
||||
vector<TurnItemDist> const turns5 = {{{10 /* idx */, TurnDirection::LeaveRoundAbout, 2 /* m_exitNum */},
|
||||
300. /* m_distMeters */},
|
||||
{{15 /* idx */, TurnDirection::EnterRoundAbout, 1 /* m_exitNum */},
|
||||
1300. /* m_distMeters */}};
|
||||
turnSound.GenerateTurnSound(turns5, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns5, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 3 meters till the second turn.
|
||||
vector<TurnItemDist> const turns6 = {{{10 /* idx */, TurnDirection::LeaveRoundAbout, 2 /* m_exitNum */},
|
||||
|
@ -476,8 +504,9 @@ UNIT_TEST(TurnsSoundRoundaboutTurnTest)
|
|||
{{15 /* idx */, TurnDirection::EnterRoundAbout, 1 /* m_exitNum */},
|
||||
1003. /* m_distMeters */}};
|
||||
vector<string> const expectedNotification6 = {{"Leave the roundabout."}};
|
||||
turnSound.GenerateTurnSound(turns6, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns6, turnNotifications);
|
||||
TEST_EQUAL(turnNotifications, expectedNotification6, ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 5 meters till the third turn.
|
||||
vector<TurnItemDist> const turns7 = {{{15 /* idx */, TurnDirection::EnterRoundAbout, 1 /* m_exitNum */},
|
||||
|
@ -486,18 +515,21 @@ UNIT_TEST(TurnsSoundRoundaboutTurnTest)
|
|||
1005. /* m_distMeters */}};
|
||||
vector<string> const expectedNotification7 = {{"Enter the roundabout."},
|
||||
{"Then. Take the first exit."}};
|
||||
turnSound.GenerateTurnSound(turns7, turnNotifications); // The first notification fast forwarding.
|
||||
turnSound.GenerateTurnSound(turns7, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(
|
||||
turns7, turnNotifications); // The first notification fast forwarding.
|
||||
notificationManager.GenerateTurnNotifications(turns7, turnNotifications);
|
||||
TEST_EQUAL(turnNotifications, expectedNotification7, ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 900 meters till the 4th turn.
|
||||
turnSound.Reset();
|
||||
notificationManager.Reset();
|
||||
vector<TurnItemDist> const turns8 = {{{25 /* idx */, TurnDirection::EnterRoundAbout, 4 /* m_exitNum */},
|
||||
900. /* m_distMeters */},
|
||||
{{30 /* idx */, TurnDirection::LeaveRoundAbout, 4 /* m_exitNum */},
|
||||
1200. /* m_distMeters */}};
|
||||
turnSound.GenerateTurnSound(turns8, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns8, turnNotifications);
|
||||
TEST(turnNotifications.empty(), ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::NoTurn, ());
|
||||
|
||||
// 620 meters till the 4th turn.
|
||||
vector<TurnItemDist> const turns9 = {{{25 /* idx */, TurnDirection::EnterRoundAbout, 4 /* m_exitNum */},
|
||||
|
@ -506,8 +538,9 @@ UNIT_TEST(TurnsSoundRoundaboutTurnTest)
|
|||
920. /* m_distMeters */}};
|
||||
vector<string> const expectedNotification9 = {{"In 600 meters. Enter the roundabout."},
|
||||
{"Then. Take the fourth exit."}};
|
||||
turnSound.GenerateTurnSound(turns9, turnNotifications);
|
||||
notificationManager.GenerateTurnNotifications(turns9, turnNotifications);
|
||||
TEST_EQUAL(turnNotifications, expectedNotification9, ());
|
||||
TEST_EQUAL(notificationManager.GetSecondTurnNotification(), TurnDirection::LeaveRoundAbout, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(GetJsonBufferTest)
|
||||
|
|
|
@ -16,11 +16,6 @@ namespace turns
|
|||
/// @todo(vbykoianko) It's a good idea to gather all the turns information into one entity.
|
||||
/// For the time being several separate entities reflect the turn information. Like Route::TTurns
|
||||
|
||||
// If the distance between two sequential turns is more than kMaxTurnDistM
|
||||
// the information about the second turn will be shown or pronounced when the user is
|
||||
// approaching to the first one.
|
||||
double constexpr kMaxTurnDistM = 400.;
|
||||
|
||||
/*!
|
||||
* \warning The order of values below shall not be changed.
|
||||
* TurnRight(TurnLeft) must have a minimal value and
|
||||
|
|
250
routing/turns_notification_manager.cpp
Normal file
250
routing/turns_notification_manager.cpp
Normal file
|
@ -0,0 +1,250 @@
|
|||
#include "routing/turns_notification_manager.hpp"
|
||||
|
||||
#include "platform/location.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
// If the distance between two sequential turns is less than kMaxTurnDistM
|
||||
// the information about the second turn will be shown or pronounced when the user is
|
||||
// approaching to the first one.
|
||||
double constexpr kMaxTurnDistM = 400.;
|
||||
|
||||
// Returns true if the closest turn is an entrance to a roundabout and the second is
|
||||
// an exit form a roundabout.
|
||||
// Note. There are some cases when another turn (besides an exit from roundabout)
|
||||
// follows an entrance to a roundabout. It could happend in case of turns inside a roundabout.
|
||||
// Returns false otherwise.
|
||||
bool IsClassicEntranceToRoundabout(routing::turns::TurnItemDist const & firstTurn,
|
||||
routing::turns::TurnItemDist const & secondTurn)
|
||||
{
|
||||
return firstTurn.m_turnItem.m_turn == routing::turns::TurnDirection::EnterRoundAbout &&
|
||||
secondTurn.m_turnItem.m_turn == routing::turns::TurnDirection::LeaveRoundAbout;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace routing
|
||||
{
|
||||
namespace turns
|
||||
{
|
||||
namespace sound
|
||||
{
|
||||
string NotificationManager::GenerateTurnText(uint32_t distanceUnits, uint8_t exitNum,
|
||||
bool useThenInsteadOfDistance, TurnDirection turnDir,
|
||||
::Settings::Units lengthUnits) const
|
||||
{
|
||||
Notification const notification(distanceUnits, exitNum, useThenInsteadOfDistance, turnDir,
|
||||
lengthUnits);
|
||||
return m_getTtsText(notification);
|
||||
}
|
||||
|
||||
void NotificationManager::GenerateTurnNotifications(vector<TurnItemDist> const & turns,
|
||||
vector<string> & turnNotifications)
|
||||
{
|
||||
m_secondTurnNotification = GenerateSecondTurnNotification(turns);
|
||||
|
||||
turnNotifications.clear();
|
||||
if (!m_enabled || turns.empty())
|
||||
return;
|
||||
|
||||
TurnItemDist const & firstTurn = turns.front();
|
||||
string firstNotification = GenerateFirstTurnSound(firstTurn.m_turnItem, firstTurn.m_distMeters);
|
||||
if (m_nextTurnNotificationProgress == PronouncedNotification::Nothing)
|
||||
return;
|
||||
if (firstNotification.empty())
|
||||
return;
|
||||
turnNotifications.emplace_back(move(firstNotification));
|
||||
|
||||
// Generating notifications like "Then turn left" if necessary.
|
||||
if (turns.size() < 2)
|
||||
return;
|
||||
TurnItemDist const & secondTurn = turns[1];
|
||||
ASSERT_LESS_OR_EQUAL(firstTurn.m_distMeters, secondTurn.m_distMeters, ());
|
||||
if (secondTurn.m_distMeters - firstTurn.m_distMeters > kMaxTurnDistM &&
|
||||
!IsClassicEntranceToRoundabout(firstTurn, secondTurn))
|
||||
{
|
||||
return;
|
||||
}
|
||||
string secondNotification = GenerateTurnText(
|
||||
0 /* distanceUnits is not used because of "Then" is used */, secondTurn.m_turnItem.m_exitNum,
|
||||
true, secondTurn.m_turnItem.m_turn, m_settings.GetLengthUnits());
|
||||
if (secondNotification.empty())
|
||||
return;
|
||||
turnNotifications.emplace_back(move(secondNotification));
|
||||
// Turn notification with word "Then" (about the second turn) will be pronounced.
|
||||
// When this second turn become the first one the first notification about the turn
|
||||
// shall be skipped.
|
||||
m_turnNotificationWithThen = true;
|
||||
}
|
||||
|
||||
string NotificationManager::GenerateFirstTurnSound(TurnItem const & turn,
|
||||
double distanceToTurnMeters)
|
||||
{
|
||||
if (m_nextTurnIndex != turn.m_index)
|
||||
{
|
||||
m_nextTurnNotificationProgress = PronouncedNotification::Nothing;
|
||||
m_nextTurnIndex = turn.m_index;
|
||||
}
|
||||
|
||||
uint32_t const distanceToPronounceNotificationM =
|
||||
m_settings.ComputeDistToPronounceDistM(m_speedMetersPerSecond);
|
||||
if (m_nextTurnNotificationProgress == PronouncedNotification::Nothing)
|
||||
{
|
||||
if (!m_settings.TooCloseForFisrtNotification(distanceToTurnMeters))
|
||||
{
|
||||
uint32_t const startPronounceDistMeters =
|
||||
m_settings.ComputeTurnDistanceM(m_speedMetersPerSecond) +
|
||||
distanceToPronounceNotificationM;
|
||||
if (distanceToTurnMeters < startPronounceDistMeters)
|
||||
{
|
||||
if (m_turnNotificationWithThen)
|
||||
{
|
||||
FastForwardFirstTurnNotification();
|
||||
}
|
||||
else
|
||||
{
|
||||
double const distToPronounceMeters = distanceToTurnMeters - distanceToPronounceNotificationM;
|
||||
if (distToPronounceMeters < 0)
|
||||
{
|
||||
FastForwardFirstTurnNotification();
|
||||
return string(); // The current position is too close to the turn for the first
|
||||
// notification.
|
||||
}
|
||||
|
||||
// Pronouncing first turn sound notification.
|
||||
double const distToPronounceUnits =
|
||||
m_settings.ConvertMetersToUnits(distToPronounceMeters);
|
||||
uint32_t const roundedDistToPronounceUnits =
|
||||
m_settings.RoundByPresetSoundedDistancesUnits(distToPronounceUnits);
|
||||
m_nextTurnNotificationProgress = PronouncedNotification::First;
|
||||
return GenerateTurnText(roundedDistToPronounceUnits, turn.m_exitNum,
|
||||
false /* useThenInsteadOfDistance */, turn.m_turn,
|
||||
m_settings.GetLengthUnits());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The first notification has not been pronounced but the distance to the turn is too short.
|
||||
// It happens if one turn follows shortly behind another one.
|
||||
m_nextTurnNotificationProgress = PronouncedNotification::First;
|
||||
FastForwardFirstTurnNotification();
|
||||
}
|
||||
return string();
|
||||
}
|
||||
|
||||
if (m_nextTurnNotificationProgress == PronouncedNotification::First &&
|
||||
distanceToTurnMeters < distanceToPronounceNotificationM)
|
||||
{
|
||||
m_nextTurnNotificationProgress = PronouncedNotification::Second;
|
||||
FastForwardFirstTurnNotification();
|
||||
return GenerateTurnText(0 /* distanceUnits */, turn.m_exitNum,
|
||||
false /* useThenInsteadOfDistance */, turn.m_turn,
|
||||
m_settings.GetLengthUnits());
|
||||
}
|
||||
return string();
|
||||
}
|
||||
|
||||
void NotificationManager::Enable(bool enable)
|
||||
{
|
||||
if (enable && !m_enabled)
|
||||
Reset();
|
||||
m_enabled = enable;
|
||||
}
|
||||
|
||||
void NotificationManager::SetLengthUnits(::Settings::Units units)
|
||||
{
|
||||
m_settings.SetLengthUnits(units);
|
||||
switch (units)
|
||||
{
|
||||
case ::Settings::Metric:
|
||||
m_settings.SetState(30 /* notificationTimeSeconds */, 200 /* minNotificationDistanceUnits */,
|
||||
2000 /* maxNotificationDistanceUnits */,
|
||||
GetSoundedDistMeters() /* soundedDistancesUnits */,
|
||||
::Settings::Metric /* lengthUnits */);
|
||||
return;
|
||||
case ::Settings::Foot:
|
||||
m_settings.SetState(30 /* notificationTimeSeconds */, 500 /* minNotificationDistanceUnits */,
|
||||
5000 /* maxNotificationDistanceUnits */,
|
||||
GetSoundedDistFeet() /* soundedDistancesUnits */,
|
||||
::Settings::Foot /* lengthUnits */);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::SetSpeedMetersPerSecond(double speed)
|
||||
{
|
||||
// When the quality of GPS data is bad the current speed may be less then zero.
|
||||
// It's easy to reproduce at an office with Nexus 5.
|
||||
// In that case zero meters per second is used.
|
||||
m_speedMetersPerSecond = max(0., speed);
|
||||
}
|
||||
|
||||
void NotificationManager::Reset()
|
||||
{
|
||||
m_nextTurnNotificationProgress = PronouncedNotification::Nothing;
|
||||
m_nextTurnIndex = 0;
|
||||
m_turnNotificationWithThen = false;
|
||||
m_secondTurnNotification = TurnDirection::NoTurn;
|
||||
m_secondTurnNotificationIndex = 0;
|
||||
}
|
||||
|
||||
void NotificationManager::FastForwardFirstTurnNotification()
|
||||
{
|
||||
m_turnNotificationWithThen = false;
|
||||
if (m_nextTurnNotificationProgress == PronouncedNotification::Nothing)
|
||||
m_nextTurnNotificationProgress = PronouncedNotification::First;
|
||||
}
|
||||
|
||||
TurnDirection NotificationManager::GenerateSecondTurnNotification(vector<TurnItemDist> const & turns)
|
||||
{
|
||||
if (turns.size() < 2)
|
||||
{
|
||||
m_secondTurnNotificationIndex = 0;
|
||||
return TurnDirection::NoTurn;
|
||||
}
|
||||
|
||||
TurnItemDist const & firstTurn = turns[0];
|
||||
TurnItemDist const & secondTurn = turns[1];
|
||||
|
||||
if (firstTurn.m_turnItem.m_index != m_secondTurnNotificationIndex)
|
||||
m_secondTurnNotificationIndex = 0; // It's a new closest(fisrt) turn.
|
||||
else if (m_secondTurnNotificationIndex != 0)
|
||||
return secondTurn.m_turnItem.m_turn; // m_secondTurnNotificationIndex was set to true before.
|
||||
|
||||
double const distBetweenTurnsMeters = secondTurn.m_distMeters - firstTurn.m_distMeters;
|
||||
ASSERT_LESS_OR_EQUAL(0., distBetweenTurnsMeters, ());
|
||||
|
||||
if (distBetweenTurnsMeters > kMaxTurnDistM)
|
||||
return TurnDirection::NoTurn;
|
||||
|
||||
uint32_t const startPronounceDistMeters =
|
||||
m_settings.ComputeTurnDistanceM(m_speedMetersPerSecond) +
|
||||
m_settings.ComputeDistToPronounceDistM(m_speedMetersPerSecond);
|
||||
if (firstTurn.m_distMeters <= startPronounceDistMeters)
|
||||
{
|
||||
m_secondTurnNotificationIndex = firstTurn.m_turnItem.m_index;
|
||||
return secondTurn.m_turnItem.m_turn; // It's time to inform about the turn after the next one.
|
||||
}
|
||||
return TurnDirection::NoTurn;
|
||||
}
|
||||
|
||||
string DebugPrint(PronouncedNotification const notificationProgress)
|
||||
{
|
||||
switch (notificationProgress)
|
||||
{
|
||||
case PronouncedNotification::Nothing:
|
||||
return "Nothing";
|
||||
case PronouncedNotification::First:
|
||||
return "First";
|
||||
case PronouncedNotification::Second:
|
||||
return "Second";
|
||||
}
|
||||
|
||||
ASSERT(false, ());
|
||||
stringstream out;
|
||||
out << "unknown PronouncedNotification (" << static_cast<int>(notificationProgress) << ")";
|
||||
return out.str();
|
||||
}
|
||||
} // namespace sound
|
||||
} // namespace turns
|
||||
} // namespace routing
|
174
routing/turns_notification_manager.hpp
Normal file
174
routing/turns_notification_manager.hpp
Normal file
|
@ -0,0 +1,174 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/turns_sound_settings.hpp"
|
||||
#include "routing/turns_tts_text.hpp"
|
||||
|
||||
#include "platform/settings.hpp"
|
||||
|
||||
#include "std/string.hpp"
|
||||
|
||||
namespace location
|
||||
{
|
||||
class FollowingInfo;
|
||||
}
|
||||
|
||||
namespace routing
|
||||
{
|
||||
namespace turns
|
||||
{
|
||||
namespace sound
|
||||
{
|
||||
/// \brief The PronouncedNotification enum represents which sound notifications
|
||||
/// for turns were heard.
|
||||
enum class PronouncedNotification
|
||||
{
|
||||
Nothing,
|
||||
First,
|
||||
Second /** The second notification just before the turn was pronounced. */
|
||||
};
|
||||
|
||||
string DebugPrint(PronouncedNotification const notificationProgress);
|
||||
|
||||
/// \brief The TurnsSound class is responsible for all route turn sound notifications functionality.
|
||||
/// To be able to generate turn sound notification the class needs to have correct Settings
|
||||
/// and relevant speed.
|
||||
class NotificationManager
|
||||
{
|
||||
friend void UnitTest_TurnsSoundMetersTest();
|
||||
friend void UnitTest_TurnsSoundMetersTwoTurnsTest();
|
||||
friend void UnitTest_TurnsSoundFeetTest();
|
||||
friend void UnitTest_TurnsSoundComposedTurnTest();
|
||||
friend void UnitTest_TurnsSoundRoundaboutTurnTest();
|
||||
|
||||
/// \brief The private contructor is used only for testing.
|
||||
NotificationManager(uint32_t startBeforeSeconds, uint32_t minStartBeforeMeters,
|
||||
uint32_t maxStartBeforeMeters, uint32_t minDistToSayNotificationMeters)
|
||||
: m_enabled(false)
|
||||
, m_speedMetersPerSecond(0.)
|
||||
, m_settings(startBeforeSeconds, minStartBeforeMeters, maxStartBeforeMeters,
|
||||
minDistToSayNotificationMeters)
|
||||
, m_nextTurnNotificationProgress(PronouncedNotification::Nothing)
|
||||
, m_turnNotificationWithThen(false)
|
||||
, m_nextTurnIndex(0)
|
||||
, m_secondTurnNotification(TurnDirection::NoTurn)
|
||||
{
|
||||
}
|
||||
|
||||
/// m_enabled == true when tts is turned on.
|
||||
/// Important! Clients (iOS/Android) implies that m_enabled is false by default.
|
||||
bool m_enabled;
|
||||
/// In m_speedMetersPerSecond is intended for some speed which will be used for
|
||||
/// convertion a distance in seconds to distance in meters. It could be a current
|
||||
/// an end user speed or an average speed for last several seconds.
|
||||
/// @TODO(vbykoianko) It's better to use an average speed
|
||||
/// for last several seconds instead of the current speed here.
|
||||
double m_speedMetersPerSecond;
|
||||
|
||||
Settings m_settings;
|
||||
/// m_nextTurnNotificationProgress keeps a status which is being changing while
|
||||
/// an end user is coming to the closest (the next) turn along the route.
|
||||
/// When an end user is far from the next turn
|
||||
/// m_nextTurnNotificationProgress == Nothing.
|
||||
/// After the first turn notification has been pronounced
|
||||
/// m_nextTurnNotificationProgress == First.
|
||||
/// After the second notification has been pronounced
|
||||
/// m_nextTurnNotificationProgress == Second.
|
||||
PronouncedNotification m_nextTurnNotificationProgress;
|
||||
/// The flag is set to true if notification about the second turn was pronounced.
|
||||
/// It could happen in expressions like "Turn right. Then turn left."
|
||||
/// This flag is used to pass the information if second turn notification was pronounced
|
||||
/// between two calls of GenerateTurnNotifications() method.
|
||||
bool m_turnNotificationWithThen;
|
||||
|
||||
uint32_t m_nextTurnIndex;
|
||||
/// getTtsText is a convector form turn notification information and locale to
|
||||
/// notification string.
|
||||
GetTtsText m_getTtsText;
|
||||
/// if m_secondTurnNotification == true it's time to display the second turn notification
|
||||
/// visual informer, and false otherwise.
|
||||
/// m_secondTurnNotification is a direction of the turn after the closest one
|
||||
/// if an end user shall be informed about it. If not, m_secondTurnNotification ==
|
||||
/// TurnDirection::NoTurn
|
||||
TurnDirection m_secondTurnNotification;
|
||||
/// m_secondTurnNotificationIndex is an index of the closest turn on the route polyline
|
||||
/// where m_secondTurnNotification was set to true last time for a turn.
|
||||
/// If the closest turn is changed m_secondTurnNotification is set to 0.
|
||||
/// \note 0 is a valid index. But in this context it could be considered as invalid
|
||||
/// because if firstTurnIndex == 0 that means we're at very beginning of the route
|
||||
/// and we're about to making a turn. In that case it's no use to inform a user about
|
||||
/// the turn after the next one.
|
||||
uint32_t m_secondTurnNotificationIndex;
|
||||
|
||||
string GenerateTurnText(uint32_t distanceUnits, uint8_t exitNum, bool useThenInsteadOfDistance,
|
||||
TurnDirection turnDir, ::Settings::Units lengthUnits) const;
|
||||
/// Generates turn sound notification for the nearest to the current position turn.
|
||||
string GenerateFirstTurnSound(TurnItem const & turn, double distanceToTurnMeters);
|
||||
/// Changes the state of the class to emulate that first turn notification is pronouned
|
||||
/// without pronunciation.
|
||||
void FastForwardFirstTurnNotification();
|
||||
/// \param turns contains information about the next turns staring from closest turn.
|
||||
/// That means turns[0] is the closest turn (if available).
|
||||
/// @return the second closest turn or TurnDirection::NoTurn.
|
||||
/// \note If GenerateSecondTurnNotification returns a value different form TurnDirection::NoTurn
|
||||
/// for a turn once it will return the same value until the turn is changed.
|
||||
/// \note This method works independent from m_enabled value.
|
||||
/// So it works when the class enable and disable.
|
||||
TurnDirection GenerateSecondTurnNotification(vector<TurnItemDist> const & turns);
|
||||
|
||||
public:
|
||||
NotificationManager()
|
||||
: m_enabled(false)
|
||||
, m_speedMetersPerSecond(0.)
|
||||
, m_settings(5 /* m_startBeforeSeconds */, 25 /* m_minStartBeforeMeters */,
|
||||
150 /* m_maxStartBeforeMeters */, 170 /* m_minDistToSayNotificationMeters */)
|
||||
, m_nextTurnNotificationProgress(PronouncedNotification::Nothing)
|
||||
, m_turnNotificationWithThen(false)
|
||||
, m_nextTurnIndex(0)
|
||||
, m_secondTurnNotification(TurnDirection::NoTurn)
|
||||
{
|
||||
}
|
||||
|
||||
bool IsEnabled() const { return m_enabled; }
|
||||
void Enable(bool enable);
|
||||
void SetLengthUnits(::Settings::Units units);
|
||||
inline ::Settings::Units GetLengthUnits() const { return m_settings.GetLengthUnits(); }
|
||||
inline void SetLocale(string const & locale) { m_getTtsText.SetLocale(locale); }
|
||||
inline string GetLocale() const { return m_getTtsText.GetLocale(); }
|
||||
void SetSpeedMetersPerSecond(double speed);
|
||||
|
||||
/// \brief GenerateTurnNotifications updates information about the next turn notification.
|
||||
/// It also fills turnNotifications when it's necessary.
|
||||
/// If this TurnsSound wants to play a sound message once it should push one item to
|
||||
/// the vector turnNotifications once when GenerateTurnNotifications is called.
|
||||
/// \param turns contains information about the next turns starting from the closest one.
|
||||
/// \param distanceToTurnMeters is distance to the next turn in meters.
|
||||
/// \param turnNotifications is a parameter to fill it if it's necessary.
|
||||
/// \note The client implies turnNotifications does not contain empty strings.
|
||||
void GenerateTurnNotifications(vector<TurnItemDist> const & turns,
|
||||
vector<string> & turnNotifications);
|
||||
/// Reset states which reflects current route position.
|
||||
/// The method shall be called after creating a new route or after rerouting.
|
||||
void Reset();
|
||||
/// \brief The method was implemented to display the second turn notification
|
||||
/// in an appropriate moment.
|
||||
/// @return the second closest turn.
|
||||
/// The return value is different form TurnDirection::NoTurn
|
||||
/// if an end user is close enough to the first (current) turn and
|
||||
/// the distance between the closest and the second closest turn is not large.
|
||||
/// (That means a notification about turn after the closest one was pronounced.)
|
||||
/// For example, if while closing to the closest turn was pronounced
|
||||
/// "Turn right. Then turn left." 500 meters before the closest turn, after that moment
|
||||
/// GetSecondTurnNotification returns TurnDirection::TurnLeft if distance to first turn < 500
|
||||
/// meters.
|
||||
/// After the closest composed turn was passed GetSecondTurnNotification returns
|
||||
/// TurnDirection::NoTurn.
|
||||
/// \note If the returning value is TurnDirection::NoTurn no turn shall be displayed.
|
||||
/// \note If GetSecondTurnNotification returns a value different form TurnDirection::NoTurn
|
||||
/// for a turn once it continues returning the same value until the turn is changed.
|
||||
/// \note This method works independent from m_enabled value.
|
||||
/// So it works when the class enable and disable.
|
||||
TurnDirection GetSecondTurnNotification() const { return m_secondTurnNotification; }
|
||||
};
|
||||
} // namespace sound
|
||||
} // namespace turns
|
||||
} // namespace routing
|
|
@ -1,218 +0,0 @@
|
|||
#include "routing/turns_sound.hpp"
|
||||
|
||||
#include "platform/location.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
uint32_t CalculateDistBeforeMeters(double speedMetersPerSecond, uint32_t startBeforeSeconds,
|
||||
uint32_t minStartBeforeMeters, uint32_t maxStartBeforeMeters)
|
||||
{
|
||||
ASSERT_LESS_OR_EQUAL(0, speedMetersPerSecond, ());
|
||||
uint32_t const startBeforeMeters =
|
||||
static_cast<uint32_t>(speedMetersPerSecond * startBeforeSeconds);
|
||||
return my::clamp(startBeforeMeters, minStartBeforeMeters, maxStartBeforeMeters);
|
||||
}
|
||||
|
||||
// Returns true if the closest turn is an entrance to a roundabout and the second is
|
||||
// an exit form a roundabout.
|
||||
// Note. There are some cases when another turn (besides an exit from roundabout)
|
||||
// follows an entrance to a roundabout. It could happend in case of turns inside a roundabout.
|
||||
// Returns false otherwise.
|
||||
bool IsClassicEntranceToRoundabout(routing::turns::TurnItemDist const & firstTurn,
|
||||
routing::turns::TurnItemDist const & secondTurn)
|
||||
{
|
||||
return firstTurn.m_turnItem.m_turn == routing::turns::TurnDirection::EnterRoundAbout
|
||||
&& secondTurn.m_turnItem.m_turn == routing::turns::TurnDirection::LeaveRoundAbout;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace routing
|
||||
{
|
||||
namespace turns
|
||||
{
|
||||
namespace sound
|
||||
{
|
||||
string TurnsSound::GenerateTurnText(uint32_t distanceUnits, uint8_t exitNum, bool useThenInsteadOfDistance,
|
||||
TurnDirection turnDir, ::Settings::Units lengthUnits) const
|
||||
{
|
||||
Notification const notification(distanceUnits, exitNum, useThenInsteadOfDistance, turnDir, lengthUnits);
|
||||
return m_getTtsText(notification);
|
||||
}
|
||||
|
||||
void TurnsSound::GenerateTurnSound(vector<TurnItemDist> const & turns, vector<string> & turnNotifications)
|
||||
{
|
||||
turnNotifications.clear();
|
||||
|
||||
if (!m_enabled || turns.empty())
|
||||
return;
|
||||
|
||||
TurnItemDist const & firstTurn = turns.front();
|
||||
string firstNotification = GenerateFirstTurnSound(firstTurn.m_turnItem, firstTurn.m_distMeters);
|
||||
if (m_nextTurnNotificationProgress == PronouncedNotification::Nothing)
|
||||
return;
|
||||
if (firstNotification.empty())
|
||||
return;
|
||||
turnNotifications.emplace_back(move(firstNotification));
|
||||
|
||||
// Generating notifications like "Then turn left" if necessary.
|
||||
if (turns.size() < 2)
|
||||
return;
|
||||
TurnItemDist const & secondTurn = turns[1];
|
||||
ASSERT_LESS_OR_EQUAL(firstTurn.m_distMeters, secondTurn.m_distMeters, ());
|
||||
if (secondTurn.m_distMeters - firstTurn.m_distMeters > kMaxTurnDistM
|
||||
&& !IsClassicEntranceToRoundabout(firstTurn, secondTurn))
|
||||
{
|
||||
return;
|
||||
}
|
||||
string secondNotification = GenerateTurnText(0 /* distanceUnits is not used because of "Then" is used */,
|
||||
secondTurn.m_turnItem.m_exitNum, true,
|
||||
secondTurn.m_turnItem.m_turn,
|
||||
m_settings.GetLengthUnits());
|
||||
if (secondNotification.empty())
|
||||
return;
|
||||
turnNotifications.emplace_back(move(secondNotification));
|
||||
// Turn notification with word "Then" (about the second turn) will be pronounced.
|
||||
// When this second turn become the first one the first notification about the turn
|
||||
// shall be skipped.
|
||||
m_turnNotificationWithThen = true;
|
||||
}
|
||||
|
||||
string TurnsSound::GenerateFirstTurnSound(TurnItem const & turn, double distanceToTurnMeters)
|
||||
{
|
||||
if (m_nextTurnIndex != turn.m_index)
|
||||
{
|
||||
m_nextTurnNotificationProgress = PronouncedNotification::Nothing;
|
||||
m_nextTurnIndex = turn.m_index;
|
||||
}
|
||||
|
||||
uint32_t const distanceToPronounceNotificationMeters =
|
||||
CalculateDistBeforeMeters(m_speedMetersPerSecond, m_startBeforeSeconds,
|
||||
m_minStartBeforeMeters, m_maxStartBeforeMeters);
|
||||
|
||||
if (m_nextTurnNotificationProgress == PronouncedNotification::Nothing)
|
||||
{
|
||||
if (distanceToTurnMeters > m_minDistToSayNotificationMeters)
|
||||
{
|
||||
double const currentSpeedUntisPerSecond =
|
||||
m_settings.ConvertMetersPerSecondToUnitsPerSecond(m_speedMetersPerSecond);
|
||||
double const turnNotificationDistUnits =
|
||||
m_settings.ComputeTurnDistance(currentSpeedUntisPerSecond);
|
||||
uint32_t const startPronounceDistMeters =
|
||||
m_settings.ConvertUnitsToMeters(turnNotificationDistUnits) + distanceToPronounceNotificationMeters;
|
||||
|
||||
if (distanceToTurnMeters < startPronounceDistMeters)
|
||||
{
|
||||
if (m_turnNotificationWithThen)
|
||||
{
|
||||
FastForwardFirstTurnNotification();
|
||||
}
|
||||
else
|
||||
{
|
||||
double const distToPronounceMeters = distanceToTurnMeters - distanceToPronounceNotificationMeters;
|
||||
if (distToPronounceMeters < 0)
|
||||
{
|
||||
FastForwardFirstTurnNotification();
|
||||
return string(); // The current position is too close to the turn for the first notification.
|
||||
}
|
||||
|
||||
// Pronouncing first turn sound notification.
|
||||
double const distToPronounceUnits = m_settings.ConvertMetersToUnits(distToPronounceMeters);
|
||||
uint32_t const roundedDistToPronounceUnits =
|
||||
m_settings.RoundByPresetSoundedDistancesUnits(distToPronounceUnits);
|
||||
m_nextTurnNotificationProgress = PronouncedNotification::First;
|
||||
return GenerateTurnText(roundedDistToPronounceUnits, turn.m_exitNum, false /* useThenInsteadOfDistance */,
|
||||
turn.m_turn, m_settings.GetLengthUnits());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The first notification has not been pronounced but the distance to the turn is too short.
|
||||
// It happens if one turn follows shortly behind another one.
|
||||
m_nextTurnNotificationProgress = PronouncedNotification::First;
|
||||
FastForwardFirstTurnNotification();
|
||||
}
|
||||
return string();
|
||||
}
|
||||
|
||||
if (m_nextTurnNotificationProgress == PronouncedNotification::First &&
|
||||
distanceToTurnMeters < distanceToPronounceNotificationMeters)
|
||||
{
|
||||
m_nextTurnNotificationProgress = PronouncedNotification::Second;
|
||||
FastForwardFirstTurnNotification();
|
||||
return GenerateTurnText(0 /* distanceUnits */, turn.m_exitNum,
|
||||
false /* useThenInsteadOfDistance */,
|
||||
turn.m_turn, m_settings.GetLengthUnits());
|
||||
}
|
||||
return string();
|
||||
}
|
||||
|
||||
void TurnsSound::Enable(bool enable)
|
||||
{
|
||||
if (enable && !m_enabled)
|
||||
Reset();
|
||||
m_enabled = enable;
|
||||
}
|
||||
|
||||
void TurnsSound::SetLengthUnits(::Settings::Units units)
|
||||
{
|
||||
m_settings.SetLengthUnits(units);
|
||||
switch(units)
|
||||
{
|
||||
case ::Settings::Metric:
|
||||
m_settings = Settings(30 /* notificationTimeSeconds */, 200 /* minNotificationDistanceUnits */,
|
||||
2000 /* maxNotificationDistanceUnits */,
|
||||
GetSoundedDistMeters() /* soundedDistancesUnits */,
|
||||
::Settings::Metric /* lengthUnits */);
|
||||
return;
|
||||
case ::Settings::Foot:
|
||||
m_settings = Settings(30 /* notificationTimeSeconds */, 500 /* minNotificationDistanceUnits */,
|
||||
5000 /* maxNotificationDistanceUnits */,
|
||||
GetSoundedDistFeet() /* soundedDistancesUnits */,
|
||||
::Settings::Foot /* lengthUnits */);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TurnsSound::SetSpeedMetersPerSecond(double speed)
|
||||
{
|
||||
// When the quality of GPS data is bad the current speed may be less then zero.
|
||||
// It's easy to reproduce at an office with Nexus 5.
|
||||
// In that case zero meters per second is used.
|
||||
m_speedMetersPerSecond = max(0., speed);
|
||||
}
|
||||
|
||||
void TurnsSound::Reset()
|
||||
{
|
||||
m_nextTurnNotificationProgress = PronouncedNotification::Nothing;
|
||||
m_nextTurnIndex = 0;
|
||||
m_turnNotificationWithThen = false;
|
||||
}
|
||||
|
||||
void TurnsSound::FastForwardFirstTurnNotification()
|
||||
{
|
||||
m_turnNotificationWithThen = false;
|
||||
if (m_nextTurnNotificationProgress == PronouncedNotification::Nothing)
|
||||
m_nextTurnNotificationProgress = PronouncedNotification::First;
|
||||
}
|
||||
|
||||
string DebugPrint(PronouncedNotification const notificationProgress)
|
||||
{
|
||||
switch (notificationProgress)
|
||||
{
|
||||
case PronouncedNotification::Nothing:
|
||||
return "Nothing";
|
||||
case PronouncedNotification::First:
|
||||
return "First";
|
||||
case PronouncedNotification::Second:
|
||||
return "Second";
|
||||
}
|
||||
|
||||
ASSERT(false, ());
|
||||
stringstream out;
|
||||
out << "unknown PronouncedNotification (" << static_cast<int>(notificationProgress) << ")";
|
||||
return out.str();
|
||||
}
|
||||
} // namespace sound
|
||||
} // namespace turns
|
||||
} // namespace routing
|
|
@ -1,144 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/turns_sound_settings.hpp"
|
||||
#include "routing/turns_tts_text.hpp"
|
||||
|
||||
#include "platform/settings.hpp"
|
||||
|
||||
#include "std/string.hpp"
|
||||
|
||||
namespace location
|
||||
{
|
||||
class FollowingInfo;
|
||||
}
|
||||
|
||||
namespace routing
|
||||
{
|
||||
namespace turns
|
||||
{
|
||||
namespace sound
|
||||
{
|
||||
/// \brief The PronouncedNotification enum represents which sound notifications
|
||||
/// for turns were heard.
|
||||
enum class PronouncedNotification
|
||||
{
|
||||
Nothing,
|
||||
First,
|
||||
Second /** The second notification just before the turn was pronounced. */
|
||||
};
|
||||
|
||||
string DebugPrint(PronouncedNotification const notificationProgress);
|
||||
|
||||
/// \brief The TurnsSound class is responsible for all route turn sound notifications functionality.
|
||||
/// To be able to generate turn sound notification the class needs to have correct Settings
|
||||
/// and relevant speed.
|
||||
class TurnsSound
|
||||
{
|
||||
friend void UnitTest_TurnsSoundMetersTest();
|
||||
friend void UnitTest_TurnsSoundMetersTwoTurnsTest();
|
||||
friend void UnitTest_TurnsSoundFeetTest();
|
||||
friend void UnitTest_TurnsSoundComposedTurnTest();
|
||||
friend void UnitTest_TurnsSoundRoundaboutTurnTest();
|
||||
|
||||
/// \brief The private contructor is used only for testing.
|
||||
TurnsSound(uint32_t startBeforeSeconds, uint32_t minStartBeforeMeters,
|
||||
uint32_t maxStartBeforeMeters, uint32_t minDistToSayNotificationMeters)
|
||||
: m_enabled(false), m_speedMetersPerSecond(0.), m_settings(),
|
||||
m_nextTurnNotificationProgress(PronouncedNotification::Nothing),
|
||||
m_turnNotificationWithThen(false), m_nextTurnIndex(0),
|
||||
m_startBeforeSeconds(startBeforeSeconds), m_minStartBeforeMeters(minStartBeforeMeters),
|
||||
m_maxStartBeforeMeters(maxStartBeforeMeters),
|
||||
m_minDistToSayNotificationMeters(minDistToSayNotificationMeters)
|
||||
{
|
||||
ASSERT_LESS_OR_EQUAL(m_minStartBeforeMeters, m_maxStartBeforeMeters, ());
|
||||
}
|
||||
|
||||
/// m_enabled == true when tts is turned on.
|
||||
/// Important! Clients (iOS/Android) implies that m_enabled is false by default.
|
||||
bool m_enabled;
|
||||
/// In m_speedMetersPerSecond is intended for some speed which will be used for
|
||||
/// convertion a distance in seconds to distance in meters. It could be a current
|
||||
/// an end user speed or an average speed for last several seconds.
|
||||
/// @TODO(vbykoianko) It's better to use an average speed
|
||||
/// for last several seconds instead of the current speed here.
|
||||
double m_speedMetersPerSecond;
|
||||
Settings m_settings;
|
||||
/// m_nextTurnNotificationProgress keeps a status which is being changing while
|
||||
/// an end user is coming to the closest (the next) turn along the route.
|
||||
/// When an end user is far from the next turn
|
||||
/// m_nextTurnNotificationProgress == Nothing.
|
||||
/// After the first turn notification has been pronounced
|
||||
/// m_nextTurnNotificationProgress == First.
|
||||
/// After the second notification has been pronounced
|
||||
/// m_nextTurnNotificationProgress == Second.
|
||||
PronouncedNotification m_nextTurnNotificationProgress;
|
||||
/// The flag is set to true if notification about the second turn was pronounced.
|
||||
/// It could happen in expressions like "Turn right. Then turn left."
|
||||
/// This flag is used to pass the information if second turn notification was pronounced
|
||||
/// between two calls of GenerateTurnSound() method.
|
||||
bool m_turnNotificationWithThen;
|
||||
uint32_t m_nextTurnIndex;
|
||||
/// getTtsText is a convector form turn notification information and locale to
|
||||
/// notification string.
|
||||
GetTtsText m_getTtsText;
|
||||
|
||||
string GenerateTurnText(uint32_t distanceUnits, uint8_t exitNum, bool useThenInsteadOfDistance,
|
||||
TurnDirection turnDir, ::Settings::Units lengthUnits) const;
|
||||
/// Generates turn sound notification for the nearest to the current position turn.
|
||||
string GenerateFirstTurnSound(TurnItem const & turn, double distanceToTurnMeters);
|
||||
/// Changes the state of the class to emulate that first turn notification is pronouned
|
||||
/// without pronunciation.
|
||||
void FastForwardFirstTurnNotification();
|
||||
|
||||
// To inform an end user about the next turn with the help of an voice information message
|
||||
// an operation system needs:
|
||||
// - to launch TTS subsystem;
|
||||
// - to pronounce the message.
|
||||
// So to inform the user in time it's necessary to start
|
||||
// m_startBeforeSeconds before the time. It is used in the following way:
|
||||
// we start playing voice notice in m_startBeforeSeconds * TurnsSound::m_speedMetersPerSecond
|
||||
// meters before the turn (for the second voice notification).
|
||||
// When m_startBeforeSeconds * TurnsSound::m_speedMetersPerSecond is too small or too large
|
||||
// we use m_minStartBeforeMeters or m_maxStartBeforeMeters correspondingly.
|
||||
uint32_t const m_startBeforeSeconds;
|
||||
uint32_t const m_minStartBeforeMeters;
|
||||
uint32_t const m_maxStartBeforeMeters;
|
||||
|
||||
// m_minDistToSayNotificationMeters is minimum distance between two turns
|
||||
// when pronouncing the first notification about the second turn makes sense.
|
||||
uint32_t const m_minDistToSayNotificationMeters;
|
||||
|
||||
public:
|
||||
TurnsSound() : m_enabled(false), m_speedMetersPerSecond(0.), m_settings(),
|
||||
m_nextTurnNotificationProgress(PronouncedNotification::Nothing),
|
||||
m_turnNotificationWithThen(false), m_nextTurnIndex(0),
|
||||
m_startBeforeSeconds(5), m_minStartBeforeMeters(25), m_maxStartBeforeMeters(150),
|
||||
m_minDistToSayNotificationMeters(170)
|
||||
{
|
||||
ASSERT_LESS_OR_EQUAL(m_minStartBeforeMeters, m_maxStartBeforeMeters, ());
|
||||
}
|
||||
|
||||
bool IsEnabled() const { return m_enabled; }
|
||||
void Enable(bool enable);
|
||||
void SetLengthUnits(::Settings::Units units);
|
||||
inline ::Settings::Units GetLengthUnits() const { return m_settings.GetLengthUnits(); }
|
||||
inline void SetLocale(string const & locale) { m_getTtsText.SetLocale(locale); }
|
||||
inline string GetLocale() const { return m_getTtsText.GetLocale(); }
|
||||
void SetSpeedMetersPerSecond(double speed);
|
||||
|
||||
/// \brief GenerateTurnSound updates information about the next turn notification.
|
||||
/// It also fills turnNotifications when it's necessary.
|
||||
/// If this TurnsSound wants to play a sound message once it should push one item to
|
||||
/// the vector turnNotifications once when GenerateTurnSound is called.
|
||||
/// \param turn contains information about the next turn.
|
||||
/// \param distanceToTurnMeters is distance to the next turn in meters.
|
||||
/// \param turnNotifications is a parameter to fill it if it's necessary.
|
||||
/// \note The client implies turnNotifications does not contain empty strings.
|
||||
void GenerateTurnSound(vector<TurnItemDist> const & turns, vector<string> & turnNotifications);
|
||||
/// Reset states which reflects current route position.
|
||||
/// The method shall be called after creating a new route or after rerouting.
|
||||
void Reset();
|
||||
};
|
||||
} // namespace sound
|
||||
} // namespace turns
|
||||
} // namespace routing
|
|
@ -12,6 +12,18 @@ namespace turns
|
|||
{
|
||||
namespace sound
|
||||
{
|
||||
void Settings::SetState(uint32_t notificationTimeSeconds, uint32_t minNotificationDistanceUnits,
|
||||
uint32_t maxNotificationDistanceUnits,
|
||||
vector<uint32_t> const & soundedDistancesUnits,
|
||||
::Settings::Units lengthUnits)
|
||||
{
|
||||
m_timeSeconds = notificationTimeSeconds;
|
||||
m_minDistanceUnits = minNotificationDistanceUnits;
|
||||
m_maxDistanceUnits = maxNotificationDistanceUnits;
|
||||
m_soundedDistancesUnits = soundedDistancesUnits;
|
||||
m_lengthUnits = lengthUnits;
|
||||
}
|
||||
|
||||
bool Settings::IsValid() const
|
||||
{
|
||||
return m_minDistanceUnits <= m_maxDistanceUnits &&
|
||||
|
@ -19,12 +31,19 @@ bool Settings::IsValid() const
|
|||
is_sorted(m_soundedDistancesUnits.cbegin(), m_soundedDistancesUnits.cend());
|
||||
}
|
||||
|
||||
double Settings::ComputeTurnDistance(double speedUnitsPerSecond) const
|
||||
uint32_t Settings::ComputeTurnDistanceM(double speedMetersPerSecond) const
|
||||
{
|
||||
ASSERT(IsValid(), ());
|
||||
|
||||
double const turnNotificationDistance = m_timeSeconds * speedUnitsPerSecond;
|
||||
return my::clamp(turnNotificationDistance, m_minDistanceUnits, m_maxDistanceUnits);
|
||||
double const turnNotificationDistanceM = m_timeSeconds * speedMetersPerSecond;
|
||||
return static_cast<uint32_t>(my::clamp(turnNotificationDistanceM,
|
||||
ConvertUnitsToMeters(m_minDistanceUnits),
|
||||
ConvertUnitsToMeters(m_maxDistanceUnits)));
|
||||
}
|
||||
|
||||
bool Settings::TooCloseForFisrtNotification(double distToTurnMeters) const
|
||||
{
|
||||
return m_minDistToSayNotificationMeters >= distToTurnMeters;
|
||||
}
|
||||
|
||||
uint32_t Settings::RoundByPresetSoundedDistancesUnits(uint32_t turnNotificationUnits) const
|
||||
|
@ -83,6 +102,14 @@ double Settings::ConvertMetersToUnits(double distanceInMeters) const
|
|||
return 0.;
|
||||
}
|
||||
|
||||
uint32_t Settings::ComputeDistToPronounceDistM(double speedMetersPerSecond) const
|
||||
{
|
||||
ASSERT_LESS_OR_EQUAL(0, speedMetersPerSecond, ());
|
||||
uint32_t const startBeforeMeters =
|
||||
static_cast<uint32_t>(speedMetersPerSecond * m_startBeforeSeconds);
|
||||
return my::clamp(startBeforeMeters, m_minStartBeforeMeters, m_maxStartBeforeMeters);
|
||||
}
|
||||
|
||||
string DebugPrint(Notification const & notification)
|
||||
{
|
||||
stringstream out;
|
||||
|
|
|
@ -14,47 +14,95 @@ namespace sound
|
|||
{
|
||||
/// \brief The Settings struct is a structure for gathering information about turn sound
|
||||
/// notifications settings.
|
||||
/// All distance parameters shall be set in m_lengthUnits. (Meters of feet for the time being.)
|
||||
/// Part distance parameters shall be set in m_lengthUnits. (Meters of feet for the time being.)
|
||||
/// Another part in meters. See the suffix to understand which units are used.
|
||||
class Settings
|
||||
{
|
||||
friend void UnitTest_TurnNotificationSettingsMetersTest();
|
||||
friend void UnitTest_TurnNotificationSettingsFeetTest();
|
||||
friend void UnitTest_TurnNotificationSettingsNotValidTest();
|
||||
|
||||
uint32_t m_timeSeconds;
|
||||
uint32_t m_minDistanceUnits;
|
||||
uint32_t m_maxDistanceUnits;
|
||||
|
||||
/// To inform an end user about the next turn with the help of an voice information message
|
||||
/// an operation system needs:
|
||||
/// - to launch TTS subsystem;
|
||||
/// - to pronounce the message.
|
||||
/// So to inform the user in time it's necessary to start
|
||||
/// m_startBeforeSeconds before the time. It is used in the following way:
|
||||
/// we start playing voice notice in m_startBeforeSeconds * TurnsSound::m_speedMetersPerSecond
|
||||
/// meters before the turn (for the second voice notification).
|
||||
/// When m_startBeforeSeconds * TurnsSound::m_speedMetersPerSecond is too small or too large
|
||||
/// we use m_{min|max}StartBeforeMeters to clamp the value.
|
||||
uint32_t m_startBeforeSeconds;
|
||||
uint32_t m_minStartBeforeMeters;
|
||||
uint32_t m_maxStartBeforeMeters;
|
||||
|
||||
/// m_minDistToSayNotificationMeters is minimum distance between two turns
|
||||
/// when pronouncing the first notification about the second turn makes sense.
|
||||
uint32_t m_minDistToSayNotificationMeters;
|
||||
|
||||
/// \brief m_distancesToPronounce is a list of distances in m_lengthUnits
|
||||
/// which are ready to be pronounced.
|
||||
vector<uint32_t> m_soundedDistancesUnits;
|
||||
::Settings::Units m_lengthUnits;
|
||||
|
||||
public:
|
||||
Settings()
|
||||
: m_minDistanceUnits(0),
|
||||
m_maxDistanceUnits(0),
|
||||
m_soundedDistancesUnits(),
|
||||
m_lengthUnits(::Settings::Metric) {}
|
||||
// This constructor is for testing only.
|
||||
Settings(uint32_t notificationTimeSeconds, uint32_t minNotificationDistanceUnits,
|
||||
uint32_t maxNotificationDistanceUnits, vector<uint32_t> const & soundedDistancesUnits,
|
||||
uint32_t maxNotificationDistanceUnits, uint32_t startBeforeSeconds,
|
||||
uint32_t minStartBeforeMeters, uint32_t maxStartBeforeMeters,
|
||||
uint32_t minDistToSayNotificationMeters, vector<uint32_t> const & soundedDistancesUnits,
|
||||
::Settings::Units lengthUnits)
|
||||
: m_timeSeconds(notificationTimeSeconds),
|
||||
m_minDistanceUnits(minNotificationDistanceUnits),
|
||||
m_maxDistanceUnits(maxNotificationDistanceUnits),
|
||||
m_soundedDistancesUnits(soundedDistancesUnits),
|
||||
m_lengthUnits(lengthUnits)
|
||||
: m_timeSeconds(notificationTimeSeconds)
|
||||
, m_minDistanceUnits(minNotificationDistanceUnits)
|
||||
, m_maxDistanceUnits(maxNotificationDistanceUnits)
|
||||
, m_startBeforeSeconds(startBeforeSeconds)
|
||||
, m_minStartBeforeMeters(minStartBeforeMeters)
|
||||
, m_maxStartBeforeMeters(maxStartBeforeMeters)
|
||||
, m_minDistToSayNotificationMeters(minDistToSayNotificationMeters)
|
||||
, m_soundedDistancesUnits(soundedDistancesUnits)
|
||||
, m_lengthUnits(lengthUnits)
|
||||
{
|
||||
ASSERT(!m_soundedDistancesUnits.empty(), ());
|
||||
}
|
||||
|
||||
public:
|
||||
Settings(uint32_t startBeforeSeconds, uint32_t minStartBeforeMeters,
|
||||
uint32_t maxStartBeforeMeters, uint32_t minDistToSayNotificationMeters)
|
||||
: m_timeSeconds(0)
|
||||
, m_minDistanceUnits(0)
|
||||
, m_maxDistanceUnits(0)
|
||||
, m_startBeforeSeconds(startBeforeSeconds)
|
||||
, m_minStartBeforeMeters(minStartBeforeMeters)
|
||||
, m_maxStartBeforeMeters(maxStartBeforeMeters)
|
||||
, m_minDistToSayNotificationMeters(minDistToSayNotificationMeters)
|
||||
, m_lengthUnits(::Settings::Metric)
|
||||
{
|
||||
}
|
||||
|
||||
void SetState(uint32_t notificationTimeSeconds, uint32_t minNotificationDistanceUnits,
|
||||
uint32_t maxNotificationDistanceUnits,
|
||||
vector<uint32_t> const & soundedDistancesUnits, ::Settings::Units lengthUnits);
|
||||
|
||||
/// \brief IsValid checks if Settings data is consistent.
|
||||
/// \warning The complexity is up to linear in size of m_soundedDistancesUnits.
|
||||
/// \note For any instance created by default constructor IsValid() returns false.
|
||||
bool IsValid() const;
|
||||
|
||||
/// \brief computes the distance an end user shall be informed about the future turn
|
||||
/// before it, taking into account speedMetersPerSecond and fields of the structure.
|
||||
/// \brief computes the distance an end user shall be informed about the future turn.
|
||||
/// \param speedMetersPerSecond is a speed. For example it could be a current speed of an end
|
||||
/// user.
|
||||
/// \return distance in units which are set in m_lengthUnits. (Meters of feet for the time being.)
|
||||
double ComputeTurnDistance(double speedUnitsPerSecond) const;
|
||||
/// \return distance in meters.
|
||||
uint32_t ComputeTurnDistanceM(double speedMetersPerSecond) const;
|
||||
|
||||
/// \brief computes the distance which will be passed at the |speedMetersPerSecond|
|
||||
/// while pronouncing turn sound notification.
|
||||
uint32_t ComputeDistToPronounceDistM(double speedMetersPerSecond) const;
|
||||
|
||||
/// @return true if distToTurnMeters is too short to start pronouncing first turn notification.
|
||||
bool TooCloseForFisrtNotification(double distToTurnMeters) const;
|
||||
|
||||
/// \brief RoundByPresetSoundedDistancesUnits rounds off its parameter by
|
||||
/// m_soundedDistancesUnits.
|
||||
|
|
Loading…
Add table
Reference in a new issue