start method
void
start()
Starts gyroscope tracking. Calls calibrate automatically.
Implementation
void start() {
stop();
calibrate();
_lastTimestamp = null;
_prevPitchFused = null;
_prevRollFused = null;
_pitchFused = 0.0;
_rollFused = 0.0;
_gyroEventsCount = 0;
isGyroscopeActive = true;
_smoothPitch = 0.0;
_smoothRoll = 0.0;
_lastAccelPitch = null;
// Detect if gyroscope is present and active within 800ms
Future.delayed(const Duration(milliseconds: 800), () {
if (_gyroEventsCount == 0) {
isGyroscopeActive = false;
}
});
if (!useIsolate) {
// Main-thread Web/Sync Fallback
_accelSubscription =
(accelerometerStreamOverride ?? accelerometerEventStream()).listen((
event,
) {
_accelX = event.x;
_accelY = event.y;
_accelZ = event.z;
if (!isGyroscopeActive) {
_updateFromAccelerometerOnly(event.x, event.y, event.z);
}
});
_subscription = (gyroscopeStreamOverride ?? gyroscopeEventStream()).listen((
event,
) {
_gyroEventsCount++;
isGyroscopeActive = true;
if (_calibrating) {
_calibrationSumX += event.x;
_calibrationSumY += event.y;
_calibrationSamples++;
return;
}
final adjustedX = event.x - _offsetX;
final adjustedY = event.y - _offsetY;
final now = DateTime.now();
if (_lastTimestamp == null) {
_lastTimestamp = now;
final accelPitch = atan2(_accelY, _accelZ);
final accelRoll = atan2(
-_accelX,
sqrt(_accelY * _accelY + _accelZ * _accelZ),
);
_pitchFused = accelPitch;
_rollFused = accelRoll;
_prevPitchFused = accelPitch;
_prevRollFused = accelRoll;
return;
}
final dt = now.difference(_lastTimestamp!).inMicroseconds / 1000000.0;
_lastTimestamp = now;
final accelPitch = atan2(_accelY, _accelZ);
final accelRoll = atan2(
-_accelX,
sqrt(_accelY * _accelY + _accelZ * _accelZ),
);
_pitchFused =
_alpha * (_pitchFused + adjustedX * dt) + (1 - _alpha) * accelPitch;
_rollFused =
_alpha * (_rollFused + adjustedY * dt) + (1 - _alpha) * accelRoll;
final predictionTime = predictionMs / 1000.0;
final predictedPitch = _pitchFused + adjustedX * predictionTime;
final predictedRoll = _rollFused + adjustedY * predictionTime;
final dPitch = predictedPitch - (_prevPitchFused ?? predictedPitch);
final dRoll = predictedRoll - (_prevRollFused ?? predictedRoll);
_prevPitchFused = predictedPitch;
_prevRollFused = predictedRoll;
target.rotate(-dPitch * sensitivity, dRoll * sensitivity * 1.8);
});
return;
}
// Native Platform: Background Isolate Setup
_receivePort = ReceivePort();
Isolate.spawn(_isolateSensorFusion, _receivePort!.sendPort).then((iso) {
_isolate = iso;
});
_receivePort!.listen((message) {
if (message is SendPort) {
_isolateSendPort = message;
_isolateSendPort!.send([
0,
_alpha,
sensitivity,
predictionMs,
_offsetX,
_offsetY,
]);
} else if (message is List) {
final dYaw = message[0] as double;
final dPitch = message[1] as double;
target.rotate(dYaw, dPitch);
}
});
_accelSubscription =
(accelerometerStreamOverride ?? accelerometerEventStream()).listen((
event,
) {
_isolateSendPort?.send([1, event.x, event.y, event.z]);
if (!isGyroscopeActive) {
_updateFromAccelerometerOnly(event.x, event.y, event.z);
}
});
_subscription = (gyroscopeStreamOverride ?? gyroscopeEventStream()).listen((
event,
) {
_gyroEventsCount++;
isGyroscopeActive = true;
if (_calibrating) {
_calibrationSumX += event.x;
_calibrationSumY += event.y;
_calibrationSamples++;
return;
}
_isolateSendPort?.send([2, event.x, event.y, event.z]);
});
}