UNPKG

kalman-filter

Version:

Kalman filter (and Extended Kalman Filter) Multi-dimensional implementation in Javascript

137 lines (117 loc) 3.81 kB
const {buildDynamic} = require('../model-collection'); /** * @typedef {Object.<DynamicName, DynamicConfig>} PerNameConfigs */ /** * @typedef {Object} DynamicConfig * @param {Array.<Number>} obsIndexes * @param {Covariance} staticCovariance */ /** *Creates a dynamic model, considering the null in order to make the predictions * @param {Object} main * @param {Object.<String, DynamicConfig>} main.perName * @param {ObservationConfig} observation * @param {Array.<Array.<Number>>} opts.observedProjection * @returns {DynamicConfig} */ module.exports = function ({perName}, observation) { const {observedProjection} = observation; const observedDynamDimension = observedProjection[0].length; const dynamicNames = Object.keys(perName); const confs = {}; let nextDynamicDimension = observedDynamDimension; let nextObservedDimension = 0; dynamicNames.forEach(k => { const obsDynaIndexes = perName[k].obsDynaIndexes; if (typeof (perName[k].name) === 'string' && perName[k].name !== k) { throw (new Error(`${perName[k].name} and "${k}" should match`)); } perName[k].name = k; const {dimension, transition, covariance, init} = buildDynamic(perName[k], observation); const dynamicIndexes = []; for (let i = 0; i < dimension; i++) { const isObserved = (i < obsDynaIndexes.length); let newIndex; if (isObserved) { newIndex = nextObservedDimension; if (newIndex !== obsDynaIndexes[i]) { throw (new Error('thsoe should match')); } nextObservedDimension++; } else { newIndex = nextDynamicDimension; nextDynamicDimension++; } dynamicIndexes.push(newIndex); } confs[k] = { dynamicIndexes, transition, dimension, covariance, init, }; }); const totalDimension = dynamicNames.map(k => confs[k].dimension).reduce((a, b) => a + b, 0); if (nextDynamicDimension !== totalDimension) { throw (new Error('miscalculation of transition')); } const init = { index: -1, mean: new Array(totalDimension), covariance: new Array(totalDimension).fill(0).map(() => new Array(totalDimension).fill(0)), }; dynamicNames.forEach(k => { const { dynamicIndexes, init: localInit, } = confs[k]; if (typeof (localInit) !== 'object') { throw new TypeError('Init is mandatory'); } dynamicIndexes.forEach((c1, i1) => dynamicIndexes.forEach((c2, i2) => { init.covariance[c1][c2] = localInit.covariance[i1][i2]; })); dynamicIndexes.forEach((c1, i1) => { init.mean[c1] = localInit.mean[i1]; }); }); return { dimension: totalDimension, init, transition(options) { const {previousCorrected} = options; const resultTransition = new Array(totalDimension).fill().map(() => new Array(totalDimension).fill(0)); dynamicNames.forEach(k => { const { dynamicIndexes, transition, } = confs[k]; const options2 = Object.assign({}, options, {previousCorrected: previousCorrected.subState(dynamicIndexes)}); const trans = transition(options2); dynamicIndexes.forEach((c1, i1) => dynamicIndexes.forEach((c2, i2) => { resultTransition[c1][c2] = trans[i1][i2]; })); }); return resultTransition; }, covariance(options) { const {previousCorrected} = options; const resultCovariance = new Array(totalDimension).fill().map(() => new Array(totalDimension).fill(0)); dynamicNames.forEach(k => { const { dynamicIndexes, covariance, } = confs[k]; const options2 = Object.assign({}, options, {previousCorrected: previousCorrected.subState(dynamicIndexes)}); const cov = covariance(options2); // Console.log('dynamic.composition',k, cov, dynamicIndexes) dynamicIndexes.forEach((c1, i1) => dynamicIndexes.forEach((c2, i2) => { resultCovariance[c1][c2] = cov[i1][i2]; })); }); return resultCovariance; }, }; };