Skip to content

feat: implementation of the Compass page #2710

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: flutter
Choose a base branch
from
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
Binary file added assets/icons/compass_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
<string>Main</string>
<key>NSMicrophoneUsageDescription</key>
<string>App needs Microphone access to capture audio</string>
<key>NSMotionUsageDescription</key>
<string>This app uses motion sensors to determine compass direction.</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
Expand Down
6 changes: 5 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -305,5 +305,9 @@
"baroMeterBulletPoint1": "The Barometer can be used to measure Atmospheric pressure. This instrument is compatible with either the built in pressure sensor on any android device or the BMP-180 pressure sensor",
"baroMeterBulletPoint2": "If you want to use the sensor BMP-180, connect the sensor to PSLab device as shown in the figure.",
"baroMeterBulletPoint3": "The above pin configuration has to be same except for the pin GND. GND is meant for Ground and any of the PSLab device GND pins can be used since they are common.",
"baroMeterBulletPoint4": "Select the sensor by going to the Configure tab from the bottom navigation bar and choose BMP-180 in the drop down menu under Select Sensor."
"baroMeterBulletPoint4": "Select the sensor by going to the Configure tab from the bottom navigation bar and choose BMP-180 in the drop down menu under Select Sensor.",
"magnetometerError" : "Magnetometer error:",
"accelerometerError" : "Accelerometer error:",
"compassTitle": "Compass",
"parallelToGround": "Select axes parallel to ground"
}
24 changes: 24 additions & 0 deletions lib/l10n/app_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1929,6 +1929,30 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Select the sensor by going to the Configure tab from the bottom navigation bar and choose BMP-180 in the drop down menu under Select Sensor.'**
String get baroMeterBulletPoint4;

/// No description provided for @magnetometerError.
///
/// In en, this message translates to:
/// **'Magnetometer error:'**
String get magnetometerError;

/// No description provided for @accelerometerError.
///
/// In en, this message translates to:
/// **'Accelerometer error:'**
String get accelerometerError;

/// No description provided for @compassTitle.
///
/// In en, this message translates to:
/// **'Compass'**
String get compassTitle;

/// No description provided for @parallelToGround.
///
/// In en, this message translates to:
/// **'Select axes parallel to ground'**
String get parallelToGround;
}

class _AppLocalizationsDelegate
Expand Down
12 changes: 12 additions & 0 deletions lib/l10n/app_localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -985,4 +985,16 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get baroMeterBulletPoint4 =>
'Select the sensor by going to the Configure tab from the bottom navigation bar and choose BMP-180 in the drop down menu under Select Sensor.';

@override
String get magnetometerError => 'Magnetometer error:';

@override
String get accelerometerError => 'Accelerometer error:';

@override
String get compassTitle => 'Compass';

