diff --git a/README.md b/README.md index 6478c93..002323e 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ const { messages, errors } = decoder.read({ convertDateTimesToDates: true, includeUnknownData: false, mergeHeartRates: true + decodeMemoGlobs: false, }); ```` #### mesgListener = (messageNumber, message) => {} @@ -178,6 +179,10 @@ When true unknown field values are stored in the message using the field id as t ```` #### mergeHeartRates: true | false When true automatically merge heart rate values from HR messages into the Record messages. This option requires the applyScaleAndOffset and expandComponents options to be enabled. This option has no effect on the Record messages when no HR messages are present in the decoded messages. + +#### decodeMemoGlobs: true | false +When true, the decoder will reconstruct strings from memoGlob messages. Each reconstructed string will overwrite the targeted message field. + ## Creating Streams Stream objects contain the binary FIT data to be decoded. Streams objects can be created from byte-arrays, ArrayBuffers, and Node.js Buffers. Internally the Stream class uses an ArrayBuffer to manage the byte stream. #### From a Byte Array diff --git a/package.json b/package.json index a426760..31aa650 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@garmin/fitsdk", - "version": "21.171.0", + "version": "21.178.0", "description": "FIT JavaScript SDK", "main": "src/index.js", "type": "module", diff --git a/src/accumulator.js b/src/accumulator.js index 0268cd7..2032c10 100644 --- a/src/accumulator.js +++ b/src/accumulator.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.171.0Release -// Tag = production/release/21.171.0-0-g57fed75 +// Profile Version = 21.178.0Release +// Tag = production/release/21.178.0-0-g3bea629 ///////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/bit-stream.js b/src/bit-stream.js index d9b8ca4..cddb035 100644 --- a/src/bit-stream.js +++ b/src/bit-stream.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.171.0Release -// Tag = production/release/21.171.0-0-g57fed75 +// Profile Version = 21.178.0Release +// Tag = production/release/21.178.0-0-g3bea629 ///////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/crc-calculator.js b/src/crc-calculator.js index 818bd6b..252b6b7 100644 --- a/src/crc-calculator.js +++ b/src/crc-calculator.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.171.0Release -// Tag = production/release/21.171.0-0-g57fed75 +// Profile Version = 21.178.0Release +// Tag = production/release/21.178.0-0-g3bea629 ///////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/decoder.js b/src/decoder.js index c57c470..5e5f96d 100644 --- a/src/decoder.js +++ b/src/decoder.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.171.0Release -// Tag = production/release/21.171.0-0-g57fed75 +// Profile Version = 21.178.0Release +// Tag = production/release/21.178.0-0-g3bea629 ///////////////////////////////////////////////////////////////////////////////////////////// @@ -729,8 +729,8 @@ class Decoder { return rawFieldValue; } - const scale = Array.isArray(field?.scale ?? 1) ? field?.scale[0] : field?.scale ?? 1; - const offset = Array.isArray(field?.offset ?? 1) ? field?.offset[0] : field?.offset ?? 0; + const scale = Array.isArray(field?.scale ?? FIT.FIELD_DEFAULT_SCALE) ? field?.scale[0] : field?.scale ?? FIT.FIELD_DEFAULT_SCALE; + const offset = Array.isArray(field?.offset ?? FIT.FIELD_DEFAULT_OFFSET) ? field?.offset[0] : field?.offset ?? FIT.FIELD_DEFAULT_OFFSET; try { if (Array.isArray(rawFieldValue)) { diff --git a/src/encoder.js b/src/encoder.js index c7e5d3f..0bd6c8d 100644 --- a/src/encoder.js +++ b/src/encoder.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.171.0Release -// Tag = production/release/21.171.0-0-g57fed75 +// Profile Version = 21.178.0Release +// Tag = production/release/21.178.0-0-g3bea629 ///////////////////////////////////////////////////////////////////////////////////////////// @@ -20,9 +20,6 @@ import Utils from "./utils.js"; const HEADER_WITH_CRC_SIZE = 14; const HEADER_WITHOUT_CRC_SIZE = 12; -const FIELD_DEFAULT_SCALE = 1; -const FIELD_DEFAULT_OFFSET = 0; - /** * A class for encoding FIT files. * @class @@ -131,7 +128,7 @@ class Encoder { throw new Error("addDeveloperField() - one or more developerDataIndex values are null.", { cause: { key, - developerDataIdMesg, + developerDataIdMesg, fieldDescriptionMesg } }); @@ -141,7 +138,7 @@ class Encoder { throw new Error("addDeveloperField() - developerDataIndex values do not match.", { cause: { key, - developerDataIdMesg, + developerDataIdMesg, fieldDescriptionMesg } }); @@ -215,12 +212,10 @@ class Encoder { throw new Error(); } - const scale = fieldDefinition.components.length > 1 ? FIELD_DEFAULT_SCALE : fieldDefinition.scale; - const offset = fieldDefinition.components.length > 1 ? FIELD_DEFAULT_OFFSET : fieldDefinition.offset; - const hasScaleOrOffset = (scale != FIELD_DEFAULT_SCALE || offset != FIELD_DEFAULT_OFFSET); + const hasScaleOrOffset = (fieldDefinition.scale != FIT.FIELD_DEFAULT_SCALE || fieldDefinition.offset != FIT.FIELD_DEFAULT_OFFSET); if (hasScaleOrOffset) { - const scaledValue = (value + offset) * scale; + const scaledValue = (value + fieldDefinition.offset) * fieldDefinition.scale; return FIT.FloatingPointFieldTypes.includes(fieldDefinition.type) ? scaledValue : Math.round(scaledValue); } diff --git a/src/fit.js b/src/fit.js index 62fbbba..efb6f1e 100644 --- a/src/fit.js +++ b/src/fit.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.171.0Release -// Tag = production/release/21.171.0-0-g57fed75 +// Profile Version = 21.178.0Release +// Tag = production/release/21.178.0-0-g3bea629 ///////////////////////////////////////////////////////////////////////////////////////////// @@ -172,6 +172,8 @@ export default { isNumberStringDateOrBoolean, isNotNumberStringDateOrBoolean, MAX_FIELD_SIZE: 255, + FIELD_DEFAULT_SCALE: 1, + FIELD_DEFAULT_OFFSET: 0, MESG_DEFINITION_MASK: 0x40, LOCAL_MESG_NUM_MASK: 0x0F, ARCH_LITTLE_ENDIAN: 0x00, diff --git a/src/index.js b/src/index.js index 91737dc..9913ce5 100644 --- a/src/index.js +++ b/src/index.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.171.0Release -// Tag = production/release/21.171.0-0-g57fed75 +// Profile Version = 21.178.0Release +// Tag = production/release/21.178.0-0-g3bea629 ///////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/mesg-definition.js b/src/mesg-definition.js index 492406b..5a342e5 100644 --- a/src/mesg-definition.js +++ b/src/mesg-definition.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.171.0Release -// Tag = production/release/21.171.0-0-g57fed75 +// Profile Version = 21.178.0Release +// Tag = production/release/21.178.0-0-g3bea629 ///////////////////////////////////////////////////////////////////////////////////////////// @@ -56,14 +56,20 @@ class MesgDefinition { const baseType = FIT.FieldTypeToBaseType[fieldProfile[1].baseType]; const baseTypeDef = FIT.BaseTypeDefinitions[baseType]; + let scale = fieldProfile[1].components.length > 1 ? FIT.FIELD_DEFAULT_SCALE : fieldProfile[1].scale; + let offset = fieldProfile[1].components.length > 1 ? FIT.FIELD_DEFAULT_OFFSET : fieldProfile[1].offset; + + scale = Array.isArray(scale) ? scale[0] : scale ?? FIT.FIELD_DEFAULT_SCALE; + offset = Array.isArray(offset) ? offset[0] : offset ?? FIT.FIELD_DEFAULT_OFFSET; + this.fieldDefinitions.push({ name: fieldName, num: fieldProfile[1].num, size: this.#fieldSize(mesg[fieldName], baseTypeDef), baseType: baseType, type: fieldProfile[1].type, - scale: fieldProfile[1].scale, - offset: fieldProfile[1].offset, + scale, + offset, components: fieldProfile[1].components, }); }); diff --git a/src/output-stream.js b/src/output-stream.js index 992f38c..7b6efe7 100644 --- a/src/output-stream.js +++ b/src/output-stream.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.171.0Release -// Tag = production/release/21.171.0-0-g57fed75 +// Profile Version = 21.178.0Release +// Tag = production/release/21.178.0-0-g3bea629 ///////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/profile.js b/src/profile.js index 4a52091..ce67945 100644 --- a/src/profile.js +++ b/src/profile.js @@ -5,15 +5,15 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.171.0Release -// Tag = production/release/21.171.0-0-g57fed75 +// Profile Version = 21.178.0Release +// Tag = production/release/21.178.0-0-g3bea629 ///////////////////////////////////////////////////////////////////////////////////////////// const Profile = { version: { major: 21, - minor: 171, + minor: 178, patch: 0, type: "Release" }, @@ -22980,6 +22980,95 @@ const Profile = { subFields: [] }, }, + }, + 470: { + num: 470, + name: "sleepDisruptionSeverityPeriod", + messagesKey: "sleepDisruptionSeverityPeriodMesgs", + fields: { + 254: { + num: 254, + name: "messageIndex", + type: "messageIndex", + baseType: "uint16", + array: false, + scale: 1, + offset: 0, + units: "", + bits: [], + components: [], + isAccumulated: false, + hasComponents: false, + subFields: [] + }, + 253: { + num: 253, + name: "timestamp", + type: "dateTime", + baseType: "uint32", + array: false, + scale: 1, + offset: 0, + units: "", + bits: [], + components: [], + isAccumulated: false, + hasComponents: false, + subFields: [] + }, + 0: { + num: 0, + name: "severity", + type: "sleepDisruptionSeverity", + baseType: "enum", + array: false, + scale: 1, + offset: 0, + units: "", + bits: [], + components: [], + isAccumulated: false, + hasComponents: false, + subFields: [] + }, + }, + }, + 471: { + num: 471, + name: "sleepDisruptionOvernightSeverity", + messagesKey: "sleepDisruptionOvernightSeverityMesgs", + fields: { + 253: { + num: 253, + name: "timestamp", + type: "dateTime", + baseType: "uint32", + array: false, + scale: 1, + offset: 0, + units: "", + bits: [], + components: [], + isAccumulated: false, + hasComponents: false, + subFields: [] + }, + 0: { + num: 0, + name: "severity", + type: "sleepDisruptionSeverity", + baseType: "enum", + array: false, + scale: 1, + offset: 0, + units: "", + bits: [], + components: [], + isAccumulated: false, + hasComponents: false, + subFields: [] + }, + }, }, 398: { num: 398, @@ -23216,6 +23305,8 @@ types: { 393: "diveApneaAlarm", 398: "skinTempOvernight", 409: "hsaWristTemperatureData", // Message number for the HSA wrist temperature data message + 470: "sleepDisruptionSeverityPeriod", + 471: "sleepDisruptionOvernightSeverity", 0xFF00: "mfgRangeMin", // 0xFF00 - 0xFFFE reserved for manufacturer specific messages 0xFFFE: "mfgRangeMax", // 0xFF00 - 0xFFFE reserved for manufacturer specific messages }, @@ -24271,6 +24362,7 @@ types: { 333: "tektroRacingProducts", 334: "daradInnovationCorporation", 335: "cycloptim", + 337: "runna", 5759: "actigraphcorp", }, garminProduct: { @@ -24716,9 +24808,11 @@ types: { 4586: "instinct3Amoled45mm", 4587: "instinct3Amoled50mm", 4588: "descentG2", + 4603: "venuX1", 4606: "hrm200", 4625: "vivoactive6", 4647: "approachS44", + 4655: "edgeMtb", 4656: "approachS50", 4666: "fenixE", 4759: "instinct3Solar50mm", @@ -27756,6 +27850,12 @@ types: { 2: "threatApproaching", 3: "threatApproachingFast", }, + sleepDisruptionSeverity: { + 0: "none", + 1: "low", + 2: "medium", + 3: "high", + }, maxMetSpeedSource: { 0: "onboardGps", 1: "connectedGps", @@ -27897,6 +27997,8 @@ MesgNum : { TANK_UPDATE: 319, TANK_SUMMARY: 323, SLEEP_ASSESSMENT: 346, + SLEEP_DISRUPTION_SEVERITY_PERIOD: 470, + SLEEP_DISRUPTION_OVERNIGHT_SEVERITY: 471, SKIN_TEMP_OVERNIGHT: 398, PAD: 105, } diff --git a/src/stream.js b/src/stream.js index f329930..1f9fede 100644 --- a/src/stream.js +++ b/src/stream.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.171.0Release -// Tag = production/release/21.171.0-0-g57fed75 +// Profile Version = 21.178.0Release +// Tag = production/release/21.178.0-0-g3bea629 ///////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/utils-hr-mesg.js b/src/utils-hr-mesg.js index 7518ba5..4406de6 100644 --- a/src/utils-hr-mesg.js +++ b/src/utils-hr-mesg.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.171.0Release -// Tag = production/release/21.171.0-0-g57fed75 +// Profile Version = 21.178.0Release +// Tag = production/release/21.178.0-0-g3bea629 ///////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/utils-internal.js b/src/utils-internal.js index 9de6ab4..f348db0 100644 --- a/src/utils-internal.js +++ b/src/utils-internal.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.171.0Release -// Tag = production/release/21.171.0-0-g57fed75 +// Profile Version = 21.178.0Release +// Tag = production/release/21.178.0-0-g3bea629 ///////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/utils-memo-glob.js b/src/utils-memo-glob.js index 207e26c..e3876ce 100644 --- a/src/utils-memo-glob.js +++ b/src/utils-memo-glob.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.171.0Release -// Tag = production/release/21.171.0-0-g57fed75 +// Profile Version = 21.178.0Release +// Tag = production/release/21.178.0-0-g3bea629 ///////////////////////////////////////////////////////////////////////////////////////////// import Profile from "./profile.js"; diff --git a/src/utils.js b/src/utils.js index 4ffd2d6..fc391f1 100644 --- a/src/utils.js +++ b/src/utils.js @@ -5,8 +5,8 @@ // Transfer (FIT) Protocol License. ///////////////////////////////////////////////////////////////////////////////////////////// // ****WARNING**** This file is auto-generated! Do NOT edit this file. -// Profile Version = 21.171.0Release -// Tag = production/release/21.171.0-0-g57fed75 +// Profile Version = 21.178.0Release +// Tag = production/release/21.178.0-0-g3bea629 ///////////////////////////////////////////////////////////////////////////////////////////// diff --git a/test/data/HrmPluginTestActivity.fit b/test/data/HrmPluginTestActivity.fit index d12136e..5d2d70d 100644 Binary files a/test/data/HrmPluginTestActivity.fit and b/test/data/HrmPluginTestActivity.fit differ diff --git a/test/data/WithGearChangeData.fit b/test/data/WithGearChangeData.fit index bfc68f0..e100381 100644 Binary files a/test/data/WithGearChangeData.fit and b/test/data/WithGearChangeData.fit differ diff --git a/test/encoder.test.js b/test/encoder.test.js index 76adba6..ba860bf 100644 --- a/test/encoder.test.js +++ b/test/encoder.test.js @@ -280,5 +280,31 @@ describe("Encoder-Decoder Integration Tests", () => { expect(decodedRecordMesg.speed).toEqual(recordMesg.speed); expect(decodedRecordMesg.enhancedSpeed).toEqual(recordMesg.speed); }); + + test("Encoder should correctly apply scale and offset to fields with singular expanded components", () => { + const recordMesg = { + heartRate: 55, + altitude: 100, + speed: 1.5 + } + + try { + const encoder = new Encoder(); + encoder.onMesg(Profile.MesgNum.RECORD, recordMesg); + const uint8Array = encoder.close(); + + const decoder = new Decoder(Stream.fromByteArray(uint8Array)); + const { messages, errors, } = decoder.read(); + + expect(errors.length).toBe(0); + expect(messages.recordMesgs.length).toBe(1); + + expect(messages.recordMesgs[0]).toMatchObject(recordMesg); + } + catch (error) { + console.error(`${error.name}: ${error.message} \n${JSON.stringify(error.cause, null, 3)}`); + throw error; + } + }); });