blob: a56fd9bc7352a546743b1a017eb0968812a858cc [file] [log] [blame]
// This script provides a basic utility for de-batching the IMUValues
// message. See imu_plotter.ts for usage.
import {IMUValuesBatch} from './imu_batch_generated';
import {MessageHandler, TimestampedMessage} from '../../aos/network/www/aos_plotter';
import {Point} from '../../aos/network/www/plotter';
import {Table} from '../../aos/network/www/reflection';
import {ByteBuffer} from 'flatbuffers';
import {Schema} from 'flatbuffers_reflection/reflection_generated';
const FILTER_WINDOW_SIZE = 100;
export class ImuMessageHandler extends MessageHandler {
// Calculated magnitude of the measured acceleration from the IMU.
private acceleration_magnitudes: Point[] = [];
constructor(private readonly schema: Schema) {
super(schema);
}
private readScalar(table: Table, fieldName: string): number|BigInt|null {
return this.parser.readScalar(table, fieldName);
}
addMessage(data: Uint8Array, time: number): void {
const batch = IMUValuesBatch.getRootAsIMUValuesBatch(new ByteBuffer(data));
for (let ii = 0; ii < batch.readingsLength(); ++ii) {
const message = batch.readings(ii);
const table = Table.getNamedTable(
message.bb, this.schema, 'frc971.IMUValues', message.bb_pos);
if (this.parser.readScalar(table, "monotonic_timestamp_ns") == null) {
console.log('Ignoring unpopulated IMU values: ');
console.log(this.parser.toObject(table));
continue;
}
const time = Number(message.monotonicTimestampNs()) * 1e-9;
this.messages.push(new TimestampedMessage(table, time));
this.acceleration_magnitudes.push(new Point(
time,
Math.hypot(
message.accelerometerX(), message.accelerometerY(),
message.accelerometerZ())));
}
}
// Computes a moving average for a given input, using a basic window centered
// on each value.
private movingAverageCentered(input: Point[]): Point[] {
const num_measurements = input.length;
const filtered_measurements = [];
for (let ii = 0; ii < num_measurements; ++ii) {
let sum = 0;
let count = 0;
for (let jj = Math.max(0, Math.ceil(ii - FILTER_WINDOW_SIZE / 2));
jj < Math.min(num_measurements, ii + FILTER_WINDOW_SIZE / 2); ++jj) {
sum += input[jj].y;
++count;
}
filtered_measurements.push(new Point(input[ii].x, sum / count));
}
return filtered_measurements;
}
getField(field: string[]): Point[] {
// Any requested input that ends with "_filtered" will get a moving average
// applied to the original field.
const filtered_suffix = "_filtered";
if (field[0] == "acceleration_magnitude") {
return this.acceleration_magnitudes;
} else if (field[0].endsWith(filtered_suffix)) {
return this.movingAverageCentered(this.getField(
[field[0].slice(0, field[0].length - filtered_suffix.length)]));
} else {
return super.getField(field);
}
}
}