@override
String get parallelToGround => 'Select axes parallel to ground';
}
2 changes: 2 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'package:pslab/view/robotic_arm_screen.dart';
import 'package:pslab/view/settings_screen.dart';
import 'package:pslab/view/about_us_screen.dart';
import 'package:pslab/view/software_licenses_screen.dart';
import 'package:pslab/view/compass_screen.dart';
import 'package:pslab/theme/app_theme.dart';
import 'package:pslab/view/soundmeter_screen.dart';
import 'constants.dart';
Expand Down Expand Up @@ -58,6 +59,7 @@ class MyApp extends StatelessWidget {
routes: {
'/': (context) => const InstrumentsScreen(),
'/oscilloscope': (context) => const OscilloscopeScreen(),
'/compass': (context) => const CompassScreen(),
'/multimeter': (context) => const MultimeterScreen(),
'/logicAnalyzer': (context) => const LogicAnalyzerScreen(),
'/connectDevice': (context) => const ConnectDeviceScreen(),
Expand Down
171 changes: 171 additions & 0 deletions lib/providers/compass_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:sensors_plus/sensors_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:pslab/others/logger_service.dart';

import '../l10n/app_localizations.dart';
import 'locator.dart';

class CompassProvider extends ChangeNotifier {
AppLocalizations appLocalizations = getIt.get<AppLocalizations>();
MagnetometerEvent _magnetometerEvent =
MagnetometerEvent(0, 0, 0, DateTime.now());
AccelerometerEvent _accelerometerEvent =
AccelerometerEvent(0, 0, 0, DateTime.now());
StreamSubscription? _magnetometerSubscription;
StreamSubscription? _accelerometerSubscription;
String _selectedAxis = 'X';
double _currentDegree = 0.0;
int _direction = 0;
double _smoothedHeading = 0.0;

MagnetometerEvent get magnetometerEvent => _magnetometerEvent;
AccelerometerEvent get accelerometerEvent => _accelerometerEvent;
String get selectedAxis => _selectedAxis;
double get currentDegree => _currentDegree;
int get direction => _direction;
double get smoothedHeading => _smoothedHeading;

void initializeSensors() {
_magnetometerSubscription = magnetometerEventStream().listen(
(event) {
_magnetometerEvent = event;
_updateCompassDirection();
notifyListeners();
},
onError: (error) {
logger.e("${appLocalizations.magnetometerError}: $error");
},
cancelOnError: false,
);

_accelerometerSubscription = accelerometerEventStream().listen(
(event) {
_accelerometerEvent = event;
_updateCompassDirection();
notifyListeners();
},
onError: (error) {
logger.e("${appLocalizations.accelerometerError}: $error");
},
cancelOnError: false,
);
}

void disposeSensors() {
_magnetometerSubscription?.cancel();
_accelerometerSubscription?.cancel();
}

@override
void dispose() {
disposeSensors();
super.dispose();
}

void _updateCompassDirection() {
double radians = _getRadiansForAxis(_selectedAxis);
double degrees = radians * (180 / pi);
if (degrees < 0) {
degrees += 360;
}

degrees = (degrees - 90) % 360;
if (degrees < 0) {
degrees += 360;
}

const double alpha = 0.45;
double angleDiff = degrees - _smoothedHeading;
if (angleDiff > 180) {
angleDiff -= 360;
} else if (angleDiff < -180) {
angleDiff += 360;
}
_smoothedHeading = _smoothedHeading + alpha * angleDiff;
if (_smoothedHeading >= 360) {
_smoothedHeading -= 360;
} else if (_smoothedHeading < 0) {
_smoothedHeading += 360;
}
switch (_selectedAxis) {
case 'X':
_currentDegree = -(_smoothedHeading * pi / 180);
break;
case 'Y':
_currentDegree = ((_smoothedHeading - 10) * pi / 180);
break;
case 'Z':
_currentDegree = -((_smoothedHeading + 90) * pi / 180);
break;
}
}

double _getRadiansForAxis(String axis) {
double ax = _accelerometerEvent.x;
double ay = _accelerometerEvent.y;
double az = _accelerometerEvent.z;
double mx = _magnetometerEvent.x;
double my = _magnetometerEvent.y;
double mz = _magnetometerEvent.z;

double pitch = atan2(ay, sqrt(ax * ax + az * az));
double roll = atan2(-ax, az);

double xH = mx * cos(pitch) + mz * sin(pitch);
double yH = mx * sin(roll) * sin(pitch) +
my * cos(roll) -
mz * sin(roll) * cos(pitch);
double zH = -mx * cos(roll) * sin(pitch) +
my * sin(roll) +
mz * cos(roll) * cos(pitch);

switch (axis) {
case 'X':
return atan2(yH, xH);
case 'Y':
return atan2(-xH, zH);
case 'Z':
return atan2(yH, -zH);
default:
return atan2(yH, xH);
}
}

double getDegreeForAxis(String axis) {
double radians = _getRadiansForAxis(axis);
double degree = radians * (180 / pi);

switch (axis) {
case 'X':
degree = (degree - 90) % 360;
break;
case 'Y':
degree = (-degree + 100) % 360;
break;
case 'Z':
degree = (degree + 90) % 360;
break;
}

return degree < 0 ? degree + 360 : degree;
}

void onAxisSelected(String axis) {
_selectedAxis = axis;
switch (axis) {
case 'X':
_direction = 0;
break;
case 'Y':
_direction = 1;
break;
case 'Z':
_direction = 2;
break;
}
notifyListeners();
}
}
Loading
Loading