Skip to content

Commit

Permalink
fix: store the collected power and speed together and send median bas…
Browse files Browse the repository at this point in the history
…ed on power readings
  • Loading branch information
David Teirney committed Jun 9, 2022
1 parent c71cfde commit 678f88a
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 73 deletions.
80 changes: 28 additions & 52 deletions lib/abrp.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,22 @@ const MIN_CALIBRATION_SPEED = 30 // kph
const OVMS_API_KEY = '32b2162f-9599-4647-8139-66e9f9528370'
const VERSION = '2.0'

function average(array) {
if (!array.length) {
return null
}
var total = 0
array.forEach(function (number) {
total += number
})
return total / array.length
}

function median(array) {
function medianPowerMetrics(array) {
if (!array.length) {
return null
}
// Find the median based on the power metric
const sorted = array.slice().sort(function (a, b) {
return a - b
return a.power - b.power
})
const midpoint = Math.floor(sorted.length / 2)
if (sorted.length % 2 === 0) {
// even, last index element in slice is not returned, hence + 1
return average(sorted.slice(midpoint - 1, midpoint + 1))
// even, average the readings either side of the midpoint
return {
power: (sorted[midpoint - 1].power + sorted[midpoint].power) / 2,
speed: (sorted[midpoint - 1].speed + sorted[midpoint].speed) / 2,
}
} else {
// odd
return sorted[midpoint]
}
}
Expand Down Expand Up @@ -99,46 +91,26 @@ function round(number, precision) {
}

const console = logger()
var collectedMetrics = []
var lastSentTelemetry = {
utc: 0,
}
var collectedPowerMetrics = []
var collectedSpeedMetrics = []
var subscribedLowFrequency = false
var subscribedHighFrequency = false

function collectHighFrequencyMetrics() {
const highFrequencyMetricNames = ['v.b.power', 'v.p.speed']
const metrics = OvmsMetrics.GetValues(highFrequencyMetricNames)
const power = metrics['v.b.power']
if (!isNil(power)) {
collectedPowerMetrics.push(power)
}
const speed = metrics['v.p.speed']
if (!isNil(speed)) {
collectedSpeedMetrics.push(speed)
if (!isNil(power) && !isNil(speed)) {
collectedMetrics.push({
power,
speed,
})
}
}

function getAndClearSmoothedMetrics() {
const smoothedMetrics = {
averagePower: round(average(collectedPowerMetrics), 2), // ~10W of precision
averageSpeed: round(average(collectedSpeedMetrics)),
medianPower: round(median(collectedPowerMetrics), 2), // ~10W of precision
medianSpeed: round(median(collectedSpeedMetrics)),
}
// Only log anything if there's some content
if (collectedPowerMetrics.length || collectedSpeedMetrics.length) {
console.info('Collected power metrics', collectedPowerMetrics)
console.info('Collected speed metrics', collectedSpeedMetrics)
console.info('Smoothed metrics', smoothedMetrics)
}
// Clear the collected metrics
collectedPowerMetrics = []
collectedSpeedMetrics = []
return smoothedMetrics
}

function getOvmsMetrics() {
const metricNames = [
'v.b.current',
Expand Down Expand Up @@ -270,14 +242,19 @@ function sendTelemetryIfNecessary() {
const metrics = getOvmsMetrics()
const currentTelemetry = mapMetricsToTelemetry(metrics)

// If being collected, smooth the point in time power and speed recordings
// using the median from the metrics collected at a higher frequency
const smoothedMetrics = getAndClearSmoothedMetrics()
if (!isNil(smoothedMetrics.medianPower)) {
currentTelemetry.power = smoothedMetrics.medianPower
}
if (!isNil(smoothedMetrics.medianSpeed)) {
currentTelemetry.speed = smoothedMetrics.medianSpeed
// If being collected, somewhat smooth the point in time power and speed
// reported using the metrics for the median power entry from those collected
// at a higher frequency
if (collectedMetrics.length) {
console.debug('Collected metrics', collectedMetrics)
const medianMetrics = medianPowerMetrics(collectedMetrics)
if (!isNil(medianMetrics)) {
console.debug('Median power metrics', medianMetrics)
currentTelemetry.power = round(medianMetrics.power, 2) // ~ nearest 10W of precision
currentTelemetry.speed = round(medianMetrics.speed)
}
// And then clear the collected metrics for the next low frequency pass
collectedMetrics = []
}

const elapsed = currentTelemetry.utc - lastSentTelemetry.utc
Expand Down Expand Up @@ -425,8 +402,7 @@ function send(onoff) {
}

module.exports = {
average, // jest
median, // jest
medianPowerMetrics, // jest
omitNil, // jest
info,
onetime,
Expand Down
65 changes: 44 additions & 21 deletions lib/arbp.test.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,60 @@
const { average, median, omitNil, round } = require('./abrp')
const { medianPowerMetrics, omitNil, round } = require('./abrp')

describe('average', () => {
describe('medianPowerMetrics', () => {
test('should return null with no array elements', () => {
expect(average([])).toBeNull()
})
test('should work as expected', () => {
expect(average([1, 2, 3, 4, 10])).toBe(4)
})
})

describe('median', () => {
test('should return null with no array elements', () => {
expect(median([])).toBeNull()
expect(medianPowerMetrics([])).toBeNull()
})
test('should work as expected with odd number of elements', () => {
expect(median([1, 2, 3, 4, 10])).toBe(3)
expect(median([10, 4, 2, 3, 1])).toBe(3)
expect(
medianPowerMetrics([
{ power: 1, speed: 10 },
{ power: 2, speed: 4 },
{ power: 3, speed: 3 },
{ power: 4, speed: 2 },
{ power: 10, speed: 1 },
])
).toEqual({ power: 3, speed: 3 })
expect(
medianPowerMetrics([
{ power: 10, speed: 1 },
{ power: 1, speed: 10 },
{ power: 4, speed: 2 },
{ power: 2, speed: 4 },
{ power: 3, speed: 3 },
])
).toEqual({ power: 3, speed: 3 })
})
test('should work as expected with even number of elements', () => {
expect(median([1, 3, 4, 10])).toBe(3.5)
expect(median([10, 1, 4, 3])).toBe(3.5)
expect(
medianPowerMetrics([
{ power: 1, speed: 10 },
{ power: 3, speed: 3 },
{ power: 4, speed: 2 },
{ power: 10, speed: 1 },
])
).toEqual({ power: 3.5, speed: 2.5 })
expect(
medianPowerMetrics([
{ power: 10, speed: 1 },
{ power: 1, speed: 10 },
{ power: 4, speed: 2 },
{ power: 3, speed: 3 },
])
).toEqual({ power: 3.5, speed: 2.5 })
})
})

describe('omitNil', () => {
test('should work as expected', () => {
expect(omitNil({
expect(
omitNil({
foo: 'foo',
bar: 'bar',
spaz: null,
})
).toEqual({
foo: 'foo',
bar: 'bar',
spaz: null
})).toEqual({
foo: 'foo',
bar: 'bar'
})
})
})
Expand Down

0 comments on commit 678f88a

Please sign in to comment.