AbstractLocationProvider.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. using System.Collections;
  2. using UnityEngine;
  3. #if PLATFORM_ANDROID
  4. using UnityEngine.Android;
  5. #endif
  6. namespace ARLocation
  7. {
  8. /// <summary>
  9. /// Abstract location provider. All concrete location provider implementations
  10. /// should derive from this.
  11. /// </summary>
  12. public abstract class AbstractLocationProvider : ILocationProvider
  13. {
  14. protected double LowPassFilterFactor;
  15. /// <summary>
  16. /// The name of the location provider.
  17. /// </summary>
  18. /// <value>The name.</value>
  19. public abstract string Name { get; }
  20. /// <summary>
  21. /// The options of the location provider.
  22. /// </summary>
  23. /// <value>The options.</value>
  24. public LocationProviderOptions Options { get; set; }
  25. /// <summary>
  26. /// Gets or sets the current location.
  27. /// </summary>
  28. /// <value>The current location.</value>
  29. public LocationReading CurrentLocation { get; protected set; }
  30. /// <summary>
  31. /// Gets or sets the previous location.
  32. /// </summary>
  33. /// <value>The last location.</value>
  34. public LocationReading LastLocation { get; protected set; }
  35. public LocationReading LastLocationRaw { get; protected set; }
  36. /// <summary>
  37. /// Gets or sets the previous raw location reading.
  38. /// </summary>
  39. /// <value>The raw location last.</value>
  40. public LocationReading CurrentLocationRaw { get; protected set; }
  41. /// <summary>
  42. /// The current heading reading.
  43. /// </summary>
  44. /// <value>The current heading.</value>
  45. public HeadingReading CurrentHeading { get; protected set; }
  46. /// <summary>
  47. /// The previous heading reading.
  48. /// </summary>
  49. /// <value>The last heading.</value>
  50. public HeadingReading LastHeading { get; protected set; }
  51. /// <summary>
  52. /// The start point, i.e., the first measured location.
  53. /// </summary>
  54. /// <value>The start point.</value>
  55. public LocationReading FirstLocation { get; protected set; }
  56. /// <summary>
  57. /// Gets or sets the current status of the location provider.
  58. /// </summary>
  59. /// <value>The status.</value>
  60. public LocationProviderStatus Status { get; protected set; }
  61. /// <summary>
  62. /// If true, the location provider is enablied and getting regular location
  63. /// updated from the device.
  64. /// </summary>
  65. /// <value><c>true</c> if is enabled; otherwise, <c>false</c>.</value>
  66. public bool IsEnabled { get; protected set; }
  67. /// <summary>
  68. /// If true, the first reading has not occured yet.
  69. /// </summary>
  70. /// <value><c>true</c> if first reading; otherwise, <c>false</c>.</value>
  71. public bool FirstReading { get; protected set; }
  72. /// <summary>
  73. /// If true, the provider has a functioning magnetic compass sensor.
  74. /// </summary>
  75. /// <value><c>true</c> if is compass enabled; otherwise, <c>false</c>.</value>
  76. public abstract bool IsCompassEnabled { get; }
  77. /// <summary>
  78. /// The start time of the location provider.
  79. /// </summary>
  80. /// <value>The start time.</value>
  81. public float StartTime { get; protected set; }
  82. /// <summary>
  83. /// If true, location updates are paused.
  84. /// </summary>
  85. public bool Paused { get; protected set; }
  86. public int LocationUpdateCount { get; protected set; }
  87. public bool HasStarted => Status == LocationProviderStatus.Started;
  88. public bool ApplyCompassTiltCompensationOnAndroid { get; set; } = true;
  89. public double DistanceFromStartPoint
  90. {
  91. get { return LocationReading.HorizontalDistance(FirstLocation, CurrentLocation); }
  92. }
  93. /// <summary>
  94. /// Event for when a new location data is received.
  95. /// </summary>
  96. public event LocationUpdatedDelegate LocationUpdated;
  97. /// <summary>
  98. /// Event for when a new compass data is received.
  99. /// </summary>
  100. public event CompassUpdateDelegate CompassUpdated;
  101. public event LocationEnabledDelegate LocationEnabled;
  102. public event LocationFailedDelegate LocationFailed;
  103. public event LocationUpdatedDelegate LocationUpdatedRaw;
  104. /// <summary>
  105. /// Reads the location from the device; should be implemented by each
  106. /// provider.
  107. /// </summary>
  108. /// <returns>The location.</returns>
  109. protected abstract LocationReading? ReadLocation();
  110. /// <summary>
  111. /// Reads the heading from the device; should be implemented by each
  112. /// provider.
  113. /// </summary>
  114. /// <returns>The heading.</returns>
  115. protected abstract HeadingReading? ReadHeading();
  116. /// <summary>
  117. /// Requests the location and compass updates from the device; should be implemented by each
  118. /// provider.
  119. /// </summary>
  120. protected abstract void RequestLocationAndCompassUpdates();
  121. /// <summary>
  122. /// Updates the location service status from the device; should be implemented by each
  123. /// provider.
  124. /// </summary>
  125. protected abstract void UpdateLocationRequestStatus();
  126. protected AbstractLocationProvider()
  127. {
  128. IsEnabled = false;
  129. FirstReading = true;
  130. Paused = false;
  131. Status = LocationProviderStatus.Idle;
  132. }
  133. public virtual IEnumerator Start(uint maxWaitTime = 10000, uint delay = 0)
  134. {
  135. // Debug.Log("[AbstractLocationProvider]: Starting...");
  136. #if PLATFORM_ANDROID
  137. if (!Permission.HasUserAuthorizedPermission(Permission.FineLocation))
  138. {
  139. Permission.RequestUserPermission(Permission.FineLocation);
  140. }
  141. yield return new WaitForSeconds(1);
  142. #endif
  143. if (delay > 0)
  144. {
  145. yield return new WaitForSeconds(delay);
  146. }
  147. RequestLocationAndCompassUpdates();
  148. uint maxWait = maxWaitTime;
  149. UpdateLocationRequestStatus();
  150. while (Status == LocationProviderStatus.Initializing && maxWait > 0)
  151. {
  152. // Debug.Log("[AbstractLocationProvider]: Wait... " + maxWait);
  153. yield return new WaitForSeconds(1);
  154. maxWait--;
  155. UpdateLocationRequestStatus();
  156. }
  157. if (maxWait < 1)
  158. {
  159. // Debug.LogError("[AbstractLocationProvider]: Timed out.");
  160. LocationFailed?.Invoke("Timed out");
  161. yield break;
  162. }
  163. if (Status == LocationProviderStatus.Failed)
  164. {
  165. // Debug.LogError("[AbstractLocationProvider]: Falied to initialize location updates.");
  166. LocationFailed?.Invoke("Falied to initialize location updates.");
  167. yield break;
  168. }
  169. if (Status != LocationProviderStatus.Started)
  170. {
  171. // Debug.LogError("[AbstractLocationProvider]: Unknown error initializing location updates. " + Status);
  172. LocationFailed?.Invoke("Unknown error initializing location updates.");
  173. yield break;
  174. }
  175. // Debug.Log("[AbstractLocationProvider]: Started!");
  176. FirstReading = true;
  177. StartTime = Time.time;
  178. }
  179. public void ForceLocationUpdate()
  180. {
  181. LocationUpdated?.Invoke(CurrentLocation, LastLocation);
  182. LocationUpdatedRaw?.Invoke(CurrentLocationRaw, LastLocationRaw);
  183. }
  184. protected virtual void InnerOnEnabled()
  185. {
  186. }
  187. protected void EmitLocationUpdated()
  188. {
  189. LocationUpdated?.Invoke(CurrentLocation, LastLocation);
  190. }
  191. protected void EmitLocationUpdatedRaw()
  192. {
  193. LocationUpdatedRaw?.Invoke(CurrentLocationRaw, LastLocationRaw);
  194. }
  195. protected void EmitCompassUpdated()
  196. {
  197. CompassUpdated?.Invoke(CurrentHeading, LastHeading);
  198. }
  199. protected void UpdateLocation(LocationReading newLocation)
  200. {
  201. if (newLocation.timestamp == CurrentLocationRaw.timestamp)
  202. {
  203. return;
  204. }
  205. LastLocationRaw = CurrentLocationRaw;
  206. CurrentLocationRaw = newLocation;
  207. EmitLocationUpdatedRaw();
  208. if (!ShouldUpdateLocation(newLocation))
  209. {
  210. return;
  211. }
  212. LastLocation = CurrentLocation;
  213. CurrentLocation = newLocation;
  214. LocationUpdateCount++;
  215. EmitLocationUpdated();
  216. }
  217. protected void UpdateHeading(HeadingReading newHeading)
  218. {
  219. if (!ShouldUpdateHeading(newHeading))
  220. {
  221. return;
  222. }
  223. LastHeading = CurrentHeading;
  224. CurrentHeading = newHeading;
  225. EmitCompassUpdated();
  226. }
  227. protected bool ShouldUpdateHeading(HeadingReading newHeading)
  228. {
  229. if (newHeading.timestamp == CurrentHeading.timestamp)
  230. {
  231. return false;
  232. }
  233. return true;
  234. }
  235. protected bool ShouldUpdateLocation(LocationReading newLocation)
  236. {
  237. if (Paused)
  238. {
  239. return false;
  240. }
  241. if (newLocation.timestamp - CurrentLocation.timestamp < ((long) (Options.TimeBetweenUpdates * 1000)))
  242. {
  243. return false;
  244. }
  245. if (LocationReading.HorizontalDistance(newLocation, CurrentLocation) < Options.MinDistanceBetweenUpdates)
  246. {
  247. return false;
  248. }
  249. if ((newLocation.accuracy > Options.AccuracyRadius) && (Options.AccuracyRadius > 0))
  250. {
  251. return false;
  252. }
  253. return true;
  254. }
  255. public virtual void Update()
  256. {
  257. if (!HasStarted)
  258. {
  259. return;
  260. }
  261. var location = ReadLocation();
  262. var heading = ReadHeading();
  263. if (location == null || heading == null)
  264. {
  265. // Debug.Log("[AbstractLocationProvider]: Null reading");
  266. return;
  267. }
  268. if (FirstReading)
  269. {
  270. FirstLocation = location.Value;
  271. CurrentLocation = FirstLocation;
  272. CurrentLocationRaw = FirstLocation;
  273. CurrentHeading = heading.Value;
  274. IsEnabled = true;
  275. FirstReading = false;
  276. LocationEnabled?.Invoke();
  277. InnerOnEnabled();
  278. EmitCompassUpdated();
  279. EmitLocationUpdated();
  280. EmitLocationUpdatedRaw();
  281. return;
  282. }
  283. UpdateLocation(location.Value);
  284. UpdateHeading(heading.Value);
  285. }
  286. public void Restart()
  287. {
  288. LocationUpdateCount = 0;
  289. FirstReading = true;
  290. }
  291. public void ResetStartPoint()
  292. {
  293. FirstLocation = CurrentLocation;
  294. }
  295. public void SetCompassLowPassFactor(double factor)
  296. {
  297. LowPassFilterFactor = factor;
  298. }
  299. public string GetStatusString()
  300. {
  301. switch (Status)
  302. {
  303. case LocationProviderStatus.Idle:
  304. return "Idle";
  305. case LocationProviderStatus.Failed:
  306. return "Failed";
  307. case LocationProviderStatus.Initializing:
  308. return "Initializing";
  309. case LocationProviderStatus.Started:
  310. return "Started";
  311. }
  312. return "UnknownStatus";
  313. }
  314. public string GetInfoString()
  315. {
  316. return Name +
  317. "{ \n" +
  318. CurrentLocation + "\n" +
  319. CurrentHeading + "\n" +
  320. "Status = " + GetStatusString() + "\n" +
  321. "DistanceFromStartPoint = " + DistanceFromStartPoint + "\n" +
  322. "TimeSinceStart = " + (Time.time - StartTime) + "\n" +
  323. "}";
  324. }
  325. public void OnEnabled(LocationEnabledDelegate del)
  326. {
  327. LocationEnabled += del;
  328. if (IsEnabled)
  329. {
  330. del();
  331. }
  332. }
  333. public void OnFail(LocationFailedDelegate del)
  334. {
  335. LocationFailed += del;
  336. }
  337. /// <summary>
  338. /// Pauses location updates
  339. /// </summary>
  340. public void Pause()
  341. {
  342. Paused = true;
  343. }
  344. /// <summary>
  345. /// Resumes location updates
  346. /// </summary>
  347. public void Resume()
  348. {
  349. Paused = false;
  350. }
  351. }
  352. }