import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; final calibrationServiceProvider = Provider((ref) { return CalibrationService(); }); class CalibrationService { static const _pitchKey = 'calibration_pitch'; static const _rollKey = 'calibration_roll'; static const _gyroBiasXKey = 'calibration_gyro_bias_x'; static const _gyroBiasYKey = 'calibration_gyro_bias_y'; static const _gyroBiasZKey = 'calibration_gyro_bias_z'; static const _isCalibratedKey = 'calibration_is_calibrated'; static const _accelCalXKey = 'calibration_accel_x'; static const _accelCalYKey = 'calibration_accel_y'; static const _accelCalZKey = 'calibration_accel_z'; static const _mountModeKey = 'calibration_mount_mode'; static const _calibratedAtKey = 'calibration_timestamp_ms'; Future saveCalibrationOffsets( double pitch, double roll, { double gyroBiasX = 0.0, double gyroBiasY = 0.0, double gyroBiasZ = 0.0, double accelCalX = 0.0, double accelCalY = 0.0, double accelCalZ = 0.0, String mountMode = 'portrait', bool isCalibrated = true, int? motorcycleId, }) async { final prefs = await SharedPreferences.getInstance(); final nowMs = DateTime.now().millisecondsSinceEpoch; if (motorcycleId != null) { await prefs.setDouble('calibration_pitch_$motorcycleId', pitch); await prefs.setDouble('calibration_roll_$motorcycleId', roll); await prefs.setDouble('calibration_gyro_bias_x_$motorcycleId', gyroBiasX); await prefs.setDouble('calibration_gyro_bias_y_$motorcycleId', gyroBiasY); await prefs.setDouble('calibration_gyro_bias_z_$motorcycleId', gyroBiasZ); await prefs.setBool( 'calibration_is_calibrated_$motorcycleId', isCalibrated, ); await prefs.setDouble('calibration_accel_x_$motorcycleId', accelCalX); await prefs.setDouble('calibration_accel_y_$motorcycleId', accelCalY); await prefs.setDouble('calibration_accel_z_$motorcycleId', accelCalZ); await prefs.setString('calibration_mount_mode_$motorcycleId', mountMode); await prefs.setInt('calibration_timestamp_ms_$motorcycleId', nowMs); } // Also save global fallback await prefs.setDouble(_pitchKey, pitch); await prefs.setDouble(_rollKey, roll); await prefs.setDouble(_gyroBiasXKey, gyroBiasX); await prefs.setDouble(_gyroBiasYKey, gyroBiasY); await prefs.setDouble(_gyroBiasZKey, gyroBiasZ); await prefs.setBool(_isCalibratedKey, isCalibrated); await prefs.setDouble(_accelCalXKey, accelCalX); await prefs.setDouble(_accelCalYKey, accelCalY); await prefs.setDouble(_accelCalZKey, accelCalZ); await prefs.setString(_mountModeKey, mountMode); await prefs.setInt(_calibratedAtKey, nowMs); } Future clearCalibration({int? motorcycleId}) async { final prefs = await SharedPreferences.getInstance(); if (motorcycleId != null) { await prefs.setBool('calibration_is_calibrated_$motorcycleId', false); await prefs.setDouble('calibration_pitch_$motorcycleId', 0.0); await prefs.setDouble('calibration_roll_$motorcycleId', 0.0); await prefs.setDouble('calibration_gyro_bias_x_$motorcycleId', 0.0); await prefs.setDouble('calibration_gyro_bias_y_$motorcycleId', 0.0); await prefs.setDouble('calibration_gyro_bias_z_$motorcycleId', 0.0); await prefs.setDouble('calibration_accel_x_$motorcycleId', 0.0); await prefs.setDouble('calibration_accel_y_$motorcycleId', 0.0); await prefs.setDouble('calibration_accel_z_$motorcycleId', 0.0); } await prefs.setBool(_isCalibratedKey, false); await prefs.setDouble(_pitchKey, 0.0); await prefs.setDouble(_rollKey, 0.0); await prefs.setDouble(_gyroBiasXKey, 0.0); await prefs.setDouble(_gyroBiasYKey, 0.0); await prefs.setDouble(_gyroBiasZKey, 0.0); await prefs.setDouble(_accelCalXKey, 0.0); await prefs.setDouble(_accelCalYKey, 0.0); await prefs.setDouble(_accelCalZKey, 0.0); } Future> loadCalibrationOffsets({ int? motorcycleId, }) async { final prefs = await SharedPreferences.getInstance(); if (motorcycleId != null) { final bikePitch = prefs.getDouble('calibration_pitch_$motorcycleId'); final bikeRoll = prefs.getDouble('calibration_roll_$motorcycleId'); final bikeGbx = prefs.getDouble('calibration_gyro_bias_x_$motorcycleId') ?? 0.0; final bikeGby = prefs.getDouble('calibration_gyro_bias_y_$motorcycleId') ?? 0.0; final bikeGbz = prefs.getDouble('calibration_gyro_bias_z_$motorcycleId') ?? 0.0; final isCal = prefs.getBool('calibration_is_calibrated_$motorcycleId'); final ax = prefs.getDouble('calibration_accel_x_$motorcycleId') ?? 0.0; final ay = prefs.getDouble('calibration_accel_y_$motorcycleId') ?? 0.0; final az = prefs.getDouble('calibration_accel_z_$motorcycleId') ?? 0.0; if (bikePitch != null && bikeRoll != null) { return { 'pitch': bikePitch, 'roll': bikeRoll, 'gyroBiasX': bikeGbx, 'gyroBiasY': bikeGby, 'gyroBiasZ': bikeGbz, 'isCalibrated': (isCal == true) ? 1.0 : 0.0, 'accelCalX': ax, 'accelCalY': ay, 'accelCalZ': az, }; } } final pitch = prefs.getDouble(_pitchKey) ?? 0.0; final roll = prefs.getDouble(_rollKey) ?? 0.0; final gbx = prefs.getDouble(_gyroBiasXKey) ?? 0.0; final gby = prefs.getDouble(_gyroBiasYKey) ?? 0.0; final gbz = prefs.getDouble(_gyroBiasZKey) ?? 0.0; final isCal = prefs.getBool(_isCalibratedKey); final ax = prefs.getDouble(_accelCalXKey) ?? 0.0; final ay = prefs.getDouble(_accelCalYKey) ?? 0.0; final az = prefs.getDouble(_accelCalZKey) ?? 0.0; return { 'pitch': pitch, 'roll': roll, 'gyroBiasX': gbx, 'gyroBiasY': gby, 'gyroBiasZ': gbz, 'isCalibrated': (isCal == true) ? 1.0 : 0.0, 'accelCalX': ax, 'accelCalY': ay, 'accelCalZ': az, }; } Future loadMountMode({int? motorcycleId}) async { final prefs = await SharedPreferences.getInstance(); if (motorcycleId != null) { final mode = prefs.getString('calibration_mount_mode_$motorcycleId'); if (mode != null) return mode; } return prefs.getString(_mountModeKey); } }