dualsense-ts
Version:
The natural interface for your DualSense and DualSense Access controllers, with Typescript
106 lines • 4.47 kB
JavaScript
"use strict";
/**
* High-level orientation tracker for the DualSense controller.
*
* Wraps the Madgwick AHRS filter and provides:
* - Fused orientation as Euler angles and quaternion
* - Accelerometer-only tilt (no drift, no yaw — useful for
* gravity-reference applications like steering)
* - Automatic unit conversion from the library's [-1, 1] calibrated
* values to the rad/s and g units the filter expects
*
* DualSense IMU hardware constants:
* Gyroscope: ±2000 deg/s full scale → 1.0 = 2000 deg/s
* Accelerometer: ±4 g full scale → 0.25 ≈ 1 g
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Orientation = void 0;
const madgwick_1 = require("./madgwick");
const quaternion_1 = require("./quaternion");
/**
* Convert our [-1, 1] calibrated gyro value to rad/s.
* Full scale is ±2000 deg/s = ±34.9066 rad/s.
*/
const GYRO_SCALE = 2000 * (Math.PI / 180); // ≈ 34.9066
class Orientation {
// ---- Configuration ----
/** Madgwick filter gain. Can be adjusted at runtime. */
get beta() {
return this.filter.beta;
}
set beta(v) {
this.filter.beta = v;
}
constructor(params) {
// ---- Public state (read these in your game loop) ----
/** Fused orientation as Euler angles (radians, updated each sample). */
this.pitch = 0;
this.yaw = 0;
this.roll = 0;
/** Fused orientation as a unit quaternion [w, x, y, z]. */
this.quaternion = [...quaternion_1.IDENTITY];
/**
* Tilt derived from the accelerometer gravity vector alone.
* No drift, but also no yaw — only pitch and roll.
* Noisy during motion; best used when the controller is relatively still.
*/
this.tiltPitch = 0;
this.tiltRoll = 0;
this.filter = new madgwick_1.MadgwickFilter(params?.beta ?? 0.1);
}
/** Reset to identity orientation (call when zeroing the view). */
reset() {
this.filter.reset();
this.pitch = 0;
this.yaw = 0;
this.roll = 0;
this.quaternion = [...quaternion_1.IDENTITY];
this.tiltPitch = 0;
this.tiltRoll = 0;
}
/**
* Incorporate one IMU sample. Called automatically by the Dualsense
* class on each HID report — you don't normally call this yourself.
*
* @param gx Calibrated gyro X (pitch), [-1, 1]
* @param gy Calibrated gyro Y (yaw), [-1, 1]
* @param gz Calibrated gyro Z (roll), [-1, 1]
* @param ax Calibrated accel X, [-1, 1]
* @param ay Calibrated accel Y, [-1, 1]
* @param az Calibrated accel Z, [-1, 1]
* @param dt Time delta in seconds
*/
update(gx, gy, gz, ax, ay, az, dt) {
// Remap DualSense axes to Madgwick's Z-up convention.
// DualSense: X = right, Y = up (gravity), Z = forward
// Madgwick: X = right, Y = forward, Z = up (gravity)
// Mapping: filter(X, Y, Z) = DS(X, -Z, Y)
this.filter.update(gx * GYRO_SCALE, // filter X = DS X (pitch axis)
-gz * GYRO_SCALE, // filter Y = -DS Z (negated forward)
gy * GYRO_SCALE, // filter Z = DS Y (gravity axis → up)
ax, // accel X unchanged
-az, // accel Y = -DS Z
ay, // accel Z = DS Y (gravity)
dt);
// Extract Euler angles and remap back to DualSense frame
this.quaternion = this.filter.q;
const euler = (0, quaternion_1.toEuler)(this.quaternion);
this.pitch = euler.pitch; // filter X rotation → DS pitch (forward/back)
this.yaw = euler.roll; // filter Z rotation → DS yaw (left/right turn)
this.roll = -euler.yaw; // filter Y rotation → DS roll (side tilt, negated)
// Accelerometer-only tilt (gravity reference)
// DualSense axes: X = lateral, Y = vertical (up at rest), Z = forward
// Pitch = forward/back tilt (around X): gravity shifts from Y toward Z
// Roll = side-to-side tilt (around Z): gravity shifts from Y toward X
const aNorm = Math.sqrt(ax * ax + ay * ay + az * az);
if (aNorm > 0) {
const nax = ax / aNorm;
const nay = ay / aNorm;
const naz = az / aNorm;
this.tiltPitch = Math.atan2(-naz, nay);
this.tiltRoll = Math.atan2(nax, Math.sqrt(nay * nay + naz * naz));
}
}
}
exports.Orientation = Orientation;
//# sourceMappingURL=orientation.js.map