215 lines
6.6 KiB
Dart
215 lines
6.6 KiB
Dart
|
|
import 'dart:async';
|
|
import 'dart:math';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:bus_infotainment/backend/live_information.dart';
|
|
import 'package:bus_infotainment/backend/modules/info_module.dart';
|
|
import 'package:bus_infotainment/utils/OrdinanceSurveyUtils.dart';
|
|
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:geolocator/geolocator.dart';
|
|
import 'package:vector_math/vector_math.dart';
|
|
|
|
import '../../tfl_datasets.dart';
|
|
|
|
class TrackerModule extends InfoModule {
|
|
|
|
// Constructor
|
|
TrackerModule() {
|
|
locationStream();
|
|
if (!kIsWeb)
|
|
{
|
|
Geolocator.getLastKnownPosition().then((Position? position) {
|
|
this._position = position;
|
|
updateNearestStop();
|
|
});
|
|
}
|
|
liveInformation.routeVariantDelegate.addListener((routeVariant) {
|
|
print("Route variant changed");
|
|
updateNearestStop();
|
|
});
|
|
}
|
|
|
|
// Location Tracker - will update the recorded location when the user moves.
|
|
Position? _position;
|
|
Position? get position => _position;
|
|
|
|
StreamSubscription<Position> locationStream() => Geolocator.getPositionStream(
|
|
locationSettings: const LocationSettings(
|
|
accuracy: LocationAccuracy.high,
|
|
distanceFilter: 1,
|
|
)
|
|
).listen((Position position) {
|
|
|
|
if (position == null) {
|
|
return;
|
|
}
|
|
|
|
this._position = position;
|
|
updateNearestStop();
|
|
});
|
|
|
|
// Location Refresher - will update the recorded location periodically.
|
|
Timer refreshTimer() => Timer.periodic(Duration(seconds: 1), (timer) async {
|
|
_position = await Geolocator.getCurrentPosition();
|
|
});
|
|
|
|
BusRouteStop? nearestStop;
|
|
bool hasArrived = false;
|
|
|
|
|
|
Future<void> updateNearestStop() async {
|
|
if (liveInformation.getRouteVariant() == null) {
|
|
return;
|
|
}
|
|
|
|
// Get the closest stop
|
|
BusRouteStop closestStop = liveInformation.getRouteVariant()!.busStops.first;
|
|
double closestDistance = OSGrid
|
|
.toNorthingEasting(_position!.latitude, _position!.longitude)
|
|
.distanceTo(Vector2(closestStop.easting.toDouble(), closestStop.northing.toDouble()));
|
|
|
|
for (BusRouteStop stop in liveInformation.getRouteVariant()!.busStops) {
|
|
double distance = OSGrid
|
|
.toNorthingEasting(_position!.latitude, _position!.longitude)
|
|
.distanceTo(Vector2(stop.easting.toDouble(), stop.northing.toDouble()));
|
|
|
|
if (distance < closestDistance) {
|
|
closestStop = stop;
|
|
closestDistance = distance;
|
|
}
|
|
}
|
|
|
|
double relativeDistance = _calculateRelativeDistance(closestStop, _position!.latitude, _position!.longitude);
|
|
|
|
|
|
|
|
if (relativeDistance < -10) {
|
|
print("Closest stop is behind us: ${closestStop.formattedStopName}");
|
|
print("Relative distance: $relativeDistance");
|
|
|
|
int stopIndex = liveInformation.getRouteVariant()!.busStops.indexOf(closestStop);
|
|
|
|
int maxStops = liveInformation.getRouteVariant()!.busStops.length;
|
|
|
|
closestStop = liveInformation.getRouteVariant()!.busStops[min(stopIndex + 1, maxStops)];
|
|
|
|
print("Closest stop is now: ${closestStop.formattedStopName}");
|
|
} else {
|
|
print("Closest stop is in front of us: ${closestStop.formattedStopName}");
|
|
}
|
|
|
|
bool preExisting = true;
|
|
|
|
if (nearestStop != closestStop) {
|
|
nearestStop = closestStop;
|
|
hasArrived = false;
|
|
preExisting = false;
|
|
}
|
|
|
|
print("Closest stop is the same as before");
|
|
|
|
double distance = OSGrid
|
|
.toNorthingEasting(_position!.latitude, _position!.longitude)
|
|
.distanceTo(Vector2(nearestStop!.easting.toDouble(), nearestStop!.northing.toDouble()));
|
|
|
|
// get the speed in mph
|
|
double speed = _position!.speed * 2.23694;
|
|
print("Speed: $speed");
|
|
|
|
Duration? duration;
|
|
{
|
|
// Testing some audio stuff
|
|
Uint8List? audioBytes = liveInformation.announcementModule.announcementCache[nearestStop!.getAudioFileName()];
|
|
|
|
if (audioBytes != null) {
|
|
AudioWrapperByteSource audioSource = AudioWrapperByteSource(audioBytes!);
|
|
|
|
AudioWrapper audio = AudioWrapper();
|
|
|
|
await audio.loadSource(audioSource);
|
|
|
|
duration = await audio.getDuration();
|
|
|
|
print("Duration of audio: $duration");
|
|
} else {
|
|
print("Audio bytes are null");
|
|
duration = Duration(seconds: 0);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// get the estimated distance travelled in 5 seconds, in meters
|
|
double distanceTravelled = speed * (3 + duration!.inSeconds);
|
|
|
|
// adjust for the it takes to send the announcement to other devices
|
|
distance -= distanceTravelled;
|
|
|
|
// get the time to the stop in seconds
|
|
double timeToStop = distance / speed;
|
|
|
|
print("Distance to stop: $distance");
|
|
print("Time to stop: $timeToStop");
|
|
|
|
int secondsBefore = 7;
|
|
|
|
print("Seconds before: $secondsBefore");
|
|
|
|
if ((timeToStop < secondsBefore ) && !hasArrived && relativeDistance > 0) {
|
|
print("We are at the stop");
|
|
hasArrived = true;
|
|
liveInformation.announcementModule.queueAnnounceByAudioName(
|
|
displayText: "${nearestStop!.formattedStopName}",
|
|
audioNames: [
|
|
// "A_NEXT_STOP_001.mp3",
|
|
nearestStop!.getAudioFileName()
|
|
],
|
|
sendToServer: true
|
|
);
|
|
}
|
|
|
|
|
|
if (!hasArrived && !preExisting) {
|
|
liveInformation.announcementModule.queueAnnounceByAudioName(
|
|
displayText: "${closestStop.formattedStopName}",
|
|
audioNames: [],
|
|
sendToServer: true
|
|
);
|
|
}
|
|
|
|
|
|
|
|
print("Closest stop: ${closestStop.formattedStopName} in ${closestDistance.round()} meters");
|
|
}
|
|
|
|
}
|
|
|
|
double _calculateRelativeDistance(BusRouteStop stop, double latitude, double longitude) {
|
|
|
|
List<double> toLatLong = OSGrid.toLatLong(stop.northing.toDouble(), stop.easting.toDouble());
|
|
|
|
Vector2 stopPoint = OSGrid.toNorthingEasting(toLatLong[0], toLatLong[1]);
|
|
Vector2 currentPoint = OSGrid.toNorthingEasting(latitude, longitude);
|
|
|
|
// calculate the heading from the current point to the stop point
|
|
// 0 degrees is north, 90 degrees is east, 180 degrees is south, 270 degrees is west
|
|
double toHeading = degrees(atan2(stopPoint.x - currentPoint.x, stopPoint.y - currentPoint.y));
|
|
toHeading = (toHeading + 360) % 360;
|
|
|
|
// get the dot product of the heading and the stop heading
|
|
double dotProduct = cos(radians(toHeading)) * cos(radians(stop.heading.toDouble())) + sin(radians(toHeading)) * sin(radians(stop.heading.toDouble()));
|
|
|
|
return (dotProduct.sign) * _calculateDistance(latitude, longitude, toLatLong[0], toLatLong[1]);
|
|
}
|
|
|
|
double _calculateDistance(double lat1, double lon1, double lat2, double lon2) {
|
|
|
|
// Convert to eastings and northings
|
|
Vector2 point1 = OSGrid.toNorthingEasting(lat1, lon1);
|
|
Vector2 point2 = OSGrid.toNorthingEasting(lat2, lon2);
|
|
|
|
return point1.distanceTo(point2);
|
|
|
|
} |