Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: PBVE - auxillary engine temperature #144

Merged
merged 1 commit into from
Aug 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 179 additions & 30 deletions hooks/proprietary/PBVE.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,15 @@

const debug = require('debug')('signalk-parser-nmea0183/PBVE')
const utils = require('@signalk/nmea0183-utilities')
const alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')
const schema = {

/*

OP30 digital oil pressure gauge sample NMEA sentence:
Note: oil pressure calculation formulas are the same for all instruments


OP30/OP60 digital oil pressure gauge sample NMEA sentence:

Sentence: $PBVE,DGOIADNNACAEACAAABBLAAEBAACMCFAAEPAIKI*37

Expand All @@ -34,85 +39,229 @@ $PBVE,x,x,xxxx,xxxx,xx,xx,xx,xx,xxxx,xxxx,xx,xxxx,xxxx,xx*xx

0: Product code: D = OP 30/60
1: Software version: G = ???
2: Oil pressure calibration number: OIAD = 14 8 0 3 = ???
3: A2D gain: NNAC = ???
2: Oil pressure calibration number: OIAD = 14 8 0 3 = 256*3 + 16*14 +8 = 1000 = 1.000
3: A2D gain: NNAC = 13 13 0 3 = 256*03 + 16*13 +13= 989= 0.989
4: Sender Type: AE = 4
5: Backlight level: AC = 2
6: Units of measure: AA = psi
6: Units of measure: AA = psi, otherwise bar
7: Built in alarms armed: AB = 1 = true
8: Low pressure alarm value: BLAA = 1 11 0 0 = ???
9: High pressure alarm value: EBAA = 4 1 0 0 = ???
8: Low pressure alarm value: BLAA = 1 11 0 0 = 256*0 + 16*1 +11= 27 psi
9: High pressure alarm value: EBAA = 4 1 0 0 = 256*0 + 16*4 +1= 65 psi
10: Checksum for Non-Volatile Memory: CM = 2 12 = ???
11: Oil Pressure: CFAA = 37
12: Sensor Volts: EPAI = 4 15 0 8 = ???
12: Sensor Volts: EPAI = 4 15 0 8 = 256*8 + 16*4 +15= 2116=2.116 VDC
13: Checksum: KI = 10 8
14: Checksum: 37

Where A=0, B=1, C=2, ... , O=14, P=15

Decode Oil Pressure as:
CFAA = 16*M + L + 4096*A + 256*A
CFAA = 16*C + F + 4096*A + 256*A
CFAA = 16*2 + 5 + 4096*0 + 256*0
CFAA = 37 psi

//ALPHA is an array map for converting sentence codes from letters to numbers
//alpha is an array map for converting sentence codes from letters to numbers
//A=0, B=1, etc


*/
D: {
path: 'propulsion.0.oilPressure',
meta: {
description: 'CruzPro OP30/OP60 Engine Oil Pressure Gauge',
units: 'pa',
displayName: 'Engine Oil Pressure',
shortName: 'EOP',
warnMethod: ['visual'],
alarmMethod: ['sound'],
gaugeAlarmOn: false,
backlight: 0,
zones: [],
originalValue: null,
}

},
/*

T30/T60 digital temperature gauge sample NMEA sentence:

Sentence: $PBVE,EDOIADOKACABABAAAACAPPCMABCGADABDOAEGL*20

Parse as:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
| | | | | | | | | | | | | | |
$PBVE,x,x,xxxx,xxxx,xx,xx,xx,xx,xxxx,xxxx,xx,xxxx,xxxx,xx*xx
$PBVE,E D OIAD OKAC AB AB AA AA CAPP CMAB CG ADAB DOAE GL 20

0: Product Code = E = T30/T60
1: Software Version #
2: Temperature Calibration Number
3: A2D gain
4: Sender Type
5: Backlight Level
6: Units of Measure (0= deg F, non-0= deg C)
7: Built-in Alarms Armed (AA=0 = NO)
8: Low temperature alarm value
9: High temperature alarm value
10: Checksum for Non-Volatile Memory
11: Engine Temperature
12: Sensor Volts
13: NMEA sentence checksum
14: Checksum

Where A=0, B=1, C=2, ... , O=14, P=15

Decode Temperature as:
ADAB = 16*A + D + 4096*A + 256*B
ADAB = 16*0 + 3 + 4096*0 +256*1
ADAB = 0 + 3 + 0 + 256
ADAB = 259 deg F

*/
const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')
E: {
path: 'propulsion.0.coolantTemperature',
meta: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will accomplish what you are after here. Check the full that results from this - does it have the meta in place that should be there?

We don't have a way to update meta information with a delta. @sbender9 was working on this at SignalK/signalk-server#481 but that stalled. This use case could be the time to resurrect that line of work.

description: 'CruzPro T30/T60 Engine Coolant Temperature Gauge',
units: 'k',
displayName: 'Engine Coolant Temperature',
shortName: 'ECT',
warnMethod: ['visual'],
alarmMethod: ['sound'],
gaugeAlarmOn: false,
backlight: 0,
zones: [],
originalValue: null
}
}

}

