react-native-guided-camera
Version:
A React Native component for agricultural camera guidance with sensor-based motion detection, orientation tracking, and real-time feedback.
183 lines • 6.17 kB
JavaScript
import { Magnetometer } from "expo-sensors";
export class YawDetector {
constructor(onYawChange, config = {}) {
this.subscription = null;
this.isActive = false;
this.smoothedYaw = 0;
this.targetYaw = null;
this.onYawChange = onYawChange;
this.config = {
updateInterval: 10,
yawTolerance: 10, // 8 degrees tolerance (more practical)
smoothingFactor: 0.8,
...config,
};
}
calculateYaw(x, y) {
let yaw = Math.atan2(y, x) * (180 / Math.PI);
// Normalize to 0-360 degrees
if (yaw < 0)
yaw += 360;
return yaw;
}
calculateYawMetrics(currentYaw) {
let deviation = 0;
let isOnTarget = true;
let direction = "on_target";
let severity = "good";
// If no target is set, use the first reading as the target
if (this.targetYaw === null) {
this.targetYaw = currentYaw;
console.log("YawDetector: Auto-setting target yaw to:", this.targetYaw);
}
// Calculate shortest angular distance (handling 360° wraparound)
let diff = currentYaw - this.targetYaw;
if (diff > 180)
diff -= 360;
if (diff < -180)
diff += 360;
deviation = Math.abs(diff);
isOnTarget = deviation <= this.config.yawTolerance;
if (!isOnTarget) {
direction = diff > 0 ? "turn_left" : "turn_right";
if (deviation <= 15) {
severity = "minor";
}
else if (deviation <= 30) {
severity = "major";
}
else {
severity = "major";
}
}
return {
yaw: currentYaw,
isOnTarget,
deviation,
direction,
severity,
};
}
async start() {
if (this.isActive)
return;
try {
console.log("YawDetector: Requesting magnetometer permissions...");
const { status } = await Magnetometer.requestPermissionsAsync();
console.log("YawDetector: Permission status:", status);
if (status !== "granted") {
console.warn("YawDetector: Magnetometer permission not granted");
return;
}
console.log("YawDetector: Setting update interval to", this.config.updateInterval);
Magnetometer.setUpdateInterval(this.config.updateInterval);
console.log("YawDetector: Adding magnetometer listener...");
this.subscription = Magnetometer.addListener((data) => {
const rawYaw = this.calculateYaw(data.x, data.y);
// Apply smoothing
if (this.smoothedYaw === 0) {
this.smoothedYaw = rawYaw;
}
else {
// Handle 360° wraparound for smoothing
let diff = rawYaw - this.smoothedYaw;
if (diff > 180)
diff -= 360;
if (diff < -180)
diff += 360;
this.smoothedYaw += diff * (1 - this.config.smoothingFactor);
if (this.smoothedYaw < 0)
this.smoothedYaw += 360;
if (this.smoothedYaw >= 360)
this.smoothedYaw -= 360;
}
const metrics = this.calculateYawMetrics(this.smoothedYaw);
// console.log(
// "YawDetector: Current:",
// this.smoothedYaw.toFixed(1),
// "Target:",
// this.targetYaw?.toFixed(1),
// "Deviation:",
// metrics.deviation.toFixed(1),
// "OnTarget:",
// metrics.isOnTarget
// );
this.onYawChange(metrics);
});
this.isActive = true;
console.log("YawDetector: Successfully started");
}
catch (error) {
console.error("YawDetector: Failed to start yaw detector:", error);
}
}
stop() {
if (this.subscription) {
this.subscription.remove();
this.subscription = null;
}
this.isActive = false;
}
setTarget(yaw) {
this.targetYaw = yaw;
console.log("YawDetector: Target yaw set to:", yaw);
}
clearTarget() {
this.targetYaw = null;
console.log("YawDetector: Target yaw cleared");
}
calibrateToCurrentPosition() {
this.targetYaw = this.smoothedYaw;
console.log("YawDetector: Calibrated target to current position:", this.targetYaw);
}
getCurrentYaw() {
return this.smoothedYaw;
}
hasTarget() {
return this.targetYaw !== null;
}
}
// Helper functions for UI
export const getYawColor = (severity) => {
switch (severity) {
case "good":
return "#4CAF5070";
case "minor":
return "#FF980070";
case "major":
return "#F4433670";
default:
return "#FF980070";
}
};
export const getYawMessage = (metrics) => {
if (metrics.isOnTarget) {
return "Compass aligned";
}
switch (metrics.direction) {
case "turn_left":
return "Turn body left";
case "turn_right":
return "Turn body right";
default:
return "Adjust orientation";
}
};
// Translation-aware version
export const getYawMessageTranslated = (metrics, translations) => {
if (metrics.isOnTarget) {
return translations.compassAligned;
}
switch (metrics.direction) {
case "turn_left":
return translations.turnBodyLeft;
case "turn_right":
return translations.turnBodyRight;
default:
return translations.adjustOrientation;
}
};
export const shouldAllowRecordingYaw = (metrics) => {
return metrics.isOnTarget || metrics.severity === "minor";
};
//# sourceMappingURL=yawDetector.js.map