Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--map/framework.cpp12
-rw-r--r--map/framework.hpp10
-rw-r--r--map/routing_session.cpp38
-rw-r--r--map/routing_session.hpp12
-rw-r--r--platform/location.hpp8
-rw-r--r--routing/osrm_router.cpp5
-rw-r--r--routing/route.cpp27
-rw-r--r--routing/route.hpp40
-rw-r--r--routing/routing.pro5
-rw-r--r--routing/routing_tests/routing_tests.pro1
-rw-r--r--routing/routing_tests/turns_sound_test.cpp250
-rw-r--r--routing/turns.cpp20
-rw-r--r--routing/turns.hpp39
-rw-r--r--routing/turns_sound.cpp137
-rw-r--r--routing/turns_sound.hpp62
-rw-r--r--routing/turns_sound_settings.cpp103
-rw-r--r--routing/turns_sound_settings.hpp95
17 files changed, 792 insertions, 72 deletions
diff --git a/map/framework.cpp b/map/framework.cpp
index 292799d0a5..dd44962082 100644
--- a/map/framework.cpp
+++ b/map/framework.cpp
@@ -2127,6 +2127,18 @@ void Framework::BuildRoute(m2::PointD const & destination)
});
}
+void Framework::EnableTurnNotification(bool enable)
+{
+ m_routingSession.EnableTurnNotification(enable);
+}
+
+bool Framework::IsTurnNotificationEnabled() { return m_routingSession.IsTurnNotificationEnabled(); }
+
+void Framework::AssignTurnSoundNotificationSettings(routing::turns::sound::Settings const & settings)
+{
+ m_routingSession.AssignTurnSoundNotificationSettings(settings);
+}
+
void Framework::SetRouter(RouterType type)
{
#ifdef DEBUG
diff --git a/map/framework.hpp b/map/framework.hpp
index 8a66fd99e0..c580de5a0a 100644
--- a/map/framework.hpp
+++ b/map/framework.hpp
@@ -62,6 +62,11 @@ namespace search
namespace gui { class Controller; }
namespace anim { class Controller; }
+namespace routing
+{
+namespace turns{ class Settings; }
+}
+
class CountryStatusDisplay;
class BenchmarkEngine;
struct FrameImage;
@@ -565,6 +570,11 @@ public:
void CloseRouting();
void GetRouteFollowingInfo(location::FollowingInfo & info) const { m_routingSession.GetRouteFollowingInfo(info); }
m2::PointD GetRouteEndPoint() const { return m_routingSession.GetEndPoint(); }
+ // @TODO The three methods below has to called from jni.
+ // Sound notifications for turn instructions.
+ void EnableTurnNotification(bool enable);
+ bool IsTurnNotificationEnabled();
+ void AssignTurnSoundNotificationSettings(routing::turns::sound::Settings const & settings);
private:
void SetRouter(routing::RouterType type);
diff --git a/map/routing_session.cpp b/map/routing_session.cpp
index 92020245c7..aa3c1d6b87 100644
--- a/map/routing_session.cpp
+++ b/map/routing_session.cpp
@@ -81,6 +81,7 @@ void RoutingSession::Reset()
RemoveRouteImpl();
m_router->ClearState();
+ m_turnsSound.Reset();
}
RoutingSession::State RoutingSession::OnLocationPositionChanged(m2::PointD const & position,
@@ -104,6 +105,8 @@ RoutingSession::State RoutingSession::OnLocationPositionChanged(m2::PointD const
UNUSED_VALUE(guard);
ASSERT(m_route.IsValid(), ());
+ m_turnsSound.SetSpeedMetersPerSecond(info.m_speed);
+
if (m_route.MoveIterator(info))
{
m_moveAwayCounter = 0;
@@ -160,17 +163,18 @@ void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const
{
formatDistFn(m_route.GetCurrentDistanceToEnd(), info.m_distToTarget, info.m_targetUnitsSuffix);
- double dist;
- TurnItem turn;
- m_route.GetTurn(dist, turn);
+ double distanceToTurnMeters = 0.;
+ turns::TurnItem turn;
+ m_route.GetTurn(distanceToTurnMeters, turn);
- formatDistFn(dist, info.m_distToTurn, info.m_turnUnitsSuffix);
+ formatDistFn(distanceToTurnMeters, info.m_distToTurn, info.m_turnUnitsSuffix);
info.m_turn = turn.m_turn;
info.m_exitNum = turn.m_exitNum;
info.m_time = m_route.GetTime();
info.m_targetName = turn.m_targetName;
- if (dist < kShowLanesDistInMeters)
+ // Lane information.
+ if (distanceToTurnMeters < kShowLanesDistInMeters)
{
// There are two nested loops below. Outer one is for lanes and inner one (ctor of
// SingleLaneInfo) is
@@ -184,6 +188,9 @@ void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const
{
info.m_lanes.clear();
}
+
+ // Voice turn notifications.
+ m_turnsSound.GetRouteFollowingInfo(info, turn, distanceToTurnMeters);
}
else
{
@@ -222,4 +229,25 @@ void RoutingSession::MatchLocationToRoute(location::GpsInfo & location,
UNUSED_VALUE(guard);
m_route.MatchLocationToRoute(location, routeMatchingInfo);
}
+
+void RoutingSession::EnableTurnNotification(bool enable)
+{
+ threads::MutexGuard guard(m_routeSessionMutex);
+ UNUSED_VALUE(guard);
+ m_turnsSound.EnableTurnNotification(enable);
+}
+
+bool RoutingSession::IsTurnNotificationEnabled() const
+{
+ threads::MutexGuard guard(m_routeSessionMutex);
+ UNUSED_VALUE(guard);
+ return m_turnsSound.IsTurnNotificationEnabled();
+}
+
+void RoutingSession::AssignTurnSoundNotificationSettings(turns::sound::Settings const & settings)
+{
+ threads::MutexGuard guard(m_routeSessionMutex);
+ UNUSED_VALUE(guard);
+ m_turnsSound.AssignSettings(settings);
+}
}
diff --git a/map/routing_session.hpp b/map/routing_session.hpp
index f6727b48f2..d842c334b6 100644
--- a/map/routing_session.hpp
+++ b/map/routing_session.hpp
@@ -3,6 +3,8 @@
#include "routing/async_router.hpp"
#include "routing/route.hpp"
#include "routing/router.hpp"
+#include "routing/turns.hpp"
+#include "routing/turns_sound.hpp"
#include "platform/location.hpp"
@@ -76,6 +78,11 @@ public:
// TODO (Dragunov) Make activation of the pedestrian routing
void ActivateAdditionalFeatures() {}
+ // Sound notifications for turn instructions.
+ void AssignTurnSoundNotificationSettings(turns::sound::Settings const & settings);
+ void EnableTurnNotification(bool enable);
+ bool IsTurnNotificationEnabled() const;
+
private:
struct DoReadyCallback
{
@@ -108,5 +115,8 @@ private:
double m_lastDistance;
int m_moveAwayCounter;
m2::PointD m_lastGoodPosition;
+
+ // Sound turn notification parameters.
+ turns::sound::TurnsSound m_turnsSound;
};
-}
+} // namespace routing
diff --git a/platform/location.hpp b/platform/location.hpp
index 28bc74b369..cd1a92c2a6 100644
--- a/platform/location.hpp
+++ b/platform/location.hpp
@@ -3,6 +3,7 @@
#include "base/base.hpp"
#include "routing/turns.hpp"
+#include "routing/turns_sound_settings.hpp"
#include "std/cmath.hpp"
#include "std/string.hpp"
@@ -128,7 +129,12 @@ namespace location
int m_time;
// m_lanes contains lane information on the edge before the turn.
vector<SingleLaneInfoClient> m_lanes;
- // The next street name
+ // m_turnNotifications contains information about the next turn notifications.
+ // If there is nothing to pronounce m_turnNotifications is empty.
+ // If there is something to pronounce the size of m_turnNotifications may be one or even more
+ // depends on the number of notifications to prononce.
+ vector<routing::turns::sound::Notifications> m_turnNotifications;
+ // The next street name.
string m_targetName;
bool IsValid() const { return !m_distToTarget.empty(); }
diff --git a/routing/osrm_router.cpp b/routing/osrm_router.cpp
index 3f28abde02..f1ebf5378e 100644
--- a/routing/osrm_router.cpp
+++ b/routing/osrm_router.cpp
@@ -699,7 +699,7 @@ OsrmRouter::ResultCode OsrmRouter::MakeTurnAnnotation(RawRoutingResult const & r
if (j > 0 && !points.empty())
{
- TurnItem t;
+ turns::TurnItem t;
t.m_index = points.size() - 1;
turns::TurnInfo turnInfo(*mapping, segment[j - 1].node, segment[j].node);
@@ -812,7 +812,8 @@ OsrmRouter::ResultCode OsrmRouter::MakeTurnAnnotation(RawRoutingResult const & r
times.push_back(Route::TTimeItem(points.size() - 1, estimatedTime));
if (routingResult.targetEdge.segment.IsValid())
- turnsDir.push_back(TurnItem(points.size() - 1, turns::TurnDirection::ReachedYourDestination));
+ turnsDir.push_back(
+ turns::TurnItem(points.size() - 1, turns::TurnDirection::ReachedYourDestination));
turns::FixupTurns(points, turnsDir);
turns::CalculateTurnGeometry(points, turnsDir, turnsGeom);
diff --git a/routing/route.cpp b/routing/route.cpp
index 66077f29e5..6e231a0848 100644
--- a/routing/route.cpp
+++ b/routing/route.cpp
@@ -21,19 +21,6 @@ static double const LOCATION_TIME_THRESHOLD = 60.0*1.0;
static double const ON_ROAD_TOLERANCE_M = 50.0;
static double const ON_END_TOLERANCE_M = 10.0;
-string DebugPrint(TurnItem const & turnItem)
-{
- stringstream out;
- out << "[ TurnItem: m_index = " << turnItem.m_index
- << ", m_turn = " << DebugPrint(turnItem.m_turn)
- << ", m_lanes = " << ::DebugPrint(turnItem.m_lanes) << ", m_exitNum = " << turnItem.m_exitNum
- << ", m_sourceName = " << turnItem.m_sourceName
- << ", m_targetName = " << turnItem.m_targetName
- << ", m_keepAnyway = " << turnItem.m_keepAnyway << " ]" << endl;
- return out.str();
-}
-
-
Route::Route(string const & router, vector<m2::PointD> const & points, string const & name)
: m_router(router), m_poly(points), m_name(name)
{
@@ -130,24 +117,24 @@ uint32_t Route::GetTime() const
return (uint32_t)((GetAllTime() - (*it).second));
}
-void Route::GetTurn(double & distance, TurnItem & turn) const
+void Route::GetTurn(double & distance, turns::TurnItem & turn) const
{
if (m_segDistance.empty() || m_turns.empty())
{
ASSERT(!m_segDistance.empty(), ());
ASSERT(!m_turns.empty(), ());
distance = 0;
- turn = TurnItem();
+ turn = turns::TurnItem();
return;
}
- TurnItem t;
+ turns::TurnItem t;
t.m_index = m_current.m_ind;
auto it = upper_bound(m_turns.begin(), m_turns.end(), t,
- [](TurnItem const & v, TurnItem const & item)
- {
- return v.m_index < item.m_index;
- });
+ [](turns::TurnItem const & v, turns::TurnItem const & item)
+ {
+ return v.m_index < item.m_index;
+ });
ASSERT_GREATER_OR_EQUAL((*it).m_index - 1, 0, ());
diff --git a/routing/route.hpp b/routing/route.hpp
index 6da41e880e..6f48945350 100644
--- a/routing/route.hpp
+++ b/routing/route.hpp
@@ -17,46 +17,10 @@ namespace location
namespace routing
{
-struct TurnItem
-{
- TurnItem()
- : m_index(numeric_limits<uint32_t>::max()),
- m_turn(turns::TurnDirection::NoTurn),
- m_exitNum(0),
- m_keepAnyway(false)
- {
- }
-
- TurnItem(uint32_t idx, turns::TurnDirection t, uint32_t exitNum = 0)
- : m_index(idx), m_turn(t), m_exitNum(exitNum), m_keepAnyway(false)
- {
- }
-
- bool operator==(TurnItem const & rhs) const
- {
- return m_index == rhs.m_index && m_turn == rhs.m_turn
- && m_lanes == rhs.m_lanes && m_exitNum == rhs.m_exitNum
- && m_sourceName == rhs.m_sourceName && m_targetName == rhs.m_targetName
- && m_keepAnyway == rhs.m_keepAnyway;
- }
-
- uint32_t m_index; // Index of point on polyline (number of segment + 1).
- turns::TurnDirection m_turn;
- vector<turns::SingleLaneInfo> m_lanes; // Lane information on the edge before the turn.
- uint32_t m_exitNum; // Number of exit on roundabout.
- string m_sourceName;
- string m_targetName;
- // m_keepAnyway is true if the turn shall not be deleted
- // and shall be demonstrated to an end user.
- bool m_keepAnyway;
-};
-
-string DebugPrint(TurnItem const & turnItem);
-
class Route
{
public:
- typedef vector<TurnItem> TTurns;
+ typedef vector<turns::TurnItem> TTurns;
typedef pair<uint32_t, double> TTimeItem;
typedef vector<TTimeItem> TTimes;
@@ -123,7 +87,7 @@ public:
double GetCurrentDistanceToEnd() const;
//@}
- void GetTurn(double & distance, TurnItem & turn) const;
+ void GetTurn(double & distance, turns::TurnItem & turn) const;
/// @return true If position was updated successfully (projection within gps error radius).
bool MoveIterator(location::GpsInfo const & info) const;
diff --git a/routing/routing.pro b/routing/routing.pro
index c60acd1632..14c8a6e451 100644
--- a/routing/routing.pro
+++ b/routing/routing.pro
@@ -32,6 +32,8 @@ SOURCES += \
routing_mapping.cpp \
turns.cpp \
turns_generator.cpp \
+ turns_sound.cpp \
+ turns_sound_settings.cpp \
vehicle_model.cpp \
HEADERS += \
@@ -57,4 +59,7 @@ HEADERS += \
routing_mapping.h \
turns.hpp \
turns_generator.hpp \
+ turns_sound.hpp \
+ turns_sound_settings.hpp \
vehicle_model.hpp \
+
diff --git a/routing/routing_tests/routing_tests.pro b/routing/routing_tests/routing_tests.pro
index 2aa3eea6a5..ed33b734f9 100644
--- a/routing/routing_tests/routing_tests.pro
+++ b/routing/routing_tests/routing_tests.pro
@@ -32,6 +32,7 @@ SOURCES += \
road_graph_builder.cpp \
road_graph_nearest_edges_test.cpp \
turns_generator_test.cpp \
+ turns_sound_test.cpp \
vehicle_model_test.cpp \
HEADERS += \
diff --git a/routing/routing_tests/turns_sound_test.cpp b/routing/routing_tests/turns_sound_test.cpp
new file mode 100644
index 0000000000..ce91cc0dd3
--- /dev/null
+++ b/routing/routing_tests/turns_sound_test.cpp
@@ -0,0 +1,250 @@
+#include "testing/testing.hpp"
+
+#include "routing/turns_sound.hpp"
+#include "routing/turns_sound_settings.hpp"
+
+#include "platform/location.hpp"
+
+namespace
+{
+using namespace location;
+using namespace routing::turns;
+using namespace routing::turns::sound;
+
+Settings const settingsMeters(20 /* notificationTimeSeconds */,
+ 200 /* minNotificationDistanceUnits */,
+ 700 /* maxNotificationDistanceUnits */,
+ {100, 200, 300, 400, 500, 600, 700} /* soundedDistancesUnits */,
+ LengthUnits::Meters /* lengthUnits */);
+
+Settings const settingsFeet(20 /* notificationTimeSeconds */,
+ 500 /* minNotificationDistanceUnits */,
+ 2000 /* maxNotificationDistanceUnits */,
+ {200, 400, 600, 800, 1000, 1500, 2000} /* soundedDistancesUnits */,
+ LengthUnits::Feet /* lengthUnits */);
+double const kEps = 1.;
+double const kSmallEps = .001;
+
+UNIT_TEST(TurnNotificationSettingsMetersTest)
+{
+ Settings const & settings = settingsMeters;
+
+ TEST(settings.IsValid(), ());
+ TEST(my::AlmostEqualAbs(
+ settings.ConvertMetersPerSecondToUnitsPerSecond(20. /* speedUnitsPerSecond */), 20., kEps), ());
+ TEST(my::AlmostEqualAbs(
+ settings.ConvertMetersPerSecondToUnitsPerSecond(0. /* speedUnitsPerSecond */), 0., kEps), ());
+ TEST(my::AlmostEqualAbs(settings.ConvertUnitsToMeters(300. /* distanceInUnits */), 300., kEps), ());
+ TEST_EQUAL(settings.RoundByPresetSoundedDistancesUnits(300 /* distanceInUnits */), 300, ());
+ TEST_EQUAL(settings.RoundByPresetSoundedDistancesUnits(0 /* distanceInUnits */), 100, ());
+
+ TEST(my::AlmostEqualAbs(settings.ComputeTurnNotificationDistanceUnits(0. /* distanceInUnits */),
+ 200., kSmallEps), ());
+ TEST(my::AlmostEqualAbs(settings.ComputeTurnNotificationDistanceUnits(10. /* distanceInUnits */),
+ 200., kSmallEps), ());
+ TEST(my::AlmostEqualAbs(settings.ComputeTurnNotificationDistanceUnits(20. /* distanceInUnits */),
+ 400., kSmallEps), ());
+ TEST(my::AlmostEqualAbs(settings.ComputeTurnNotificationDistanceUnits(35. /* distanceInUnits */),
+ 700., kSmallEps), ());
+ TEST(my::AlmostEqualAbs(settings.ComputeTurnNotificationDistanceUnits(200. /* distanceInUnits */),
+ 700., kSmallEps), ());
+}
+
+UNIT_TEST(TurnNotificationSettingsFeetTest)
+{
+ Settings const & settings = settingsFeet;
+
+ TEST(settings.IsValid(), ());
+ TEST(my::AlmostEqualAbs(
+ settings.ConvertMetersPerSecondToUnitsPerSecond(20. /* speedUnitsPerSecond */), 65., kEps), ());
+ TEST(my::AlmostEqualAbs(
+ settings.ConvertMetersPerSecondToUnitsPerSecond(0. /* speedUnitsPerSecond */), 0., kEps), ());
+ 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.ComputeTurnNotificationDistanceUnits(0. /* distanceInUnits */),
+ 500., kSmallEps), ());
+ TEST(my::AlmostEqualAbs(settings.ComputeTurnNotificationDistanceUnits(10. /* distanceInUnits */),
+ 500., kSmallEps), ());
+ TEST(my::AlmostEqualAbs(settings.ComputeTurnNotificationDistanceUnits(30. /* distanceInUnits */),
+ 600., kSmallEps), ());
+ TEST(my::AlmostEqualAbs(settings.ComputeTurnNotificationDistanceUnits(40. /* distanceInUnits */),
+ 800., kSmallEps), ());
+ TEST(my::AlmostEqualAbs(settings.ComputeTurnNotificationDistanceUnits(200. /* distanceInUnits */),
+ 2000., kSmallEps), ());
+}
+
+UNIT_TEST(TurnNotificationSettingsNotValidTest)
+{
+ Settings settings1(20 /* notificationTimeSeconds */, 500 /* minNotificationDistanceUnits */,
+ 2000 /* maxNotificationDistanceUnits */,
+ {200, 400, 800, 600, 1000, 1500, 2000} /* soundedDistancesUnits */,
+ LengthUnits::Feet /* lengthUnits */);
+ TEST(!settings1.IsValid(), ());
+
+ Settings settings2(20 /* notificationTimeSeconds */, 500 /* minNotificationDistanceUnits */,
+ 2000 /* maxNotificationDistanceUnits */,
+ {200, 400, 600, 800, 1000, 1500, 2000} /* soundedDistancesUnits */,
+ LengthUnits::Undefined /* lengthUnits */);
+ TEST(!settings2.IsValid(), ());
+
+ Settings settings3(20 /* notificationTimeSeconds */, 5000 /* minNotificationDistanceUnits */,
+ 2000 /* maxNotificationDistanceUnits */,
+ {200, 400, 600, 800, 1000, 1500, 2000} /* soundedDistancesUnits */,
+ LengthUnits::Meters /* lengthUnits */);
+ TEST(!settings3.IsValid(), ());
+}
+
+UNIT_TEST(TurnsSoundMetersTest)
+{
+ TurnsSound turnSound;
+ turnSound.EnableTurnNotification(true);
+ turnSound.AssignSettings(settingsMeters);
+ turnSound.Reset();
+ turnSound.SetSpeedMetersPerSecond(30.);
+
+ TurnItem turnItem(5 /* idx */, TurnDirection::TurnRight);
+ FollowingInfo followInfo;
+
+ ASSERT(followInfo.m_turnNotifications.empty(), ());
+
+ // Starting nearing the turnItem.
+ // 1000 meters till the turn. No sound notifications is required.
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 1000. /* distanceToTurnMeters */);
+ TEST(followInfo.m_turnNotifications.empty(), ());
+
+ // 700 meters till the turn. No sound notifications is required.
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 700. /* distanceToTurnMeters */);
+ TEST(followInfo.m_turnNotifications.empty(), ());
+
+ // 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
+ // we need to play the first voice notification 20 seconds before the turn.
+ // Besides that we need 5 seconds (but 100 meters maximum) for playing the notification.
+ // 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.
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 699. /* distanceToTurnMeters */);
+ TEST_EQUAL(followInfo.m_turnNotifications.size(), 1, ());
+ TEST_EQUAL(followInfo.m_turnNotifications[0].m_distanceUnits, 600, ());
+ TEST_EQUAL(followInfo.m_turnNotifications[0].m_exitNum, 0, ());
+ TEST(!followInfo.m_turnNotifications[0].m_useThenInsteadOfDistance, ());
+ TEST_EQUAL(followInfo.m_turnNotifications[0].m_turnDir, TurnDirection::TurnRight, ());
+ TEST_EQUAL(followInfo.m_turnNotifications[0].m_lengthUnits, LengthUnits::Meters, ());
+
+ // 650 meters till the turn. No sound notifications is required.
+ followInfo.m_turnNotifications.clear();
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 650. /* distanceToTurnMeters */);
+ TEST(followInfo.m_turnNotifications.empty(), ());
+
+ // 150 meters till the turn. No sound notifications is required.
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 150. /* distanceToTurnMeters */);
+ TEST(followInfo.m_turnNotifications.empty(), ());
+
+ // 100 meters till the turn. No sound notifications is required.
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 100. /* distanceToTurnMeters */);
+ TEST(followInfo.m_turnNotifications.empty(), ());
+
+ // 99 meters till the turn. It's time to pronounce the second voice notification.
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 99. /* distanceToTurnMeters */);
+ TEST_EQUAL(followInfo.m_turnNotifications.size(), 1, ());
+ TEST_EQUAL(followInfo.m_turnNotifications[0].m_distanceUnits, 0, ());
+ TEST_EQUAL(followInfo.m_turnNotifications[0].m_exitNum, 0, ());
+ TEST(!followInfo.m_turnNotifications[0].m_useThenInsteadOfDistance, ());
+ TEST_EQUAL(followInfo.m_turnNotifications[0].m_turnDir, TurnDirection::TurnRight, ());
+ TEST_EQUAL(followInfo.m_turnNotifications[0].m_lengthUnits, LengthUnits::Meters, ());
+
+ // 99 meters till the turn again. No sound notifications is required.
+ followInfo.m_turnNotifications.clear();
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 99. /* distanceToTurnMeters */);
+ TEST(followInfo.m_turnNotifications.empty(), ());
+
+ // 50 meters till the turn. No sound notifications is required.
+ followInfo.m_turnNotifications.clear();
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 50. /* distanceToTurnMeters */);
+ TEST(followInfo.m_turnNotifications.empty(), ());
+
+ // 0 meters till the turn. No sound notifications is required.
+ followInfo.m_turnNotifications.clear();
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 0. /* distanceToTurnMeters */);
+ TEST(followInfo.m_turnNotifications.empty(), ());
+
+ TEST(turnSound.IsTurnNotificationEnabled(), ());
+}
+
+UNIT_TEST(TurnsSoundFeetTest)
+{
+ TurnsSound turnSound;
+ turnSound.EnableTurnNotification(true);
+ turnSound.AssignSettings(settingsFeet);
+ turnSound.Reset();
+ turnSound.SetSpeedMetersPerSecond(30.);
+
+ TurnItem turnItem(7 /* idx */, TurnDirection::EnterRoundAbout, 3 /* exitNum */);
+ FollowingInfo followInfo;
+
+ ASSERT(followInfo.m_turnNotifications.empty(), ());
+
+ // Starting nearing the turnItem.
+ // 1000 meters till the turn. No sound notifications is required.
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 1000. /* distanceToTurnMeters */);
+ TEST(followInfo.m_turnNotifications.empty(), ());
+
+ // 700 meters till the turn. No sound notifications is required.
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 700. /* distanceToTurnMeters */);
+ TEST(followInfo.m_turnNotifications.empty(), ());
+
+ // 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
+ // we need to play the first voice notification 20 seconds before the turn.
+ // Besides that we need 5 seconds (but 100 meters maximum) for playing the notification.
+ // 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.
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 699. /* distanceToTurnMeters */);
+ TEST_EQUAL(followInfo.m_turnNotifications.size(), 1, ());
+ TEST_EQUAL(followInfo.m_turnNotifications[0].m_distanceUnits, 2000, ());
+ TEST_EQUAL(followInfo.m_turnNotifications[0].m_exitNum, 3, ());
+ TEST(!followInfo.m_turnNotifications[0].m_useThenInsteadOfDistance, ());
+ TEST_EQUAL(followInfo.m_turnNotifications[0].m_turnDir, TurnDirection::EnterRoundAbout, ());
+ TEST_EQUAL(followInfo.m_turnNotifications[0].m_lengthUnits, LengthUnits::Feet, ());
+
+ // 650 meters till the turn. No sound notifications is required.
+ followInfo.m_turnNotifications.clear();
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 650. /* distanceToTurnMeters */);
+ TEST(followInfo.m_turnNotifications.empty(), ());
+
+ // 150 meters till the turn. No sound notifications is required.
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 150. /* distanceToTurnMeters */);
+ TEST(followInfo.m_turnNotifications.empty(), ());
+
+ // 100 meters till the turn. No sound notifications is required.
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 100. /* distanceToTurnMeters */);
+ TEST(followInfo.m_turnNotifications.empty(), ());
+
+ // 99 meters till the turn. It's time to pronounce the second voice notification.
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 99. /* distanceToTurnMeters */);
+ TEST_EQUAL(followInfo.m_turnNotifications.size(), 1, ());
+ TEST_EQUAL(followInfo.m_turnNotifications[0].m_distanceUnits, 0, ());
+ TEST_EQUAL(followInfo.m_turnNotifications[0].m_exitNum, 3, ());
+ TEST(!followInfo.m_turnNotifications[0].m_useThenInsteadOfDistance, ());
+ TEST_EQUAL(followInfo.m_turnNotifications[0].m_turnDir, TurnDirection::EnterRoundAbout, ());
+ TEST_EQUAL(followInfo.m_turnNotifications[0].m_lengthUnits, LengthUnits::Feet, ());
+
+ // 99 meters till the turn again. No sound notifications is required.
+ followInfo.m_turnNotifications.clear();
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 99. /* distanceToTurnMeters */);
+ TEST(followInfo.m_turnNotifications.empty(), ());
+
+ // 50 meters till the turn. No sound notifications is required.
+ followInfo.m_turnNotifications.clear();
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 50. /* distanceToTurnMeters */);
+ TEST(followInfo.m_turnNotifications.empty(), ());
+
+ // 0 meters till the turn. No sound notifications is required.
+ followInfo.m_turnNotifications.clear();
+ turnSound.GetRouteFollowingInfo(followInfo, turnItem, 0. /* distanceToTurnMeters */);
+ TEST(followInfo.m_turnNotifications.empty(), ());
+
+ TEST(turnSound.IsTurnNotificationEnabled(), ());
+}
+} // namespace
diff --git a/routing/turns.cpp b/routing/turns.cpp
index 271ade6865..db0e4840e7 100644
--- a/routing/turns.cpp
+++ b/routing/turns.cpp
@@ -8,6 +8,7 @@
namespace
{
using namespace routing::turns;
+
/// The order is important. Starting with the most frequent tokens according to
/// taginfo.openstreetmap.org we minimize the number of the comparisons in ParseSingleLane().
array<pair<LaneWay, string>, static_cast<size_t>(LaneWay::Count)> const g_laneWayNames = {
@@ -43,7 +44,7 @@ array<pair<TurnDirection, string>, static_cast<size_t>(TurnDirection::Count)> co
{TurnDirection::ReachedYourDestination, "ReachedYourDestination"}}};
static_assert(g_turnNames.size() == static_cast<size_t>(TurnDirection::Count),
"Check the size of g_turnNames");
-}
+} // namespace
namespace routing
{
@@ -60,6 +61,18 @@ bool SingleLaneInfo::operator==(SingleLaneInfo const & other) const
return m_lane == other.m_lane && m_isRecommended == other.m_isRecommended;
}
+string DebugPrint(TurnItem const & turnItem)
+{
+ stringstream out;
+ out << "[ TurnItem: m_index = " << turnItem.m_index
+ << ", m_turn = " << DebugPrint(turnItem.m_turn)
+ << ", m_lanes = " << ::DebugPrint(turnItem.m_lanes) << ", m_exitNum = " << turnItem.m_exitNum
+ << ", m_sourceName = " << turnItem.m_sourceName
+ << ", m_targetName = " << turnItem.m_targetName
+ << ", m_keepAnyway = " << turnItem.m_keepAnyway << " ]" << endl;
+ return out.str();
+}
+
string const GetTurnString(TurnDirection turn)
{
for (auto const & p : g_turnNames)
@@ -242,6 +255,5 @@ string DebugPrint(SingleLaneInfo const & singleLaneInfo)
<< ", m_lane == " << ::DebugPrint(singleLaneInfo.m_lane) << " ]" << endl;
return out.str();
}
-
-}
-}
+} // namespace turns
+} // namespace routing
diff --git a/routing/turns.hpp b/routing/turns.hpp
index 20c2b2e3ba..1dc77d86a0 100644
--- a/routing/turns.hpp
+++ b/routing/turns.hpp
@@ -89,7 +89,7 @@ struct TurnGeom
string DebugPrint(TurnGeom const & turnGeom);
-typedef vector<turns::TurnGeom> TTurnsGeom;
+typedef vector<TurnGeom> TTurnsGeom;
typedef vector<LaneWay> TSingleLane;
struct SingleLaneInfo
@@ -103,6 +103,43 @@ struct SingleLaneInfo
string DebugPrint(SingleLaneInfo const & singleLaneInfo);
+struct TurnItem
+{
+ TurnItem()
+ : m_index(numeric_limits<uint32_t>::max()),
+ m_turn(TurnDirection::NoTurn),
+ m_exitNum(0),
+ m_keepAnyway(false)
+ {
+ }
+
+ TurnItem(uint32_t idx, TurnDirection t, uint32_t exitNum = 0)
+ : m_index(idx), m_turn(t), m_exitNum(exitNum), m_keepAnyway(false)
+ {
+ }
+
+ bool operator==(TurnItem const & rhs) const
+ {
+ return m_index == rhs.m_index && m_turn == rhs.m_turn && m_lanes == rhs.m_lanes &&
+ m_exitNum == rhs.m_exitNum && m_sourceName == rhs.m_sourceName &&
+ m_targetName == rhs.m_targetName && m_keepAnyway == rhs.m_keepAnyway;
+ }
+
+ uint32_t m_index; /*!< Index of point on polyline (number of segment + 1). */
+ TurnDirection m_turn;
+ vector<SingleLaneInfo> m_lanes; /*!< Lane information on the edge before the turn. */
+ uint32_t m_exitNum; /*!< Number of exit on roundabout. */
+ string m_sourceName;
+ string m_targetName;
+ /*!
+ * \brief m_keepAnyway is true if the turn shall not be deleted
+ * and shall be demonstrated to an end user.
+ */
+ bool m_keepAnyway;
+};
+
+string DebugPrint(TurnItem const & turnItem);
+
string const GetTurnString(TurnDirection turn);
bool IsLeftTurn(TurnDirection t);
diff --git a/routing/turns_sound.cpp b/routing/turns_sound.cpp
new file mode 100644
index 0000000000..de27b081f5
--- /dev/null
+++ b/routing/turns_sound.cpp
@@ -0,0 +1,137 @@
+#include "routing/turns_sound.hpp"
+
+#include "platform/location.hpp"
+
+namespace
+{
+// 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
+// kStartBeforeSeconds before the time. It is used in the following way:
+// we start playing voice notice in kStartBeforeSeconds * TurnsSound::m_speedMetersPerSecond
+// meters before the turn (for the second voice notification).
+// When kStartBeforeSeconds * TurnsSound::m_speedMetersPerSecond is too small or too large
+// we use kMinStartBeforeMeters or kMaxStartBeforeMeters correspondingly.
+uint32_t const kStartBeforeSeconds = 5;
+uint32_t const kMinStartBeforeMeters = 10;
+uint32_t const kMaxStartBeforeMeters = 100;
+
+uint32_t CalculateDistBeforeMeters(double m_speedMetersPerSecond)
+{
+ ASSERT_LESS_OR_EQUAL(0, m_speedMetersPerSecond, ());
+ uint32_t const startBeforeMeters =
+ static_cast<uint32_t>(m_speedMetersPerSecond * kStartBeforeSeconds);
+ if (startBeforeMeters <= kMinStartBeforeMeters)
+ return kMinStartBeforeMeters;
+ if (startBeforeMeters >= kMaxStartBeforeMeters)
+ return kMaxStartBeforeMeters;
+ return startBeforeMeters;
+}
+} // namespace
+
+namespace routing
+{
+namespace turns
+{
+namespace sound
+{
+void TurnsSound::GetRouteFollowingInfo(location::FollowingInfo & info, TurnItem const & turn,
+ double distanceToTurnMeters) const
+{
+ if (m_turnNotificationEnabled)
+ {
+ if (m_nextTurnIndex != turn.m_index)
+ {
+ m_nextTurnNotificationProgress = SoundNotificationProgress::NoNotificationsPronounced;
+ m_nextTurnIndex = turn.m_index;
+ }
+
+ uint32_t const distanceToPronounceNotificationMeters = CalculateDistBeforeMeters(m_speedMetersPerSecond);
+
+ if (m_nextTurnNotificationProgress == SoundNotificationProgress::NoNotificationsPronounced)
+ {
+ double const currentSpeedUntisPerSecond =
+ m_settings.ConvertMetersPerSecondToUnitsPerSecond(m_speedMetersPerSecond);
+ double const turnNotificationDistUnits =
+ m_settings.ComputeTurnNotificationDistanceUnits(currentSpeedUntisPerSecond);
+ uint32_t const turnNotificationDistMeters =
+ m_settings.ConvertUnitsToMeters(turnNotificationDistUnits) + distanceToPronounceNotificationMeters;
+
+ if (distanceToTurnMeters < turnNotificationDistMeters)
+ {
+ // First turn sound notification.
+ uint32_t const distToPronounce =
+ m_settings.RoundByPresetSoundedDistancesUnits(turnNotificationDistUnits);
+ info.m_turnNotifications.emplace_back(distToPronounce, turn.m_exitNum, false, turn.m_turn,
+ m_settings.GetLengthUnits());
+ // @TODO(vbykoianko) Check if there's a turn immediately after the current turn.
+ // If so add an extra item to info.m_turnNotifications with "then parameter".
+ m_nextTurnNotificationProgress =
+ SoundNotificationProgress::BeforehandNotificationPronounced;
+ }
+ }
+ else if (m_nextTurnNotificationProgress ==
+ SoundNotificationProgress::BeforehandNotificationPronounced &&
+ distanceToTurnMeters < distanceToPronounceNotificationMeters)
+ {
+ info.m_turnNotifications.emplace_back(0, turn.m_exitNum, false, turn.m_turn,
+ m_settings.GetLengthUnits());
+
+ // @TODO(vbykoianko) Check if there's a turn immediately after the current turn.
+ // If so add an extra item to info.m_turnNotifications with "then parameter".
+ m_nextTurnNotificationProgress = SoundNotificationProgress::SecondNotificationPronounced;
+ }
+ }
+}
+
+void TurnsSound::EnableTurnNotification(bool enable)
+{
+ if (enable)
+ Reset();
+ m_turnNotificationEnabled = enable;
+}
+
+void TurnsSound::AssignSettings(Settings const & newSettings)
+{
+ ASSERT(newSettings.IsValid(), ());
+ m_settings = newSettings;
+}
+
+void TurnsSound::SetSpeedMetersPerSecond(double speed)
+{
+ Reset();
+ // 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 =
+ turns::sound::SoundNotificationProgress::NoNotificationsPronounced;
+ m_nextTurnIndex = 0;
+}
+
+string DebugPrint(SoundNotificationProgress const notificationProgress)
+{
+ switch (notificationProgress)
+ {
+ case SoundNotificationProgress::NoNotificationsPronounced:
+ return "NoNotificationsPronounced";
+ case SoundNotificationProgress::BeforehandNotificationPronounced:
+ return "BeforehandNotificationPronounced";
+ case SoundNotificationProgress::SecondNotificationPronounced:
+ return "SecondNotificationPronounced";
+ default:
+ ASSERT(false, ());
+ stringstream out;
+ out << "unknown SoundNotificationProgress (" << static_cast<int>(notificationProgress) << ")";
+ return out.str();
+ }
+}
+} // namespace sound
+} // namespace turns
+} // namespace routing
diff --git a/routing/turns_sound.hpp b/routing/turns_sound.hpp
new file mode 100644
index 0000000000..bd0993691b
--- /dev/null
+++ b/routing/turns_sound.hpp
@@ -0,0 +1,62 @@
+#pragma once
+
+#include "routing/turns.hpp"
+#include "routing/turns_sound_settings.hpp"
+
+#include "std/string.hpp"
+
+namespace location
+{
+class FollowingInfo;
+}
+
+namespace routing
+{
+namespace turns
+{
+namespace sound
+{
+/// \brief The SoundNotificationProgress enum represents which sound notifications
+/// for turns were heard.
+enum class SoundNotificationProgress
+{
+ NoNotificationsPronounced,
+ BeforehandNotificationPronounced,
+ SecondNotificationPronounced /** The second notification just before the turn was pronounced. */
+};
+
+string DebugPrint(SoundNotificationProgress const l);
+
+/// \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
+{
+ bool m_turnNotificationEnabled;
+ /// 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 avarage speed for last several seconds.
+ /// @TODO(vbykoianko) It's better to use an avarage speed
+ /// for last several seconds instead of the current speed here.
+ double m_speedMetersPerSecond;
+ Settings m_settings;
+ mutable SoundNotificationProgress m_nextTurnNotificationProgress;
+ mutable uint32_t m_nextTurnIndex;
+
+public:
+ TurnsSound() : m_turnNotificationEnabled(false) {}
+
+ bool IsTurnNotificationEnabled() const { return m_turnNotificationEnabled; }
+ void EnableTurnNotification(bool enable);
+ void AssignSettings(Settings const & newSettings);
+ void SetSpeedMetersPerSecond(double speed);
+
+ void GetRouteFollowingInfo(location::FollowingInfo & info, TurnItem const & turn,
+ double distanceToTurnMeters) const;
+ /// 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
diff --git a/routing/turns_sound_settings.cpp b/routing/turns_sound_settings.cpp
new file mode 100644
index 0000000000..935356f612
--- /dev/null
+++ b/routing/turns_sound_settings.cpp
@@ -0,0 +1,103 @@
+#include "routing/turns_sound_settings.hpp"
+
+#include "platform/measurement_utils.hpp"
+
+#include "base/string_utils.hpp"
+
+namespace
+{
+using namespace routing::turns::sound;
+
+} // namespace
+
+namespace routing
+{
+namespace turns
+{
+namespace sound
+{
+bool Settings::IsValid() const
+{
+ return m_lengthUnits != LengthUnits::Undefined &&
+ m_minNotificationDistanceUnits <= m_maxNotificationDistanceUnits &&
+ !m_soundedDistancesUnits.empty() &&
+ is_sorted(m_soundedDistancesUnits.cbegin(), m_soundedDistancesUnits.cend());
+}
+
+double Settings::ComputeTurnNotificationDistanceUnits(double speedUnitsPerSecond) const
+{
+ ASSERT(IsValid(), ());
+
+ double turnNotificationDistance = m_notificationTimeSeconds * speedUnitsPerSecond;
+
+ if (turnNotificationDistance < m_minNotificationDistanceUnits)
+ turnNotificationDistance = m_minNotificationDistanceUnits;
+ if (turnNotificationDistance > m_maxNotificationDistanceUnits)
+ turnNotificationDistance = m_maxNotificationDistanceUnits;
+
+ return turnNotificationDistance;
+}
+
+uint32_t Settings::RoundByPresetSoundedDistancesUnits(uint32_t turnNotificationUnits) const
+{
+ ASSERT(IsValid(), ());
+
+ for (auto const distUnits : m_soundedDistancesUnits)
+ {
+ if (distUnits >= turnNotificationUnits)
+ return distUnits;
+ }
+
+ ASSERT(false, ("m_soundedDistancesUnits shall contain bigger values."));
+ return m_soundedDistancesUnits.empty() ? 0 : m_soundedDistancesUnits.back();
+}
+
+double Settings::ConvertMetersPerSecondToUnitsPerSecond(double speedInMetersPerSecond) const
+{
+ switch (m_lengthUnits)
+ {
+ case LengthUnits::Undefined:
+ default:
+ ASSERT(false, ());
+ return 0.;
+ case LengthUnits::Meters:
+ return speedInMetersPerSecond;
+ case LengthUnits::Feet:
+ return MeasurementUtils::MetersToFeet(speedInMetersPerSecond);
+ }
+}
+
+double Settings::ConvertUnitsToMeters(double distanceInUnits) const
+{
+ switch (m_lengthUnits)
+ {
+ case LengthUnits::Undefined:
+ default:
+ ASSERT(false, ());
+ return 0.;
+ case LengthUnits::Meters:
+ return distanceInUnits;
+ case LengthUnits::Feet:
+ return MeasurementUtils::FeetToMeters(distanceInUnits);
+ }
+}
+
+string DebugPrint(LengthUnits const & lengthUnits)
+{
+ switch (lengthUnits)
+ {
+ case LengthUnits::Undefined:
+ return "LengthUnits::Undefined";
+ case LengthUnits::Meters:
+ return "LengthUnits::Undefined";
+ case LengthUnits::Feet:
+ return "LengthUnits::Feet";
+ default:
+ stringstream out;
+ out << "Unknown LengthUnits value: " << static_cast<int>(lengthUnits);
+ return out.str();
+ }
+}
+} // namespace sound
+} // namespace turns
+} // namespace routing
diff --git a/routing/turns_sound_settings.hpp b/routing/turns_sound_settings.hpp
new file mode 100644
index 0000000000..310a553ac3
--- /dev/null
+++ b/routing/turns_sound_settings.hpp
@@ -0,0 +1,95 @@
+#pragma once
+
+#include "routing/turns.hpp"
+
+namespace routing
+{
+namespace turns
+{
+namespace sound
+{
+enum class LengthUnits
+{
+ Undefined = 0,
+ Meters,
+ Feet
+};
+
+string DebugPrint(LengthUnits const & lengthUnits);
+
+/// \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.)
+class Settings
+{
+ uint32_t m_notificationTimeSeconds;
+ uint32_t m_minNotificationDistanceUnits;
+ uint32_t m_maxNotificationDistanceUnits;
+
+ /// \brief m_distancesToPronounce is a list of distances in m_lengthUnits
+ /// which are ready to be pronounced.
+ vector<uint32_t> m_soundedDistancesUnits;
+ LengthUnits m_lengthUnits;
+
+public:
+ Settings() : m_lengthUnits(LengthUnits::Undefined) {}
+ Settings(uint32_t notificationTimeSeconds, uint32_t minNotificationDistanceUnits,
+ uint32_t maxNotificationDistanceUnits, vector<uint32_t> const & soundedDistancesUnits,
+ LengthUnits lengthUnits)
+ : m_notificationTimeSeconds(notificationTimeSeconds),
+ m_minNotificationDistanceUnits(minNotificationDistanceUnits),
+ m_maxNotificationDistanceUnits(maxNotificationDistanceUnits),
+ m_soundedDistancesUnits(soundedDistancesUnits),
+ m_lengthUnits(lengthUnits)
+ {
+ ASSERT(!m_soundedDistancesUnits.empty(), ());
+ }
+
+ /// \brief IsValid checks if Settings data is consistent.
+ /// \warning The complexity is up to linear in size of m_soundedDistancesUnits.
+ 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.
+ /// \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 ComputeTurnNotificationDistanceUnits(double speedUnitsPerSecond) const;
+
+ /// \brief RoundByPresetSoundedDistancesUnits rounds off its parameter by
+ /// m_soundedDistancesUnits.
+ /// \param turnNotificationDistance is a distance in m_lengthUnits.
+ /// \return the distance which will be used (will be pronounced) in the next turn sound
+ /// notification in m_lengthUnits units. (Meters of feet for the time being.)
+ /// The result will be one of the m_soundedDistancesUnits values.
+ uint32_t RoundByPresetSoundedDistancesUnits(uint32_t turnNotificationUnits) const;
+
+ LengthUnits GetLengthUnits() const { return m_lengthUnits; }
+ double ConvertMetersPerSecondToUnitsPerSecond(double speedInMetersPerSecond) const;
+ double ConvertUnitsToMeters(double distanceInUnits) const;
+};
+
+/// \brief The Notifications struct contains all the information about the next sound
+/// notification to pronounce.
+struct Notifications
+{
+ uint32_t const m_distanceUnits;
+ uint8_t const m_exitNum;
+ bool const m_useThenInsteadOfDistance;
+ TurnDirection const m_turnDir;
+ LengthUnits const m_lengthUnits;
+
+ Notifications(uint32_t distanceUnits, uint8_t exitNum, bool useThenInsteadOfDistance,
+ TurnDirection turnDir, LengthUnits lengthUnits)
+ : m_distanceUnits(distanceUnits),
+ m_exitNum(exitNum),
+ m_useThenInsteadOfDistance(useThenInsteadOfDistance),
+ m_turnDir(turnDir),
+ m_lengthUnits(lengthUnits)
+ {
+ }
+ bool IsValid() { return m_lengthUnits != LengthUnits::Undefined; }
+};
+} // namespace sound
+} // namespace turns
+} // namespace routing