start method

void start()

Starts tracking. Keep the device still during the one-second calibration.

Implementation

void start() {
  stop();
  if (!canStart) return;
  _running = true;
  final session = _session;
  _offsetX = 0;
  _offsetY = 0;
  _fusion.reset(clearGravity: true);
  _lastGravityEvent = null;
  _silentIntervals = 0;
  _gravitySilentIntervals = 0;
  _smoothPitch = 0;
  calibrate();

  // One timer, not a newly allocated timer per 60–120 Hz sample.
  _healthTimer = Timer.periodic(const Duration(milliseconds: 200), (_) {
    if (!_running || session != _session) return;
    if (++_silentIntervals >= 4 && isGyroscopeActive) {
      _loseGyroscope();
    }
    if (++_gravitySilentIntervals >= 4 && _lastGravityEvent != null) {
      _loseGravity();
    }
  });

  if (useIsolate) _startWorker(session);
  // An injected stream may work on desktop even though the native backend
  // does not. In that case never fill a missing channel with a plugin call.
  final platformSensors = VrSensorCapabilities.supportsDeviceMotion;
  final accelerationEvents =
      accelerometerStreamOverride ??
      (platformSensors
          ? accelerometerEventStream(
              samplingPeriod: SensorInterval.fastestInterval,
            )
          : null);
  _accelSubscription = accelerationEvents?.listen(
    (event) {
      if (!_running || session != _session) return;
      if (!_fusion.updateGravity(event.x, event.y, event.z)) return;
      _lastGravityEvent = event;
      _gravitySilentIntervals = 0;
      _isolateSendPort?.send([1, event.x, event.y, event.z]);
      if (!isGyroscopeActive) {
        _smoothPitch = _smoothPitch * 0.85 + _fusion.gravityPitch! * 0.15;
        target.setPitch(_smoothPitch * pitchGain);
      }
    },
    onError: (Object error) {
      if (_running && session == _session) _loseGravity();
    },
    onDone: () {
      if (_running && session == _session) _loseGravity();
    },
  );

  final gyroscopeEvents =
      gyroscopeStreamOverride ??
      (platformSensors
          ? gyroscopeEventStream(
              samplingPeriod: SensorInterval.fastestInterval,
            )
          : null);
  _subscription = gyroscopeEvents?.listen(
    (event) {
      if (!_running ||
          session != _session ||
          !event.x.isFinite ||
          !event.y.isFinite ||
          !event.z.isFinite) {
        return;
      }
      _silentIntervals = 0;
      if (!isGyroscopeActive) _resetFusion();
      isGyroscopeActive = true;
      if (_calibrating) {
        _calibrationSumX += event.x;
        _calibrationSumY += event.y;
        _calibrationSamples++;
        return;
      }
      final timestampUs = event.timestamp.microsecondsSinceEpoch;
      if (_isolateSendPort != null) {
        _sendConfiguration();
        _isolateSendPort.send([
          2,
          event.x,
          event.y,
          timestampUs,
          _workerRevision,
        ]);
      } else {
        _configureFusion();
        if (_fusion.addGyroscope(event.x, event.y, timestampUs)) {
          _applyFusion(_fusion.dYaw, _fusion.dPitch, _fusion.absolutePitch);
        }
      }
    },
    onError: (Object error) {
      if (_running && session == _session) _loseGyroscope();
    },
    onDone: () {
      if (_running && session == _session) _loseGyroscope();
    },
  );
}