Skip to content

Commit 24a9bc3

Browse files
feat: implementation of the Compass page (#2710)
Co-authored-by: Marc Nause <[email protected]>
1 parent 66bb5e7 commit 24a9bc3

File tree

9 files changed

+438
-0
lines changed

9 files changed

+438
-0
lines changed

assets/icons/compass_icon.png

247 KB
Loading

ios/Runner/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
<string>Main</string>
3131
<key>NSMicrophoneUsageDescription</key>
3232
<string>App needs Microphone access to capture audio</string>
33+
<key>NSMotionUsageDescription</key>
34+
<string>This app uses motion sensors to determine compass direction.</string>
3335
<key>NSPhotoLibraryUsageDescription</key>
3436
<string>App needs access to photo library</string>
3537
<key>UISupportedInterfaceOrientations</key>

lib/l10n/app_en.arb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,12 @@
323323
"baroMeterBulletPoint2": "If you want to use the sensor BMP-180, connect the sensor to PSLab device as shown in the figure.",
324324
"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.",
325325
"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.",
326+
"magnetometerError" : "Magnetometer error:",
327+
"accelerometerError" : "Accelerometer error:",
328+
"compassTitle": "Compass",
329+
"parallelToGround": "Select axes parallel to ground",
330+
"sharingMessage": "Sharing PSLab Data",
331+
"delete": "Delete",
326332
"thermometerTitle" : "Thermometer",
327333
"thermometerIntro" : "Thermometer instrument is used to measure ambient temprature. It can be measured using inbuilt ambient temprature sensor or through SHT21.",
328334
"celsius": "°C",

lib/l10n/app_localizations.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2032,6 +2032,30 @@ abstract class AppLocalizations {
20322032
/// **'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.'**
20332033
String get baroMeterBulletPoint4;
20342034

2035+
/// No description provided for @magnetometerError.
2036+
///
2037+
/// In en, this message translates to:
2038+
/// **'Magnetometer error:'**
2039+
String get magnetometerError;
2040+
2041+
/// No description provided for @accelerometerError.
2042+
///
2043+
/// In en, this message translates to:
2044+
/// **'Accelerometer error:'**
2045+
String get accelerometerError;
2046+
2047+
/// No description provided for @compassTitle.
2048+
///
2049+
/// In en, this message translates to:
2050+
/// **'Compass'**
2051+
String get compassTitle;
2052+
2053+
/// No description provided for @parallelToGround.
2054+
///
2055+
/// In en, this message translates to:
2056+
/// **'Select axes parallel to ground'**
2057+
String get parallelToGround;
2058+
20352059
/// No description provided for @thermometerTitle.
20362060
///
20372061
/// In en, this message translates to:

lib/l10n/app_localizations_en.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,17 @@ class AppLocalizationsEn extends AppLocalizations {
10381038
'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.';
10391039

10401040
@override
1041+
String get magnetometerError => 'Magnetometer error:';
1042+
1043+
@override
1044+
String get accelerometerError => 'Accelerometer error:';
1045+
1046+
@override
1047+
String get compassTitle => 'Compass';
1048+
1049+
@override
1050+
String get parallelToGround => 'Select axes parallel to ground';
1051+
10411052
String get thermometerTitle => 'Thermometer';
10421053

10431054
@override

lib/main.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import 'package:pslab/view/sensors_screen.dart';
2020
import 'package:pslab/view/settings_screen.dart';
2121
import 'package:pslab/view/about_us_screen.dart';
2222
import 'package:pslab/view/software_licenses_screen.dart';
23+
import 'package:pslab/view/compass_screen.dart';
2324
import 'package:pslab/theme/app_theme.dart';
2425
import 'package:pslab/view/soundmeter_screen.dart';
2526
import 'package:pslab/view/thermometer_screen.dart';
@@ -70,6 +71,7 @@ class MyApp extends StatelessWidget {
7071
'/waveGenerator': (context) => const WaveGeneratorScreen(),
7172
'/logicAnalyzer': (context) => const LogicAnalyzerScreen(),
7273
'/powerSource': (context) => const PowerSourceScreen(),
74+
'/compass': (context) => const CompassScreen(),
7375
'/connectDevice': (context) => const ConnectDeviceScreen(),
7476
'/faq': (context) => FAQScreen(),
7577
'/settings': (context) => const SettingsScreen(),

lib/providers/compass_provider.dart

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import 'dart:async';
2+
import 'dart:math';
3+
import 'package:flutter/material.dart';
4+
import 'package:sensors_plus/sensors_plus.dart';
5+
import 'package:flutter/foundation.dart';
6+
import 'package:pslab/others/logger_service.dart';
7+
8+
import '../l10n/app_localizations.dart';
9+
import 'locator.dart';
10+
11+
class CompassProvider extends ChangeNotifier {
12+
AppLocalizations appLocalizations = getIt.get<AppLocalizations>();
13+
MagnetometerEvent _magnetometerEvent =
14+
MagnetometerEvent(0, 0, 0, DateTime.now());
15+
AccelerometerEvent _accelerometerEvent =
16+
AccelerometerEvent(0, 0, 0, DateTime.now());
17+
StreamSubscription? _magnetometerSubscription;
18+
StreamSubscription? _accelerometerSubscription;
19+
String _selectedAxis = 'X';
20+
double _currentDegree = 0.0;
21+
int _direction = 0;
22+
double _smoothedHeading = 0.0;
23+
24+
MagnetometerEvent get magnetometerEvent => _magnetometerEvent;
25+
AccelerometerEvent get accelerometerEvent => _accelerometerEvent;
26+
String get selectedAxis => _selectedAxis;
27+
double get currentDegree => _currentDegree;
28+
int get direction => _direction;
29+
double get smoothedHeading => _smoothedHeading;
30+
31+
void initializeSensors() {
32+
_magnetometerSubscription = magnetometerEventStream().listen(
33+
(event) {
34+
_magnetometerEvent = event;
35+
_updateCompassDirection();
36+
notifyListeners();
37+
},
38+
onError: (error) {
39+
logger.e("${appLocalizations.magnetometerError}: $error");
40+
},
41+
cancelOnError: false,
42+
);
43+
44+
_accelerometerSubscription = accelerometerEventStream().listen(
45+
(event) {
46+
_accelerometerEvent = event;
47+
_updateCompassDirection();
48+
notifyListeners();
49+
},
50+
onError: (error) {
51+
logger.e("${appLocalizations.accelerometerError}: $error");
52+
},
53+
cancelOnError: false,
54+
);
55+
}
56+
57+
void disposeSensors() {
58+
_magnetometerSubscription?.cancel();
59+
_accelerometerSubscription?.cancel();
60+
}
61+
62+
@override
63+
void dispose() {
64+
disposeSensors();
65+
super.dispose();
66+
}
67+
68+
void _updateCompassDirection() {
69+
double radians = _getRadiansForAxis(_selectedAxis);
70+
double degrees = radians * (180 / pi);
71+
if (degrees < 0) {
72+
degrees += 360;
73+
}
74+
75+
degrees = (degrees - 90) % 360;
76+
if (degrees < 0) {
77+
degrees += 360;
78+
}
79+
80+
const double alpha = 0.45;
81+
double angleDiff = degrees - _smoothedHeading;
82+
if (angleDiff > 180) {
83+
angleDiff -= 360;
84+
} else if (angleDiff < -180) {
85+
angleDiff += 360;
86+
}
87+
_smoothedHeading = _smoothedHeading + alpha * angleDiff;
88+
if (_smoothedHeading >= 360) {
89+
_smoothedHeading -= 360;
90+
} else if (_smoothedHeading < 0) {
91+
_smoothedHeading += 360;
92+
}
93+
switch (_selectedAxis) {
94+
case 'X':
95+
_currentDegree = -(_smoothedHeading * pi / 180);
96+
break;
97+
case 'Y':
98+
_currentDegree = ((_smoothedHeading - 10) * pi / 180);
99+
break;
100+
case 'Z':
101+
_currentDegree = -((_smoothedHeading + 90) * pi / 180);
102+
break;
103+
}
104+
}
105+
106+
double _getRadiansForAxis(String axis) {
107+
double ax = _accelerometerEvent.x;
108+
double ay = _accelerometerEvent.y;
109+
double az = _accelerometerEvent.z;
110+
double mx = _magnetometerEvent.x;
111+
double my = _magnetometerEvent.y;
112+
double mz = _magnetometerEvent.z;
113+
114+
double pitch = atan2(ay, sqrt(ax * ax + az * az));
115+
double roll = atan2(-ax, az);
116+
117+
double xH = mx * cos(pitch) + mz * sin(pitch);
118+
double yH = mx * sin(roll) * sin(pitch) +
119+
my * cos(roll) -
120+
mz * sin(roll) * cos(pitch);
121+
double zH = -mx * cos(roll) * sin(pitch) +
122+
my * sin(roll) +
123+
mz * cos(roll) * cos(pitch);
124+
125+
switch (axis) {
126+
case 'X':
127+
return atan2(yH, xH);
128+
case 'Y':
129+
return atan2(-xH, zH);
130+
case 'Z':
131+
return atan2(yH, -zH);
132+
default:
133+
return atan2(yH, xH);
134+
}
135+
}
136+
137+
double getDegreeForAxis(String axis) {
138+
double radians = _getRadiansForAxis(axis);
139+
double degree = radians * (180 / pi);
140+
141+
switch (axis) {
142+
case 'X':
143+
degree = (degree - 90) % 360;
144+
break;
145+
case 'Y':
146+
degree = (-degree + 100) % 360;
147+
break;
148+
case 'Z':
149+
degree = (degree + 90) % 360;
150+
break;
151+
}
152+
153+
return degree < 0 ? degree + 360 : degree;
154+
}
155+
156+
void onAxisSelected(String axis) {
157+
_selectedAxis = axis;
158+
switch (axis) {
159+
case 'X':
160+
_direction = 0;
161+
break;
162+
case 'Y':
163+
_direction = 1;
164+
break;
165+
case 'Z':
166+
_direction = 2;
167+
break;
168+
}
169+
notifyListeners();
170+
}
171+
}

0 commit comments

Comments
 (0)