Add definitions for W3C Generic Sensor API

This commit is contained in:
Kenneth Rohde Christiansen
2017-09-15 17:38:28 +02:00
parent 2673835592
commit 5fa6827a03
4 changed files with 368 additions and 0 deletions

98
types/w3c-generic-sensor/index.d.ts vendored Normal file
View File

@@ -0,0 +1,98 @@
// Type definitions for W3C Generic Sensor API 1.0
// Project: https://www.w3.org/TR/generic-sensor/
// Definitions by: Kenneth Rohde Christiansen <https://github.com/kenchris>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.2
// Explainer: https://www.w3.org/TR/motion-sensors/
/// <reference types="geometry-dom" />
declare class SensorErrorEvent extends Event {
constructor(type: string, errorEventInitDict: SensorErrorEventInit);
readonly error: Error;
}
interface SensorErrorEventInit extends EventInit {
error: Error;
}
declare class Sensor extends EventTarget {
readonly activated: boolean;
readonly timestamp?: number; // Should be DOMHighResTimeStamp.
start(): void;
stop(): void;
onreading: (this: this, ev: Event) => any;
onactivate: (this: this, ev: Event) => any;
onerror: (this: this, ev: SensorErrorEvent) => any;
addEventListener(type: "reading" | "activate", listener: (this: this, ev: Event) => any, useCapture?: boolean): void;
addEventListener(type: "error", listener: (this: this, ev: SensorErrorEvent) => any, useCapture?: boolean): void;
}
interface SensorOptions {
frequency?: number;
}
// Accelerometer: https://www.w3.org/TR/accelerometer/
declare class Accelerometer extends Sensor {
constructor(options?: SensorOptions);
readonly x?: number;
readonly y?: number;
readonly z?: number;
}
declare class LinearAccelerationSensor extends Accelerometer {
constructor(options?: SensorOptions);
}
declare class GravitySensor extends Accelerometer {
constructor(options?: SensorOptions);
}
// Gyroscope: https://www.w3.org/TR/gyroscope/
declare class Gyroscope extends Sensor {
constructor(options?: SensorOptions);
readonly x?: number;
readonly y?: number;
readonly z?: number;
}
// Magnetometer: https://www.w3.org/TR/magnetometer/
declare class Magnetometer extends Sensor {
constructor(options?: SensorOptions);
readonly x?: number;
readonly y?: number;
readonly z?: number;
}
declare class UncalibratedMagnetometer extends Sensor {
constructor(options?: SensorOptions);
readonly x?: number;
readonly y?: number;
readonly z?: number;
readonly xBias?: number;
readonly yBias?: number;
readonly zBias?: number;
}
// Orientation Sensor: https://www.w3.org/TR/orientation-sensor/
type RotationMatrixType = Float32Array | Float64Array | GeometryDom.DOMMatrix;
declare class OrientationSensor extends Sensor {
readonly quaternion?: number[];
populateMatrix(targetMatrix: RotationMatrixType): void;
}
declare class AbsoluteOrientationSensor extends OrientationSensor {
constructor(options?: SensorOptions);
}
declare class RelativeOrientationSensor extends OrientationSensor {
constructor(options?: SensorOptions);
}

View File

@@ -0,0 +1,23 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"dom"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"w3c-generic-sensor-tests.ts"
]
}

View File

@@ -0,0 +1 @@
{ "extends": "dtslint/dt.json" }

View File

