Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 119 additions & 18 deletions lib/sensors/overtaking_prediction_sensor.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:fl_chart/fl_chart.dart';
import 'package:sensebox_bike/blocs/ble_bloc.dart';
import 'package:sensebox_bike/blocs/geolocation_bloc.dart';
import 'package:sensebox_bike/sensors/sensor.dart';
Expand All @@ -7,7 +8,11 @@ import 'package:sensebox_bike/ui/widgets/sensor/sensor_card.dart';
import 'package:sensebox_bike/utils/sensor_utils.dart';

class OvertakingPredictionSensor extends Sensor {
List<double> _latestPrediction = [0.0];
double _latestCarPrediction = 0.0;
double _latestBikePrediction = 0.0;
final _carPredictions = <FlSpot>[];
final _bikePredictions = <FlSpot>[];
double currTime = 0.0;

@override
get uiPriority => 40;
Expand All @@ -24,40 +29,136 @@ class OvertakingPredictionSensor extends Sensor {
void onDataReceived(List<double> data) {
super.onDataReceived(data); // Call the parent class to handle buffering
if (data.isNotEmpty) {
_latestPrediction =
data; // Assuming the first value is the prediction score
currTime = DateTime.now().millisecondsSinceEpoch.toDouble();
_latestCarPrediction = data[0];
_latestBikePrediction = data[1];
while (_carPredictions.length > 0 &&
_carPredictions[0].x < currTime - 3000) {
_carPredictions.removeAt(0);
_bikePredictions.removeAt(0);
}
_carPredictions.add(FlSpot(currTime, _latestCarPrediction));
_bikePredictions.add(FlSpot(currTime, _latestBikePrediction));
}
}

@override
List<double> aggregateData(List<List<double>> valueBuffer) {
List<double> myValues = valueBuffer.map((e) => e[0]).toList();
// Example aggregation logic: calculating the mean prediction score
return [myValues.reduce((a, b) => a + b) / myValues.length];
List<double> sumValues = [0.0, 0.0, 0.0];
int count = valueBuffer.length;

for (var values in valueBuffer) {
sumValues[0] += values[0];
sumValues[1] += values[1];
sumValues[1] += 1.0 - values[0] - values[1];
}

// Calculate the mean for asphalt, compacted, paving, sett, and standing
return sumValues.map((value) => value / count).toList();
}

Widget _buildLegendEntry(
String title, Color color, double value, BuildContext context) {
return Row(children: [
Container(
height: 16,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: color,
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 0.5),
child: Text(value.toStringAsFixed(1),
style: Theme.of(context).textTheme.labelSmall),
),
),
const SizedBox(width: 8),
Text(title)
]);
}

@override
Widget buildWidget() {
return StreamBuilder<List<double>>(
stream: valueStream,
initialData: _latestPrediction,
stream: valueStream
.map((event) => [event[0], event[1], event[2], event[3], event[4]]),
initialData: [
_latestCarPrediction,
_latestBikePrediction,
],
builder: (context, snapshot) {
double displayValue = snapshot.data?[0] ?? _latestPrediction[0];

List<double> displayValues = snapshot.data ??
[
_latestCarPrediction,
_latestBikePrediction,
];
return SensorCard(
title: "Overtaking",
icon: getSensorIcon(title),
color: getSensorColor(title),
child: Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
child: Column(
children: [
Text(
displayValue.toStringAsFixed(1),
style: const TextStyle(fontSize: 48),
const SizedBox(
height: 2,
),
const Text(
'%',
for (int i = 0; i < displayValues.length; i++)
_buildLegendEntry(
["Car", "Bike"][i],
[
Colors.red,
Colors.blue,
][i],
displayValues[i],
context),
SizedBox(
width: double.infinity,
height: 80,
child: LineChart(
LineChartData(
minY: 0,
maxY: 1,
minX: _carPredictions.length > 0
? _carPredictions.first.x
: 0.0,
maxX: _carPredictions.length > 0
? _carPredictions.last.x
: 0.0,
clipData: const FlClipData.all(),
gridData: FlGridData(
show: true,
drawVerticalLine: false,
horizontalInterval: 0.25,
),
borderData: FlBorderData(show: false),
lineBarsData: [
LineChartBarData(
spots: _carPredictions,
dotData: const FlDotData(
show: false,
),
color: Colors.red,
barWidth: 4,
isCurved: true,
isStrokeCapRound: true,
),
LineChartBarData(
spots: _bikePredictions,
dotData: const FlDotData(
show: false,
),
color: Colors.blue,
barWidth: 4,
isCurved: true,
isStrokeCapRound: true,
)
],
titlesData: const FlTitlesData(
show: false,
),
),
duration: Duration(milliseconds: 0),
curve: Curves.linear,
),
),
],
));
Expand Down
23 changes: 15 additions & 8 deletions lib/services/opensensemap_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -306,61 +306,68 @@ class OpenSenseMapService {
{
"id": "6",
"icon": 'osem-shock',
"title": 'Overtaking Manoeuvre',
"title": 'Overtaking Car',
"unit": '%',
"sensorType": 'VL53L8CX'
},
{
"id": "7",
"icon": 'osem-shock',
"title": 'Overtaking Bike',
"unit": '%',
"sensorType": 'VL53L8CX'
},
{
"id": "8",
"icon": 'osem-shock',
"title": 'Overtaking Distance',
"unit": 'cm',
"sensorType": 'VL53L8CX'
},
{
"id": "8",
"id": "9",
"icon": 'osem-shock',
"title": 'Surface Asphalt',
"unit": '%',
"sensorType": 'MPU-6050'
},
{
"id": "9",
"id": "10",
"icon": 'osem-shock',
"title": 'Surface Sett',
"unit": '%',
"sensorType": 'MPU-6050'
},
{
"id": "10",
"id": "11",
"icon": 'osem-shock',
"title": 'Surface Compacted',
"unit": '%',
"sensorType": 'MPU-6050'
},
{
"id": "11",
"id": "12",
"icon": 'osem-shock',
"title": 'Surface Paving',
"unit": '%',
"sensorType": 'MPU-6050'
},
{
"id": "12",
"id": "13",
"icon": 'osem-shock',
"title": 'Standing',
"unit": '%',
"sensorType": 'MPU-6050'
},
{
"id": "13",
"id": "14",
"icon": 'osem-shock',
"title": 'Surface Anomaly',
"unit": 'Δ',
"sensorType": 'MPU-6050'
},
{
"id": "14",
"id": "15",
"icon": 'osem-dashboard',
"title": 'Speed',
"unit": 'm/s',
Expand Down
1 change: 1 addition & 0 deletions lib/ui/screens/track_detail_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ class _TrackDetailScreenState extends State<TrackDetailScreen> {
'humidity',
'distance',
'overtaking',
'overtaking_bike',
'surface_classification_asphalt',
'surface_classification_compacted',
'surface_classification_paving',
Expand Down
6 changes: 4 additions & 2 deletions lib/utils/sensor_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ String? getTitleFromSensorKey(String key, String? attribute) {
return 'Finedust PM1';
case 'distance':
return 'Overtaking Distance';
case 'overtaking':
return 'Overtaking Manoeuvre';
case 'overtaking_car':
return 'Overtaking Car';
case 'overtaking_bike':
return 'Overtaking Bike';
case 'surface_classification_asphalt':
return 'Surface Asphalt';
case 'surface_classification_sett':
Expand Down