addGyroscope method
Uses acquisition time, never callback delivery time. Duplicate/out-of- order samples are ignored; gaps over 250 ms restart integration instead of extrapolating a resumed sensor across an unknown interval.
Implementation
bool addGyroscope(double x, double y, int timestampUs) {
dYaw = 0;
dPitch = 0;
absolutePitch = null;
if (!x.isFinite || !y.isFinite) return false;
final previous = _lastTimestampUs;
if (previous != null && timestampUs <= previous) return false;
if (previous != null && timestampUs - previous > 250000) reset();
if (gravityPitch != null && !_hasPitchReference) {
_pitch = gravityPitch!;
_previousPitch = (_pitch * pitchGain).clamp(-1.45, 1.45);
_hasPitchReference = true;
}
if (_lastTimestampUs == null) {
_lastTimestampUs = timestampUs;
if (_hasPitchReference) {
absolutePitch = (_pitch * pitchGain).clamp(-1.45, 1.45);
}
return absolutePitch != null;
}
final dt = (timestampUs - _lastTimestampUs!) / 1000000;
_lastTimestampUs = timestampUs;
// A low angular velocity is not evidence of rest. Bias changes only via
// explicit stationary calibration, so slow intentional turns survive.
var gx = x - offsetX;
var gy = y - offsetY;
if (gx.abs() < 0.002) gx = 0;
if (gy.abs() < 0.002) gy = 0;
_yaw += gx * dt;
_pitch += gy * dt;
if (_hasPitchReference) {
// Equivalent to alpha=.98 at 100 Hz, independent of sensor frequency.
final alpha = pow(0.98, dt * 100).toDouble();
_pitch = alpha * _pitch + (1 - alpha) * gravityPitch!;
}
final prediction = predictionMs / 1000;
final yaw = _yaw + gx * prediction;
final pitch = ((_pitch + gy * prediction) * pitchGain).clamp(-1.45, 1.45);
final rawYaw = yaw - _previousYaw;
final rawPitch = pitch - _previousPitch;
_previousYaw = yaw;
_previousPitch = pitch;
if (jitterDamping) {
// Smooth without a per-frame deadband: that would erase slow motion
// increasingly often as a device's sampling frequency increases.
_dampedYaw = _dampedYaw * 0.15 + rawYaw * 0.85;
_dampedPitch = _dampedPitch * 0.15 + rawPitch * 0.85;
dYaw = _dampedYaw * sensitivity;
dPitch = _dampedPitch * sensitivity;
} else {
dYaw = rawYaw * sensitivity;
dPitch = rawPitch * sensitivity;
}
if (_hasPitchReference) {
absolutePitch = pitch;
dPitch = 0;
}
return true;
}