Skip to content

Commit

Permalink
Merge pull request #40 from Crequency/dev=new-rotation
Browse files Browse the repository at this point in the history
Enhanced device sensors test page
  • Loading branch information
Dynesshely authored Mar 31, 2024
2 parents 99f9b30 + c583726 commit 52df417
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 122 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ Runs on `Android`, `iOS`

> **Warning**
>
> Please use physical devices to debug, any emulator may cause problems.
> We recommend that you use a physical device for debugging, you may experience some issues debugging on an emulator
>
> Android emulators are currently known to experience problems; the situation with iOS simulators is currently unknown.
> Known issues are as follows:
> 1. KitX Mobile will join the LAN multicast. If the emulator does not support this function, an error will occur.
> 2. The simulator does not provide sensor data, KitX Mobile will use random data to replace scenarios that require sensor data.
### Prerequisites

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'dart:async';
import 'dart:math';

import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sensors_plus/sensors_plus.dart';
Expand All @@ -15,22 +17,78 @@ class AccelerationDisplayStandState extends State<AccelerationDisplayStand> {
/// Acceleration x-axis, y-axis, z-axis
final accX = 0.0.obs, accY = 0.0.obs, accZ = 0.0.obs;

/// Sampling Rage
var samplingRate = 0.05.obs;

/// User accelerometer sensor data listener
StreamSubscription<UserAccelerometerEvent>? userAccelerometerDataListener;

/// Is listener paused, errored
var listenerPaused = false.obs, listnerErrored = false.obs;

/// Chart related data
var minX = 0.0.obs, maxX = 0.05.obs, xCount = 10.obs;

/// Acceleration x-axis, y-axis, z-axis values
var xValues = <FlSpot>[], yValues = <FlSpot>[], zValues = <FlSpot>[];

/// Random sensor data timer
Timer? randomSensorDataTimer;

@override
void initState() {
userAccelerometerDataListener = userAccelerometerEventStream(samplingPeriod: Duration(milliseconds: 50)).listen((event) {
accX.value = event.x;
accY.value = event.y;
accZ.value = event.z;
});
userAccelerometerDataListener = userAccelerometerEventStream(
samplingPeriod: Duration(milliseconds: (samplingRate * 1000).toInt()),
).listen(
(event) {
accX.value = event.x;
accY.value = event.y;
accZ.value = event.z;
maxX.value += samplingRate.value;
if ((maxX.value - minX.value) >= samplingRate.value * xCount.value) {
minX.value += samplingRate.value;
}
xValues.add(FlSpot(maxX.value, event.x));
yValues.add(FlSpot(maxX.value, event.y));
zValues.add(FlSpot(maxX.value, event.z));
if (xValues.length > xCount.value + 1) {
xValues.removeAt(0);
yValues.removeAt(0);
zValues.removeAt(0);
}
},
onError: (error) {
listnerErrored.value = true;

var random = Random(114514);

randomSensorDataTimer = Timer.periodic(Duration(milliseconds: (samplingRate * 1000).toInt()), (timer) {
accX.value = random.nextDouble() * 10 - 5;
accY.value = random.nextDouble() * 10 - 5;
accZ.value = random.nextDouble() * 10 - 5;
maxX.value += samplingRate.value;
if ((maxX.value - minX.value) >= samplingRate.value * xCount.value) {
minX.value += samplingRate.value;
}
xValues.add(FlSpot(maxX.value, accX.value));
yValues.add(FlSpot(maxX.value, accY.value));
zValues.add(FlSpot(maxX.value, accZ.value));
if (xValues.length > xCount.value + 1) {
xValues.removeAt(0);
yValues.removeAt(0);
zValues.removeAt(0);
}
});
},
cancelOnError: true,
);
super.initState();
}

@override
void dispose() {
userAccelerometerDataListener?.cancel();
randomSensorDataTimer?.cancel();
super.dispose();
}

Expand All @@ -42,14 +100,120 @@ class AccelerationDisplayStandState extends State<AccelerationDisplayStand> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Acceleration Data', style: TextStyle(fontSize: 32)),
Container(
height: 300,
margin: EdgeInsets.fromLTRB(0, 30, 20, 30),
child: Obx(
() => LineChart(
LineChartData(
minX: minX.value,
maxX: maxX.value,
minY: -5,
maxY: 5,
lineBarsData: [
LineChartBarData(
spots: xValues,
color: Colors.redAccent,
isStrokeCapRound: true,
isStrokeJoinRound: true,
),
LineChartBarData(
spots: yValues,
color: Colors.greenAccent,
isStrokeCapRound: true,
isStrokeJoinRound: true,
),
LineChartBarData(
spots: zValues,
color: Colors.blueAccent,
isStrokeCapRound: true,
isStrokeJoinRound: true,
),
],
titlesData: FlTitlesData(
show: true,
rightTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
topTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 40,
interval: 1,
getTitlesWidget: (a, b) => Padding(
padding: EdgeInsets.only(top: 10),
child: Text(a.toStringAsFixed(3).toString()),
),
),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
interval: 1,
getTitlesWidget: (a, b) => Text(a.toInt().toString()),
reservedSize: 30,
),
),
),
gridData: FlGridData(
show: true,
drawVerticalLine: true,
horizontalInterval: 1,
verticalInterval: samplingRate.value,
getDrawingHorizontalLine: (value) {
return const FlLine(
color: Colors.indigo,
strokeWidth: 1,
);
},
getDrawingVerticalLine: (value) {
return const FlLine(
color: Colors.indigo,
strokeWidth: 1,
);
},
),
borderData: FlBorderData(
show: true,
border: Border.all(color: Colors.indigo),
),
),
duration: const Duration(milliseconds: 0),
),
),
),
Obx(
() => Text('${accX > 0 ? "⏪" : "⏩"} \tx: ${accX.value}', style: TextStyle(fontSize: 16, color: Colors.redAccent)),
),
Obx(
() => Text('${accY > 0 ? "⏬" : "⏫"} \ty: ${accY.value}', style: TextStyle(fontSize: 16, color: Colors.greenAccent)),
),
Obx(
() => Text('x: ${accX.value}', style: TextStyle(fontSize: 14)),
() => Text('${accZ > 0 ? "⬇" : "⬆"} \tz: ${accZ.value}', style: TextStyle(fontSize: 16, color: Colors.blueAccent)),
),
Obx(
() => Text('y: ${accY.value}', style: TextStyle(fontSize: 14)),
() => Text('\tSampling Rate: ${samplingRate.value} s', style: TextStyle(fontSize: 16)),
),
const Text('↔ \tUnit: m/s^2', style: TextStyle(fontSize: 16)),
const SizedBox(height: 20),
Obx(
() => Text('z: ${accZ.value}', style: TextStyle(fontSize: 14)),
() => listnerErrored.value
? const Text('No sensor data, you are seeing random data.')
: ElevatedButton(
onPressed: () {
if (userAccelerometerDataListener?.isPaused ?? true) {
userAccelerometerDataListener?.resume();
listenerPaused.value = false;
} else {
userAccelerometerDataListener?.pause();
listenerPaused.value = true;
}
},
child: listenerPaused.value ? const Text('Resume') : const Text("Pause"),
),
),
],
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:get/get.dart';
Expand All @@ -17,46 +18,62 @@ class GyroscopeDisplayStandState extends State<GyroscopeDisplayStand> {
/// Gyroscope x-axis, y-axis, z-axis
final dirX = 0.0.obs, dirY = 0.0.obs, dirZ = 0.0.obs;

/// Gyroscope direction x, y, z
final directionX = 'none'.obs, directionY = 'none'.obs, directionZ = 'none'.obs;

/// Drawing canvas width
static double canvasWidth = 400;

/// Drawing canvas height
static double canvasHeight = 300;
/// Drawing canvas size
static double canvasWidth = 400, canvasHeight = 300;

/// Is drawing paused
var rotationPaused = false.obs;
/// Is listener errored
var rotationPaused = false.obs, listenerErrored = false.obs;

/// Sampling Rage
var samplingRate = 0.05.obs;

/// Gyroscope sensor data listener
StreamSubscription<GyroscopeEvent>? gyroscopeDataListener;

/// Painter
Painter painter = Painter();

/// Random sensor data timer
Timer? randomSensorDataTimer;

@override
void initState() {
painter.initialize();

gyroscopeDataListener = gyroscopeEventStream(samplingPeriod: Duration(milliseconds: 50)).listen((event) {
DeviceRotationHost.rotateWithAcceleration(event.x, event.y, event.z, 0.05);
gyroscopeDataListener = gyroscopeEventStream(
samplingPeriod: Duration(milliseconds: (samplingRate.value * 1000).toInt()),
).listen(
(event) {
DeviceRotationHost.rotateWithAcceleration(event.x, event.y, event.z, samplingRate.value);

dirX.value = event.x;
dirY.value = event.y;
dirZ.value = event.z;
},
onError: (error) {
listenerErrored.value = true;

var random = Random(114514);

dirX.value = event.x;
dirY.value = event.y;
dirZ.value = event.z;
randomSensorDataTimer = Timer.periodic(Duration(milliseconds: 50), (timer) {
var rad = 0.5 + random.nextDouble() / 10;

directionX.value = dirX >= 0 ? '⇊' : '⇈';
directionY.value = dirY >= 0 ? '↻' : '↺';
directionZ.value = dirZ >= 0 ? '↶' : '↷';
});
dirY.value = rad;

DeviceRotationHost.rotateWithAcceleration(dirX.value, dirY.value, dirZ.value, samplingRate.value);
});
},
cancelOnError: true,
);

super.initState();
}

@override
void dispose() {
gyroscopeDataListener?.cancel();
randomSensorDataTimer?.cancel();
super.dispose();
}

Expand Down Expand Up @@ -117,14 +134,20 @@ class GyroscopeDisplayStandState extends State<GyroscopeDisplayStand> {
],
),
Obx(
() => Text('${directionX.value} x: ${dirX.value}', style: TextStyle(fontSize: 16)),
() => Text('${dirX >= 0 ? '⏬' : '⏫'} \tx: ${dirX.value}', style: TextStyle(fontSize: 16)),
),
Obx(
() => Text('${directionY.value} y: ${dirY.value}', style: TextStyle(fontSize: 16)),
() => Text('${dirY >= 0 ? '⤵' : '⤴'} \ty: ${dirY.value}', style: TextStyle(fontSize: 16)),
),
Obx(
() => Text('${directionZ.value} z: ${dirZ.value}', style: TextStyle(fontSize: 16)),
() => Text('${dirZ >= 0 ? '⏪' : '⏩'} \tz: ${dirZ.value}', style: TextStyle(fontSize: 16)),
),
Obx(
() => Text('⏱ \tSampling Rate: ${samplingRate.value} s', style: TextStyle(fontSize: 16)),
),
const Text('↔ \tUnit: rad/s', style: TextStyle(fontSize: 16)),
const SizedBox(height: 20),
Obx(() => listenerErrored.value ? const Text('No sensor data, you are seeing random data.') : const SizedBox())
],
),
);
Expand All @@ -142,22 +165,19 @@ class Painter extends CustomPainter {
/// Return absolute value
double abs(double num) => num >= 0 ? num : -num;

/// Yaw - Pitch - Roll
vector_math.Vector3 getRotationAngles() => DeviceRotationHost.getRotationAngles();

/// Initialize
void initialize() {
DeviceRotationHost.axis = vector_math.Vector3(objectWidth / 2, 0, 0);
DeviceRotationHost.ayis = vector_math.Vector3(0, objectHeight / 2, 0);
DeviceRotationHost.azis = vector_math.Vector3(0, 0, 1);
DeviceRotationHost.xDir = vector_math.Quaternion(objectWidth / 2, 0, 0, 0);
DeviceRotationHost.yDir = vector_math.Quaternion(0, objectHeight / 2, 0, 0);
DeviceRotationHost.zDir = vector_math.Quaternion(0, 0, 1, 0);

DeviceRotationHost.setPoints([
vector_math.Vector3(-objectWidth / 2, objectHeight / 2, 0),
vector_math.Vector3(objectWidth / 2, objectHeight / 2, 0),
vector_math.Vector3(objectWidth / 2, -objectHeight / 2, 0),
vector_math.Vector3(-objectWidth / 2, -objectHeight / 2, 0),
vector_math.Vector3(-objectWidth / 2 + objectWidth / 4, -objectHeight / 2 + objectHeight / 24, 0),
vector_math.Vector3(objectWidth / 2 - objectWidth / 4, -objectHeight / 2 + objectHeight / 24, 0),
vector_math.Quaternion(-objectWidth / 2, objectHeight / 2, 0, 0),
vector_math.Quaternion(objectWidth / 2, objectHeight / 2, 0, 0),
vector_math.Quaternion(objectWidth / 2, -objectHeight / 2, 0, 0),
vector_math.Quaternion(-objectWidth / 2, -objectHeight / 2, 0, 0),
vector_math.Quaternion(-objectWidth / 2 + objectWidth / 4, -objectHeight / 2 + objectHeight / 24, 0, 0),
vector_math.Quaternion(objectWidth / 2 - objectWidth / 4, -objectHeight / 2 + objectHeight / 24, 0, 0),
]);
}

Expand All @@ -168,7 +188,7 @@ class Painter extends CustomPainter {
List<vector_math.Vector3> displayPoints = [];

for (int i = 0; i < rotatedPoints.length; ++i) {
displayPoints.add(getCrossPoint(rotatedPoints[i], camera, null, null) ?? vector_math.Vector3(0, 0, 0));
displayPoints.add(getCrossPoint(rotatedPoints[i].toPoint(), camera, null, null) ?? vector_math.Vector3(0, 0, 0));
}

return displayPoints;
Expand All @@ -185,11 +205,14 @@ class Painter extends CustomPainter {

@override
void paint(Canvas canvas, Size size) {
var angles = getRotationAngles();
// var angles = getRotationAngles();
var points = getPoints();
var isBack = abs(angles.y) > 90 || abs(angles.z) > 90;
// var isBack = abs(angles.y) > 90 || abs(angles.z) > 90;
// var paint = Paint()
// ..color = isBack ? Colors.blue : Colors.red
// ..strokeWidth = 1.0;
var paint = Paint()
..color = isBack ? Colors.blue : Colors.red
..color = Colors.blue
..strokeWidth = 1.0;

var a = points[0];
Expand Down
Loading

0 comments on commit 52df417

Please sign in to comment.