@@ -0,0 +1,246 @@
// Accelerometer: https://www.w3.org/TR/accelerometer/
const accelerometer1 = () => {
const sensor = new Accelerometer();
sensor.start();
sensor.onreading = () => {
console.log("Acceleration along X-axis: " + sensor.x);
console.log("Acceleration along Y-axis: " + sensor.y);
console.log("Acceleration along Z-axis: " + sensor.z);
};
sensor.onerror = event => console.log(event.error.name, event.error.message);
};
// Gyroscope: https://www.w3.org/TR/gyroscope/
const gyroscope1 = () => {
const sensor = new Gyroscope();
sensor.start();
sensor.onreading = () => {
console.log("Angular velocity around the X-axis " + sensor.x);
console.log("Angular velocity around the Y-axis " + sensor.y);
console.log("Angular velocity around the Z-axis " + sensor.z);
};
sensor.onerror = event => console.log(event.error.name, event.error.message);
};
// From Magnetometer spec: https://www.w3.org/TR/magnetometer/
const magnetometer1 = () => {
const sensor = new Magnetometer();
sensor.start();
sensor.onreading = () => {
console.log("Magnetic field along the X-axis " + sensor.x);
console.log("Magnetic field along the Y-axis " + sensor.y);
console.log("Magnetic field along the Z-axis " + sensor.z);
};
sensor.onerror = event => console.log(event.error.name, event.error.message);
};
const magnetometer2 = () => {
const sensor = new Magnetometer();
sensor.start();
sensor.onreading = () => {
if (!sensor.y || !sensor.x) {
return;
}
const heading = Math.atan2(sensor.y, sensor.x) * (180 / Math.PI);
console.log('Heading in degrees: ' + heading);
};
};
const magnetometer3 = () => {
const sensor = new Magnetometer();
sensor.start();
sensor.onreading = async () => {
if (!sensor.y || !sensor.x) {
return;
}
const heading = Math.atan2(sensor.y, sensor.x) * (180 / Math.PI);
// Get the latitude and longitude, omitted for brevity here.
const latitude = 0;
const longitude = 0;
// Get the magnetic declination at the given latitude and longitude.
const response = await fetch('https://www.ngdc.noaa.gov/geomag-web/calculators/calculateDeclination' +
`?lat1=${latitude}&lon1=${longitude}&resultFormat=csv`);
const text = await response.text();
const declination = parseFloat(text.replace(/^#.*$/gm, '').trim().split(',')[4]);
// Compensate for the magnetic declination to get the geographic north.
console.log('True heading in degrees: ' + (heading + declination));
};
};
// From Orientation Sensor spec: https://www.w3.org/TR/orientation-sensor/
const orientation1 = () => {
const sensor = new AbsoluteOrientationSensor();
const mat4 = new Float32Array(16);
sensor.start();
sensor.onerror = event => console.log(event.error.name, event.error.message);
sensor.onreading = () => {
sensor.populateMatrix(mat4);
};
};
const orientation2 = () => {
const sensor = new AbsoluteOrientationSensor({ frequency: 60 });
const mat4 = new Float32Array(16);
sensor.start();
sensor.onerror = event => console.log(event.error.name, event.error.message);
function draw() {
window.requestAnimationFrame(draw);
try {
sensor.populateMatrix(mat4);
} catch (err) {
console.log("mat4 has not been updated: " + err);
}
// Drawing...
}
window.requestAnimationFrame(draw);
};
// From Explainer: https://www.w3.org/TR/motion-sensors/
const explainer1 = () => {
class LowPassFilterData {
x: number;
y: number;
z: number;
bias: number;
constructor(reading: Accelerometer | Gyroscope | Magnetometer, bias: number) {
Object.assign(this, { x: reading.x, y: reading.y, z: reading.z });
this.bias = bias;
}
update(reading: Accelerometer | Gyroscope | Magnetometer) {
if (!reading.x || !reading.y || !reading.z) {
return;
}
this.x = this.x * this.bias + reading.x * (1 - this.bias);
this.y = this.y * this.bias + reading.y * (1 - this.bias);
this.z = this.z * this.bias + reading.z * (1 - this.bias);
}
}
const accl = new Accelerometer({ frequency: 20 });
// Isolate gravity with low-pass filter.
const filter = new LowPassFilterData(accl, 0.8);
accl.onreading = () => {
filter.update(accl); // Pass latest values through filter.
console.log(`Isolated gravity (${filter.x}, ${filter.y}, ${filter.z})`);
};
accl.start();
};
const explainer2 = () => {
class HighPassFilterData {
cutoff: number;
timestamp?: number;
x?: number;
y?: number;
z?: number;
constructor(reading: Accelerometer | Gyroscope | Magnetometer, cutoffFrequency: number) {
this.x = reading.x;
this.y = reading.y;
this.z = reading.z;
this.cutoff = cutoffFrequency;
this.timestamp = reading.timestamp;
}
update(reading: Accelerometer | Gyroscope | Magnetometer) {
if (!this.timestamp || !this.x || !this.y || !this.z) {
this.x = reading.x;
this.y = reading.y;
this.z = reading.z;
this.timestamp = reading.timestamp;
return;
}
if (!reading.timestamp || !reading.x || ! reading.y || !reading.z) {
return;
}
const dt = reading.timestamp - this.timestamp / 1000;
this.timestamp = reading.timestamp;
const alpha = this.cutoff / (this.cutoff + dt);
this.x = this.x + alpha * (reading.x - this.x);
this.y = this.y + alpha * (reading.y - this.y);
this.z = this.z + alpha * (reading.z - this.z);
}
}
const gyro = new Gyroscope({ frequency: 20 });
// Remove drift with a high pass filter.
const filter = new HighPassFilterData(gyro, 0.8);
gyro.onreading = () => {
filter.update(gyro); // Pass latest values through filter.
console.log(`Steady gyroscope (${filter.x}, ${filter.y}, ${filter.z})`);
};
gyro.start();
};
const explainer3 = () => {
const options = { frequency: 50 };
const accl = new Accelerometer(options);
const gyro = new Gyroscope(options);
let timestamp = 0;
let alpha = 0;
let beta = 0;
let gamma = 0;
const bias = 0.98;
gyro.onreading = () => {
if (!gyro.timestamp || !gyro.x || !gyro.y || !gyro.z || !accl.x || !accl.y || !accl.z) {
return;
}
const dt = timestamp ? (gyro.timestamp - timestamp) / 1000 : 0;
timestamp = gyro.timestamp;
// Treat the acceleration vector as an orientation vector by normalizing it.
// Keep in mind that the if the device is flipped, the vector will just be
// pointing in the other direction, so we have no way to know from the
// accelerometer data which way the device is oriented.
const norm = Math.sqrt(accl.x ** 2 + accl.y ** 2 + accl.z ** 2);
// As we only can cover half (PI rad) of the full spectrum (2*PI rad) we multiply
// the unit vector with values from [-1, 1] with PI/2, covering [-PI/2, PI/2].
const scale = Math.PI / 2;
alpha = alpha + gyro.z * dt;
beta = bias * (beta + gyro.x * dt) + (1.0 - bias) * (accl.x * scale / norm);
gamma = bias * (gamma + gyro.y * dt) + (1.0 - bias) * (accl.y * -scale / norm);
// Do something with Euler angles (alpha, beta, gamma).
};
accl.start();
gyro.start();
};