/*
@function convertAlphasToInts
@param alphas String
@return Array
*/
function convertAlphasToInts(alphas){
function toInts(alphas){
let replacement = [];
let i;
alphas = alphas.split('')
var replacement = [];
for(var i = 0; i < alphas.length;i++){
const indexValue = ALPHA.indexOf(alphas[i]);
replacement.push(indexValue)

for(i = 0; i < alphas.length; i++){
replacement.push(alpha.indexOf(alphas[i]))
}
return replacement;
}


/*
@function convertToOilPressure
@param values Array
@function convertToValue
@param values String
@return Int
*/
function convertToOilPressure(values){
return (16 * values[0]) + values[1] + (4096 * values[2]) + (256 * values[3])
function convertToValue(values){
const parts = toInts(values)
return (16 * parts[0]) + parts[1] + (4096 * parts[2]) + (256 * parts[3])
}

module.exports = function(input) {
const { id, sentence, parts, tags } = input
/*
@function convertToAlarmValue
@param values String
@return Int
*/
function convertToAlarmValue(values){
const parts = toInts(values)
return ( (16 * parts[0]) + parts[1] + (256 * parts[2]) )
}

let values = []
module.exports = function(input) {
let convertedValue = {}
let delta = {}

const { id, sentence, parts, tags } = input
const data = parts[0]
const productCode = data.substr(0,1)

if(productCode !== 'D'){
debug('Unsupported CruzPro instrument type')
//just retun right away if not supported product code
if (productCode in schema === false) {
debug('Unsupported product code: ', productCode)
return;
}

const convertedValue = convertAlphasToInts(data.substr(28,4))
const oilPressure = convertToOilPressure(convertedValue)
const backlight = data.substr(8,2)
const gaugeUnits = data.substr(15,2)
const gaugeAlarmOn = data.substr(17,2)
const lower = convertToAlarmValue(data.substr(18,4))
const upper = convertToAlarmValue(data.substr(22,4))
const value = convertToValue(data.substr(28,4))

if (productCode === 'D') {

const conditionalValue = function(derivedValue){
return gaugeUnits === 'AA' ? derivedValue * 6894.757 : derivedValue * 100000
}

convertedValue = {
//convert to pascals from psi/bar
value: conditionalValue(value),
path: schema[productCode].path,
meta: schema[productCode].meta
}
convertedValue.meta.gaugeUnits = gaugeUnits === 'AA' ? 'psi' : 'bar'
//TODO: Add warning zone values
convertedValue.meta.zones = [
{
lower: conditionalValue(lower),
state: 'alarm',
message: 'Engine oil pressure at lowest threshold'
},
{
upper: conditionalValue(upper),
state: 'alarm',
message: 'Engine oil pressure at highest threshold'
}
]

} else if(productCode === 'E') {

const conditionalValue = function(derivedValue){
return (gaugeUnits === 'AA' ? (derivedValue - 32) * (5/9) + 273.15 : derivedValue + 273.15)
}

convertedValue = {
//convert to C from F
value: conditionalValue(value),
path: schema[productCode].path,
meta: schema[productCode].meta
}
convertedValue.meta.gaugeUnits = gaugeUnits === 'AA' ? 'f' : 'c'
//TODO: Add warning zone values
convertedValue.meta.zones = [
{
lower: conditionalValue(lower),
state: 'alarm',
message: 'Engine coolant temperature at lowest threshold'
},
{
upper: conditionalValue(upper),
state: 'alarm',
message: 'Engine coolant temperature at highest threshold'
}
]

const oilPressureValue = {
path: 'propulsion.0.oilPressure',
value: oilPressure
}

//baclight is AA, AB, etc so just need second value
convertedValue.meta.backlight = toInts(backlight)[1]
// instrument gauge alarm is armed when second value is 1 (A,B)
convertedValue.meta.gaugeAlarmOn = toInts(gaugeAlarmOn)[1] === 1
//store original value in meta
convertedValue.meta.originalValue = value;

delta = {
updates: [{
source: tags.source,
timestamp: tags.timestamp,
values: [oilPressureValue]
values: [convertedValue]
}]
}

Expand Down
74 changes: 69 additions & 5 deletions test/PBVE.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,78 @@ const should = require('chai').Should()
const toFull = require('./toFull')

describe('PBVE', () => {
it('Converts OK using individual parser', () => {

it('Converts engine oil pressure using individual parser', () => {
const delta = new Parser().parse('$PBVE,DGOIADNNACAEACAAABBLAAEBAACMCFAAEPAIKI*37')
delta.updates[0].values.should.contain.an.item.with.property('path', 'propulsion.0.oilPressure')
delta.updates[0].values[0].value.should.equal(37)
delta.updates[0].values[0].value.should.equal(255106.009)
delta.updates[0].values[0].meta.should.deep.equal(
{
description: 'CruzPro OP30/OP60 Engine Oil Pressure Gauge',
units: 'pa',
displayName: 'Engine Oil Pressure',
shortName: 'EOP',
warnMethod: [ 'visual' ],
alarmMethod: [ 'sound' ],
gaugeAlarmOn: true,
backlight: 2,
originalValue: 37,
gaugeUnits: 'psi',
zones: [
{
lower: 186158.43899999998,
state: 'alarm',
message: 'Engine oil pressure at lowest threshold'
},
{
upper: 448159.20499999996,
state: 'alarm',
message: 'Engine oil pressure at highest threshold'
}
]
}
)

toFull(delta).should.be.validSignalK
})
it('Converts engine coolant temperature using individual parser', () => {
const delta = new Parser().parse('$PBVE,EDOIADOKACABABAAAACAPPCMABCGADABDOAEGL*20')
delta.updates[0].values.should.contain.an.item.with.property('path', 'propulsion.0.coolantTemperature')
delta.updates[0].values[0].value.should.equal(399.26111111111106)
delta.updates[0].values[0].meta.should.deep.equal(
{
description: 'CruzPro T30/T60 Engine Coolant Temperature Gauge',
units: 'k',
displayName: 'Engine Coolant Temperature',
shortName: 'ECT',
warnMethod: ['visual'],
alarmMethod: ['sound'],
gaugeAlarmOn: false,
backlight: 2,
originalValue: 259,
gaugeUnits: 'f',
zones: [
{
lower: 2406.4833333333336,
state: 'alarm',
message: 'Engine coolant temperature at lowest threshold'
},
{
upper: 279.81666666666666,
state: 'alarm',
message: 'Engine coolant temperature at highest threshold'
}
]
}
)

toFull(delta).should.be.validSignalK
})
//currently only produce code D is supported so test that all others do not process
it('Will not convert if productCode not D', () => {
should.equal(new Parser().parse('$PBVE,FGLABCGJAAAHADEE*21'),undefined)

it('Should not parse if product code is F, B, or A', () => {
should.not.exist(new Parser().parse('$PBVE,FGLABCGJAAAHADEE*21'))
should.not.exist(new Parser().parse('$PBVE,BEAAADAAABFKBCIIBDAGOOABAAAAADAAAAOIACAAONFMAKCAADAAAAHG*2A'))
should.not.exist(new Parser().parse('$PBVE,AQHKAAAACIAAAAABAAGEDOBCAAAAFLABCKAADIAADIAAAAAANDEN*3F'))
})

})
4 changes: 2 additions & 2 deletions test/PNKEP.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
*/

const Parser = require('../lib')
const toFull = require("./toFull")
const chai = require("chai")
const should = chai.Should()
chai.use(require('@signalk/signalk-schema').chaiModule)
const toFull = require("./toFull")

chai.use(require('@signalk/signalk-schema').chaiModule)
chai.use(require("chai-things"))

describe("PNKEP", () => {
Expand Down