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]);
  });
}