lora-packet
Version:
LoRa packet decoder/encoder
843 lines • 36.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LorawanVersion = void 0;
const util_1 = require("./util");
const crypto_1 = require("./crypto");
const mic_1 = require("./mic");
var MType;
(function (MType) {
MType[MType["JOIN_REQUEST"] = 0] = "JOIN_REQUEST";
MType[MType["JOIN_ACCEPT"] = 1] = "JOIN_ACCEPT";
MType[MType["UNCONFIRMED_DATA_UP"] = 2] = "UNCONFIRMED_DATA_UP";
MType[MType["UNCONFIRMED_DATA_DOWN"] = 3] = "UNCONFIRMED_DATA_DOWN";
MType[MType["CONFIRMED_DATA_UP"] = 4] = "CONFIRMED_DATA_UP";
MType[MType["CONFIRMED_DATA_DOWN"] = 5] = "CONFIRMED_DATA_DOWN";
MType[MType["REJOIN_REQUEST"] = 6] = "REJOIN_REQUEST";
})(MType || (MType = {}));
const MTYPE_DESCRIPTIONS = {
[MType.JOIN_REQUEST]: "Join Request",
[MType.JOIN_ACCEPT]: "Join Accept",
[MType.UNCONFIRMED_DATA_UP]: "Unconfirmed Data Up",
[MType.UNCONFIRMED_DATA_DOWN]: "Unconfirmed Data Down",
[MType.CONFIRMED_DATA_UP]: "Confirmed Data Up",
[MType.CONFIRMED_DATA_DOWN]: "Confirmed Data Down",
[MType.REJOIN_REQUEST]: "Rejoin Request",
};
const DESCRIPTIONS_MTYPE = Object.keys(MTYPE_DESCRIPTIONS).reduce((acc, key) => {
const mTypeKey = key; // Cast the key to MType
const description = MTYPE_DESCRIPTIONS[mTypeKey];
acc[description] = mTypeKey;
return acc;
}, {});
const PACKET_STRUCTURES = {
JOIN_REQUEST: {
AppEUI: { start: 1, end: 9 },
DevEUI: { start: 9, end: 17 },
DevNonce: { start: 17, end: 19 },
},
JOIN_ACCEPT: {
AppNonce: { start: 1, end: 4 },
NetID: { start: 4, end: 7 },
DevAddr: { start: 7, end: 11 },
DLSettings: { start: 11, end: 12 },
RxDelay: { start: 12, end: 13 },
},
REJOIN_TYPE_1: {
NetID: { start: 2, end: 5 },
DevEUI: { start: 5, end: 13 },
RJCount0: { start: 13, end: 15 },
},
REJOIN_TYPE_2: {
JoinEUI: { start: 2, end: 10 },
DevEUI: { start: 10, end: 18 },
RJCount1: { start: 13, end: 15 },
},
};
var LorawanVersion;
(function (LorawanVersion) {
LorawanVersion["V1_0"] = "1.0";
LorawanVersion["V1_1"] = "1.1";
})(LorawanVersion || (LorawanVersion = {}));
exports.LorawanVersion = LorawanVersion;
var Masks;
(function (Masks) {
Masks[Masks["FCTRL_ADR"] = 128] = "FCTRL_ADR";
Masks[Masks["FCTRL_ADRACKREQ"] = 64] = "FCTRL_ADRACKREQ";
Masks[Masks["FCTRL_ACK"] = 32] = "FCTRL_ACK";
Masks[Masks["FCTRL_FPENDING"] = 16] = "FCTRL_FPENDING";
Masks[Masks["DLSETTINGS_RXONEDROFFSET_MASK"] = 112] = "DLSETTINGS_RXONEDROFFSET_MASK";
Masks[Masks["DLSETTINGS_RXONEDROFFSET_POS"] = 4] = "DLSETTINGS_RXONEDROFFSET_POS";
Masks[Masks["DLSETTINGS_RXTWODATARATE_MASK"] = 15] = "DLSETTINGS_RXTWODATARATE_MASK";
Masks[Masks["DLSETTINGS_RXTWODATARATE_POS"] = 0] = "DLSETTINGS_RXTWODATARATE_POS";
Masks[Masks["DLSETTINGS_OPTNEG_MASK"] = 128] = "DLSETTINGS_OPTNEG_MASK";
Masks[Masks["DLSETTINGS_OPTNEG_POS"] = 7] = "DLSETTINGS_OPTNEG_POS";
Masks[Masks["RXDELAY_DEL_MASK"] = 15] = "RXDELAY_DEL_MASK";
Masks[Masks["RXDELAY_DEL_POS"] = 0] = "RXDELAY_DEL_POS";
})(Masks || (Masks = {}));
function extractBytesFromBuffer(buffer, start, end) {
return (0, util_1.reverseBuffer)(buffer.slice(start, end));
}
function extractStructuredBytesFromBuffer(buffer, name) {
const structure = PACKET_STRUCTURES[name];
const ret = {};
for (const key in structure) {
if (structure.hasOwnProperty(key)) {
ret[key] = extractBytesFromBuffer(buffer, structure[key].start, structure[key].end);
}
}
return ret;
}
class LoraPacket {
static fromWire(buffer) {
const payload = new LoraPacket();
payload._initfromWire(buffer);
return payload;
}
static fromFields(fields, AppSKey, NwkSKey, AppKey, FCntMSBytes, ConfFCntDownTxDrTxCh) {
if (!FCntMSBytes)
FCntMSBytes = Buffer.alloc(2, 0);
const payload = new LoraPacket();
payload._initFromFields(fields);
if (payload.isDataMessage()) {
// to encrypt, need NwkSKey if port=0, else AppSKey
const port = payload.getFPort();
if (port !== null && ((port === 0 && (NwkSKey === null || NwkSKey === void 0 ? void 0 : NwkSKey.length) === 16) || (port > 0 && (AppSKey === null || AppSKey === void 0 ? void 0 : AppSKey.length) === 16))) {
// crypto is reversible (just XORs FRMPayload), so we can
// just do "decrypt" on the plaintext to get ciphertext
let ciphertext;
if (port === 0 && (NwkSKey === null || NwkSKey === void 0 ? void 0 : NwkSKey.length) === 16 && (AppSKey === null || AppSKey === void 0 ? void 0 : AppSKey.length) === 16 && (AppKey === null || AppKey === void 0 ? void 0 : AppKey.length) === 16) {
ciphertext = (0, crypto_1.decrypt)(payload, undefined, AppSKey, FCntMSBytes);
}
else {
ciphertext = (0, crypto_1.decrypt)(payload, AppSKey, NwkSKey, FCntMSBytes);
}
// overwrite payload with ciphertext
payload.FRMPayload = ciphertext;
// recalculate buffers to be ready for MIC calc'n
payload._mergeGroupFields();
if ((NwkSKey === null || NwkSKey === void 0 ? void 0 : NwkSKey.length) === 16) {
(0, mic_1.recalculateMIC)(payload, NwkSKey, AppKey, FCntMSBytes, ConfFCntDownTxDrTxCh);
payload._mergeGroupFields();
}
}
}
else if (payload._getMType() === MType.JOIN_REQUEST) {
if ((AppKey === null || AppKey === void 0 ? void 0 : AppKey.length) === 16) {
(0, mic_1.recalculateMIC)(payload, NwkSKey, AppKey, FCntMSBytes);
payload._mergeGroupFields();
}
}
else if (payload._getMType() === MType.JOIN_ACCEPT) {
if ((AppKey === null || AppKey === void 0 ? void 0 : AppKey.length) === 16) {
(0, mic_1.recalculateMIC)(payload, NwkSKey, AppKey, FCntMSBytes);
payload._mergeGroupFields();
const ciphertext = (0, crypto_1.decryptJoin)(payload, AppKey);
// overwrite payload with ciphertext
if (payload.MACPayloadWithMIC)
ciphertext.copy(payload.MACPayloadWithMIC);
}
}
return payload;
}
assignFromStructuredBuffer(buffer, structure) {
const fields = extractStructuredBytesFromBuffer(buffer, structure);
Object.assign(this, fields);
}
_initfromWire(contents) {
const incoming = Buffer.from(contents);
this.PHYPayload = incoming;
this.MHDR = incoming.slice(0, 1);
this.MACPayload = incoming.slice(1, incoming.length - 4);
this.MACPayloadWithMIC = incoming.slice(1, incoming.length);
this.MIC = incoming.slice(incoming.length - 4);
const mtype = this._getMType();
if (mtype == MType.JOIN_REQUEST) {
if (incoming.length < 5 + 18) {
throw new Error("contents too short for a Join Request");
}
this.assignFromStructuredBuffer(incoming, "JOIN_REQUEST");
}
else if (mtype == MType.JOIN_ACCEPT) {
if (incoming.length < 5 + 12) {
throw new Error("contents too short for a Join Accept");
}
this.assignFromStructuredBuffer(incoming, "JOIN_ACCEPT");
this.JoinReqType = Buffer.from([0xff]);
if (incoming.length == 13 + 16 + 4) {
this.CFList = incoming.slice(13, 13 + 16);
}
else {
this.CFList = Buffer.alloc(0);
}
}
else if (mtype == MType.REJOIN_REQUEST) {
this.RejoinType = incoming.slice(1, 1 + 1);
if (this.RejoinType[0] === 0 || this.RejoinType[0] === 2) {
if (incoming.length < 5 + 14) {
throw new Error("contents too short for a Rejoin Request (Type 0/2)");
}
this.assignFromStructuredBuffer(incoming, "REJOIN_TYPE_1");
}
else if (this.RejoinType[0] === 1) {
if (incoming.length < 5 + 19) {
throw new Error("contents too short for a Rejoin Request (Type 1)");
}
this.assignFromStructuredBuffer(incoming, "REJOIN_TYPE_2");
}
}
else if (this.isDataMessage()) {
this.DevAddr = (0, util_1.reverseBuffer)(incoming.slice(1, 5));
this.FCtrl = (0, util_1.reverseBuffer)(incoming.slice(5, 6));
this.FCnt = (0, util_1.reverseBuffer)(incoming.slice(6, 8));
const FCtrl = this.FCtrl.readInt8(0);
const FOptsLen = FCtrl & 0x0f;
this.FOpts = incoming.slice(8, 8 + FOptsLen);
const FHDR_length = 7 + FOptsLen;
this.FHDR = incoming.slice(1, 1 + FHDR_length);
if (FHDR_length == this.MACPayload.length) {
this.FPort = Buffer.alloc(0);
this.FRMPayload = Buffer.alloc(0);
}
else {
this.FPort = incoming.slice(FHDR_length + 1, FHDR_length + 2);
this.FRMPayload = incoming.slice(FHDR_length + 2, incoming.length - 4);
}
}
}
_initFromFields(userFields) {
if (typeof userFields.MType !== "undefined") {
let MTypeNo;
if (typeof userFields.MType === "number") {
MTypeNo = userFields.MType;
}
else if (typeof userFields.MType == "string") {
const mhdr_idx = DESCRIPTIONS_MTYPE[userFields.MType];
if (mhdr_idx >= 0) {
MTypeNo = mhdr_idx;
}
else {
throw new Error("MType is unknown");
}
}
else {
throw new Error("MType is required in a suitable format");
}
if (MTypeNo == MType.JOIN_REQUEST) {
this._initialiseJoinRequestPacketFromFields(userFields);
}
else if (MTypeNo == MType.JOIN_ACCEPT) {
this._initialiseJoinAcceptPacketFromFields(userFields);
}
else {
this._initialiseDataPacketFromFields(userFields);
}
}
else {
if (userFields.DevAddr && typeof userFields.payload !== "undefined") {
this._initialiseDataPacketFromFields(userFields);
}
else if (userFields.AppEUI && userFields.DevEUI && userFields.DevNonce) {
this._initialiseJoinRequestPacketFromFields(userFields);
}
else if (userFields.AppNonce && userFields.NetID && userFields.DevAddr) {
this._initialiseJoinAcceptPacketFromFields(userFields);
}
else {
throw new Error("No plausible packet");
}
}
}
_mergeGroupFields() {
if (this.MHDR && this.MIC) {
if (this._getMType() === MType.JOIN_REQUEST && this.AppEUI && this.DevEUI && this.DevNonce) {
this.MACPayload = Buffer.concat([
(0, util_1.reverseBuffer)(this.AppEUI),
(0, util_1.reverseBuffer)(this.DevEUI),
(0, util_1.reverseBuffer)(this.DevNonce),
]);
this.PHYPayload = Buffer.concat([this.MHDR, this.MACPayload, this.MIC]);
this.MACPayloadWithMIC = this.PHYPayload.slice(this.MHDR.length, this.PHYPayload.length);
}
else if (this._getMType() === MType.JOIN_ACCEPT &&
this.AppNonce &&
this.NetID &&
this.DevAddr &&
this.DLSettings &&
this.RxDelay &&
this.CFList) {
this.MACPayload = Buffer.concat([
(0, util_1.reverseBuffer)(this.AppNonce),
(0, util_1.reverseBuffer)(this.NetID),
(0, util_1.reverseBuffer)(this.DevAddr),
this.DLSettings,
this.RxDelay,
this.CFList,
]);
this.PHYPayload = Buffer.concat([this.MHDR, this.MACPayload, this.MIC]);
this.MACPayloadWithMIC = this.PHYPayload.slice(this.MHDR.length, this.PHYPayload.length);
}
else if (this.FCtrl && this.DevAddr && this.FPort && this.FCnt && this.FRMPayload && this.FOpts) {
this.FHDR = Buffer.concat([(0, util_1.reverseBuffer)(this.DevAddr), this.FCtrl, (0, util_1.reverseBuffer)(this.FCnt), this.FOpts]);
this.MACPayload = Buffer.concat([this.FHDR, this.FPort, this.FRMPayload]);
this.PHYPayload = Buffer.concat([this.MHDR, this.MACPayload, this.MIC]);
this.MACPayloadWithMIC = this.PHYPayload.slice(this.MHDR.length, this.PHYPayload.length);
}
}
}
_initialiseDataPacketFromFields(userFields) {
var _a, _b, _c, _d;
if (userFields.DevAddr && userFields.DevAddr.length == 4) {
this.DevAddr = Buffer.from(userFields.DevAddr);
}
else {
throw new Error("DevAddr is required in a suitable format");
}
if (typeof userFields.payload === "string") {
this.FRMPayload = Buffer.from(userFields.payload);
}
else if (userFields.payload instanceof Buffer) {
this.FRMPayload = Buffer.from(userFields.payload);
}
if (typeof userFields.MType !== "undefined") {
if (typeof userFields.MType === "number") {
this.MHDR = Buffer.alloc(1);
this.MHDR.writeUInt8(userFields.MType << 5, 0);
}
else if (typeof userFields.MType === "string") {
const mhdr_idx = DESCRIPTIONS_MTYPE[userFields.MType];
if (mhdr_idx >= 0) {
this.MHDR = Buffer.alloc(1);
this.MHDR.writeUInt8(mhdr_idx << 5, 0);
}
else {
throw new Error("MType is unknown");
}
}
else {
throw new Error("MType is required in a suitable format");
}
}
if (userFields.FCnt) {
if (userFields.FCnt instanceof Buffer && userFields.FCnt.length == 2) {
this.FCnt = Buffer.from(userFields.FCnt);
}
else if (typeof userFields.FCnt === "number") {
this.FCnt = Buffer.alloc(2);
this.FCnt.writeUInt16BE(userFields.FCnt, 0);
}
else {
throw new Error("FCnt is required in a suitable format");
}
}
if (typeof userFields.FOpts !== "undefined") {
if (typeof userFields.FOpts === "string") {
this.FOpts = Buffer.from(userFields.FOpts, "hex");
}
else if (userFields.FOpts instanceof Buffer) {
this.FOpts = Buffer.from(userFields.FOpts);
}
else {
throw new Error("FOpts is required in a suitable format");
}
if (15 < this.FOpts.length) {
throw new Error("Too many options for piggybacking");
}
}
else {
this.FOpts = Buffer.from("", "hex");
}
let fctrl = 0;
if ((_a = userFields.FCtrl) === null || _a === void 0 ? void 0 : _a.ADR) {
fctrl |= Masks.FCTRL_ADR;
}
if ((_b = userFields.FCtrl) === null || _b === void 0 ? void 0 : _b.ADRACKReq) {
fctrl |= Masks.FCTRL_ADRACKREQ;
}
if ((_c = userFields.FCtrl) === null || _c === void 0 ? void 0 : _c.ACK) {
fctrl |= Masks.FCTRL_ACK;
}
if ((_d = userFields.FCtrl) === null || _d === void 0 ? void 0 : _d.FPending) {
fctrl |= Masks.FCTRL_FPENDING;
}
fctrl |= this.FOpts.length & 0x0f;
this.FCtrl = Buffer.alloc(1);
this.FCtrl.writeUInt8(fctrl, 0);
if (!isNaN(userFields.FPort) && userFields.FPort >= 0 && userFields.FPort <= 255) {
this.FPort = Buffer.alloc(1);
this.FPort.writeUInt8(userFields.FPort, 0);
}
if (!(this === null || this === void 0 ? void 0 : this.MHDR)) {
this.MHDR = Buffer.alloc(1);
this.MHDR.writeUInt8(MType.UNCONFIRMED_DATA_UP << 5, 0);
}
if ((this === null || this === void 0 ? void 0 : this.FPort) == null) {
if ((this === null || this === void 0 ? void 0 : this.FRMPayload) && this.FRMPayload.length > 0) {
this.FPort = Buffer.from("01", "hex");
}
else {
this.FPort = Buffer.alloc(0);
}
}
if (!(this === null || this === void 0 ? void 0 : this.FPort) == null) {
this.FPort = Buffer.from("01", "hex");
}
if (!this.FCnt) {
this.FCnt = Buffer.from("0000", "hex");
}
if (!this.MIC) {
this.MIC = Buffer.from("EEEEEEEE", "hex");
}
this._mergeGroupFields();
}
_initialiseJoinRequestPacketFromFields(userFields) {
if (userFields.AppEUI && userFields.AppEUI.length == 8) {
this.AppEUI = Buffer.from(userFields.AppEUI);
}
else {
throw new Error("AppEUI is required in a suitable format");
}
if (userFields.DevEUI && userFields.DevEUI.length == 8) {
this.DevEUI = Buffer.from(userFields.DevEUI);
}
else {
throw new Error("DevEUI is required in a suitable format");
}
if (userFields.DevNonce && userFields.DevNonce.length == 2) {
this.DevNonce = Buffer.from(userFields.DevNonce);
}
else {
throw new Error("DevNonce is required in a suitable format");
}
if (userFields.FCnt) {
if (userFields.FCnt instanceof Buffer && userFields.FCnt.length == 2) {
this.FCnt = Buffer.from(userFields.FCnt);
}
else if (typeof userFields.FCnt === "number") {
this.FCnt = Buffer.alloc(2);
this.FCnt.writeUInt16BE(userFields.FCnt, 0);
}
else {
throw new Error("FCnt is required in a suitable format");
}
}
this.MHDR = Buffer.alloc(1);
this.MHDR.writeUInt8(MType.JOIN_REQUEST << 5, 0);
if (!this.MIC) {
this.MIC = Buffer.from("EEEEEEEE", "hex");
}
this._mergeGroupFields();
}
_initialiseJoinAcceptPacketFromFields(userFields) {
if (userFields.AppNonce && userFields.AppNonce.length == 3) {
this.AppNonce = Buffer.from(userFields.AppNonce);
}
else {
throw new Error("AppNonce is required in a suitable format");
}
if (userFields.NetID && userFields.NetID.length == 3) {
this.NetID = Buffer.from(userFields.NetID);
}
else {
throw new Error("NetID is required in a suitable format");
}
if (userFields.DevAddr && userFields.DevAddr.length == 4) {
this.DevAddr = Buffer.from(userFields.DevAddr);
}
else {
throw new Error("DevAddr is required in a suitable format");
}
if (userFields.DLSettings) {
if (userFields.DLSettings instanceof Buffer && userFields.DLSettings.length == 1) {
this.DLSettings = Buffer.from(userFields.DLSettings);
}
else if (typeof userFields.DLSettings === "number") {
this.DLSettings = Buffer.alloc(1);
this.DLSettings.writeUInt8(userFields.DLSettings, 0);
}
else {
throw new Error("DLSettings is required in a suitable format");
}
}
if (userFields.RxDelay) {
if (userFields.RxDelay instanceof Buffer && userFields.RxDelay.length == 1) {
this.RxDelay = Buffer.from(userFields.RxDelay);
}
else if (typeof userFields.RxDelay == "number") {
this.RxDelay = Buffer.alloc(1);
this.RxDelay.writeUInt8(userFields.RxDelay, 0);
}
else {
throw new Error("RxDelay is required in a suitable format");
}
}
if (userFields.CFList) {
if (userFields.CFList instanceof Buffer && (userFields.CFList.length == 0 || userFields.CFList.length == 16)) {
this.CFList = Buffer.from(userFields.CFList);
}
else {
throw new Error("CFList is required in a suitable format");
}
}
if (!userFields.JoinReqType) {
this.JoinReqType = Buffer.from("ff", "hex");
}
else {
if (userFields.JoinReqType instanceof Buffer && userFields.JoinReqType.length == 1) {
this.JoinReqType = Buffer.from(userFields.JoinReqType);
}
else if (typeof userFields.JoinReqType === "number") {
this.JoinReqType = Buffer.alloc(1);
this.JoinReqType.writeUInt8(userFields.JoinReqType, 0);
}
else {
throw new Error("JoinReqType is required in a suitable format");
}
}
if (userFields.AppEUI && userFields.AppEUI.length == 8) {
this.AppEUI = Buffer.from(userFields.AppEUI);
}
else if (this.getDLSettingsOptNeg()) {
throw new Error("AppEUI/JoinEUI is required in a suitable format");
}
if (userFields.DevNonce && userFields.DevNonce.length == 2) {
this.DevNonce = Buffer.from(userFields.DevNonce);
}
else if (this.getDLSettingsOptNeg()) {
throw new Error("DevNonce is required in a suitable format");
}
if (!this.DLSettings) {
this.DLSettings = Buffer.from("00", "hex");
}
if (!this.RxDelay) {
this.RxDelay = Buffer.from("00", "hex");
}
if (!this.CFList) {
this.CFList = Buffer.from("", "hex");
}
this.MHDR = Buffer.alloc(1);
this.MHDR.writeUInt8(MType.JOIN_ACCEPT << 5, 0);
if (!this.MIC) {
this.MIC = Buffer.from("EEEEEEEE", "hex");
}
this._mergeGroupFields();
}
_getMType() {
if (this.MHDR)
return (this.MHDR.readUInt8(0) & 0xff) >> 5;
return -1;
}
isDataMessage() {
const mtype = this._getMType();
return mtype >= MType.UNCONFIRMED_DATA_UP && mtype <= MType.CONFIRMED_DATA_DOWN;
}
isConfirmed() {
const mtype = this._getMType();
return mtype === MType.CONFIRMED_DATA_DOWN || mtype === MType.CONFIRMED_DATA_UP;
}
/**
* Provide MType as a string
*/
getMType() {
return MTYPE_DESCRIPTIONS[this._getMType()] || "Proprietary";
}
/**
* Provide Direction as a string
*/
getDir() {
const mType = this._getMType();
if (mType > 5)
return null;
if (mType % 2 == 0)
return "up";
return "down";
}
/**
* Provide FPort as a number
*/
getFPort() {
if (this.FPort && this.FPort.length)
return this.FPort.readUInt8(0);
return null;
}
/**
* Provide FCnt as a number
*/
getFCnt() {
if (this.FCnt)
return this.FCnt.readUInt16BE(0);
return null;
}
/**
* Provide FCtrl.ACK as a flag
*/
getFCtrlACK() {
if (!this.FCtrl)
return null;
return !!(this.FCtrl.readUInt8(0) & Masks.FCTRL_ACK);
}
/**
* Provide FCtrl.ADR as a flag
*/
getFCtrlADR() {
if (!this.FCtrl)
return null;
return !!(this.FCtrl.readUInt8(0) & Masks.FCTRL_ADR);
}
/**
* Provide FCtrl.ADRACKReq as a flag
*/
getFCtrlADRACKReq() {
if (!this.FCtrl)
return null;
return !!(this.FCtrl.readUInt8(0) & Masks.FCTRL_ADRACKREQ);
}
/**
* Provide FCtrl.FPending as a flag
*/
getFCtrlFPending() {
if (!this.FCtrl)
return null;
return !!(this.FCtrl.readUInt8(0) & Masks.FCTRL_FPENDING);
}
/**
* Provide DLSettings.RX1DRoffset as integer
*/
getDLSettingsRxOneDRoffset() {
if (!this.DLSettings)
return null;
return (this.DLSettings.readUInt8(0) & Masks.DLSETTINGS_RXONEDROFFSET_MASK) >> Masks.DLSETTINGS_RXONEDROFFSET_POS;
}
/**
* Provide DLSettings.RX2DataRate as integer
*/
getDLSettingsRxTwoDataRate() {
if (!this.DLSettings)
return null;
return (this.DLSettings.readUInt8(0) & Masks.DLSETTINGS_RXTWODATARATE_MASK) >> Masks.DLSETTINGS_RXTWODATARATE_POS;
}
/**
* Provide DLSettings.OptNeg as boolean
*/
getDLSettingsOptNeg() {
if (!this.DLSettings)
return null;
return (this.DLSettings.readUInt8(0) & Masks.DLSETTINGS_OPTNEG_MASK) >> Masks.DLSETTINGS_OPTNEG_POS === 1;
}
/**
* Provide RxDelay.Del as integer
*/
getRxDelayDel() {
if (!this.RxDelay)
return null;
return (this.RxDelay.readUInt8(0) & Masks.RXDELAY_DEL_MASK) >> Masks.RXDELAY_DEL_POS;
}
/**
* Provide CFList.FreqChFour as buffer
*/
getCFListFreqChFour() {
if (this.CFList && this.CFList.length === 16) {
return (0, util_1.reverseBuffer)(this.CFList.slice(0, 0 + 3));
}
else {
return Buffer.alloc(0);
}
}
/**
* Provide CFList.FreqChFive as buffer
*/
getCFListFreqChFive() {
if (this.CFList && this.CFList.length === 16) {
return (0, util_1.reverseBuffer)(this.CFList.slice(3, 3 + 3));
}
else {
return Buffer.alloc(0);
}
}
/**
* Provide CFList.FreqChSix as buffer
*/
getCFListFreqChSix() {
if (this.CFList && this.CFList.length === 16) {
return (0, util_1.reverseBuffer)(this.CFList.slice(6, 6 + 3));
}
else {
return Buffer.alloc(0);
}
}
/**
* Provide CFList.FreqChSeven as buffer
*/
getCFListFreqChSeven() {
if (this.CFList && this.CFList.length === 16) {
return (0, util_1.reverseBuffer)(this.CFList.slice(9, 9 + 3));
}
else {
return Buffer.alloc(0);
}
}
/**
* Provide CFList.FreqChEight as buffer
*/
getCFListFreqChEight() {
if (this.CFList && this.CFList.length === 16) {
return (0, util_1.reverseBuffer)(this.CFList.slice(12, 12 + 3));
}
else {
return Buffer.alloc(0);
}
}
getBuffers() {
return this;
}
decryptFOpts(NwkSEncKey, NwkSKey, FCntMSBytes, ConfFCntDownTxDrTxCh) {
return this.encryptFOpts(NwkSEncKey, NwkSKey, FCntMSBytes, ConfFCntDownTxDrTxCh);
}
encryptFOpts(NwkSEncKey, SNwkSIntKey, FCntMSBytes, ConfFCntDownTxDrTxCh) {
if (!this.FOpts)
return Buffer.alloc(0);
if (!NwkSEncKey || (NwkSEncKey === null || NwkSEncKey === void 0 ? void 0 : NwkSEncKey.length) !== 16)
throw new Error("NwkSEncKey must be 16 bytes");
this.FOpts = (0, crypto_1.decryptFOpts)(this, NwkSEncKey, FCntMSBytes);
this._mergeGroupFields();
if ((SNwkSIntKey === null || SNwkSIntKey === void 0 ? void 0 : SNwkSIntKey.length) === 16) {
(0, mic_1.recalculateMIC)(this, SNwkSIntKey, undefined, FCntMSBytes, ConfFCntDownTxDrTxCh);
this._mergeGroupFields();
}
return this.FOpts;
}
getPHYPayload() {
return this.PHYPayload;
}
isJoinRequestMessage() {
return this._getMType() == MType.JOIN_REQUEST;
}
isRejoinRequestMessage() {
return this._getMType() == MType.REJOIN_REQUEST;
}
// deprecated (bogus capitalisation)
isReJoinRequestMessage() {
return this._getMType() == MType.REJOIN_REQUEST;
}
isJoinAcceptMessage() {
return this._getMType() == MType.JOIN_ACCEPT;
}
toString() {
let msg = "";
if (this.isJoinRequestMessage()) {
msg += " Message Type = Join Request" + "\n";
msg += " PHYPayload = " + (0, util_1.asHexString)(this.PHYPayload).toUpperCase() + "\n";
msg += "\n";
msg += " ( PHYPayload = MHDR[1] | MACPayload[..] | MIC[4] )\n";
msg += " MHDR = " + (0, util_1.asHexString)(this.MHDR) + "\n";
msg += " MACPayload = " + (0, util_1.asHexString)(this.MACPayload) + "\n";
msg += " MIC = " + (0, util_1.asHexString)(this.MIC) + "\n";
msg += "\n";
msg += " ( MACPayload = AppEUI[8] | DevEUI[8] | DevNonce[2] )\n";
msg += " AppEUI = " + (0, util_1.asHexString)(this.AppEUI) + "\n";
msg += " DevEUI = " + (0, util_1.asHexString)(this.DevEUI) + "\n";
msg += " DevNonce = " + (0, util_1.asHexString)(this.DevNonce) + "\n";
}
else if (this.isJoinAcceptMessage()) {
msg += " Message Type = Join Accept" + "\n";
msg += " PHYPayload = " + (0, util_1.asHexString)(this.PHYPayload).toUpperCase() + "\n";
msg += "\n";
msg += " ( PHYPayload = MHDR[1] | MACPayload[..] | MIC[4] )\n";
msg += " MHDR = " + (0, util_1.asHexString)(this.MHDR) + "\n";
msg += " MACPayload = " + (0, util_1.asHexString)(this.MACPayload) + "\n";
msg += " MIC = " + (0, util_1.asHexString)(this.MIC) + "\n";
msg += "\n";
msg +=
" ( MACPayload = AppNonce[3] | NetID[3] | DevAddr[4] | DLSettings[1] | RxDelay[1] | CFList[0|15] )\n";
msg += " AppNonce = " + (0, util_1.asHexString)(this.AppNonce) + "\n";
msg += " NetID = " + (0, util_1.asHexString)(this.NetID) + "\n";
msg += " DevAddr = " + (0, util_1.asHexString)(this.DevAddr) + "\n";
msg += " DLSettings = " + (0, util_1.asHexString)(this.DLSettings) + "\n";
msg += " RxDelay = " + (0, util_1.asHexString)(this.RxDelay) + "\n";
msg += " CFList = " + (0, util_1.asHexString)(this.CFList) + "\n";
msg += "\n";
msg += "DLSettings.RX1DRoffset = " + this.getDLSettingsRxOneDRoffset() + "\n";
msg += "DLSettings.RX2DataRate = " + this.getDLSettingsRxTwoDataRate() + "\n";
msg += " RxDelay.Del = " + this.getRxDelayDel() + "\n";
msg += "\n";
if (this.CFList.length === 16) {
msg += " ( CFList = FreqCh4[3] | FreqCh5[3] | FreqCh6[3] | FreqCh7[3] | FreqCh8[3] )\n";
msg += " FreqCh4 = " + (0, util_1.asHexString)(this.getCFListFreqChFour()) + "\n";
msg += " FreqCh5 = " + (0, util_1.asHexString)(this.getCFListFreqChFive()) + "\n";
msg += " FreqCh6 = " + (0, util_1.asHexString)(this.getCFListFreqChSix()) + "\n";
msg += " FreqCh7 = " + (0, util_1.asHexString)(this.getCFListFreqChSeven()) + "\n";
msg += " FreqCh8 = " + (0, util_1.asHexString)(this.getCFListFreqChEight()) + "\n";
}
}
else if (this.isRejoinRequestMessage()) {
msg += " Message Type = ReJoin Request" + "\n";
msg += " PHYPayload = " + (0, util_1.asHexString)(this.PHYPayload).toUpperCase() + "\n";
msg += "\n";
msg += " ( PHYPayload = MHDR[1] | MACPayload[..] | MIC[4] )\n";
msg += " MHDR = " + (0, util_1.asHexString)(this.MHDR) + "\n";
msg += " MACPayload = " + (0, util_1.asHexString)(this.MACPayload) + "\n";
msg += " MIC = " + (0, util_1.asHexString)(this.MIC) + "\n";
msg += "\n";
if (this.RejoinType[0] === 0 || this.RejoinType[0] === 2) {
msg += " ( MACPayload = RejoinType[1] | NetID[3] | DevEUI[8] | RJCount0[2] )\n";
msg += " RejoinType = " + (0, util_1.asHexString)(this.RejoinType) + "\n";
msg += " NetID = " + (0, util_1.asHexString)(this.NetID) + "\n";
msg += " DevEUI = " + (0, util_1.asHexString)(this.DevEUI) + "\n";
msg += " RJCount0 = " + (0, util_1.asHexString)(this.RJCount0) + "\n";
}
else if (this.RejoinType[0] === 1) {
msg += " ( MACPayload = RejoinType[1] | JoinEUI[8] | DevEUI[8] | RJCount0[2] )\n";
msg += " RejoinType = " + (0, util_1.asHexString)(this.RejoinType) + "\n";
msg += " JoinEUI = " + (0, util_1.asHexString)(this.JoinEUI) + "\n";
msg += " DevEUI = " + (0, util_1.asHexString)(this.DevEUI) + "\n";
msg += " RJCount0 = " + (0, util_1.asHexString)(this.RJCount0) + "\n";
}
}
else if (this.isDataMessage()) {
msg += "Message Type = Data" + "\n";
msg += " PHYPayload = " + (0, util_1.asHexString)(this.PHYPayload).toUpperCase() + "\n";
msg += "\n";
msg += " ( PHYPayload = MHDR[1] | MACPayload[..] | MIC[4] )\n";
msg += " MHDR = " + (0, util_1.asHexString)(this.MHDR) + "\n";
msg += " MACPayload = " + (0, util_1.asHexString)(this.MACPayload) + "\n";
msg += " MIC = " + (0, util_1.asHexString)(this.MIC) + "\n";
msg += "\n";
msg += " ( MACPayload = FHDR | FPort | FRMPayload )\n";
msg += " FHDR = " + (0, util_1.asHexString)(this.FHDR) + "\n";
msg += " FPort = " + (0, util_1.asHexString)(this.FPort) + "\n";
msg += " FRMPayload = " + (0, util_1.asHexString)(this.FRMPayload) + "\n";
msg += "\n";
msg += " ( FHDR = DevAddr[4] | FCtrl[1] | FCnt[2] | FOpts[0..15] )\n";
msg += " DevAddr = " + (0, util_1.asHexString)(this.DevAddr) + " (Big Endian)\n";
msg += " FCtrl = " + (0, util_1.asHexString)(this.FCtrl) + "\n"; //TODO as binary?
msg += " FCnt = " + (0, util_1.asHexString)(this.FCnt) + " (Big Endian)\n";
msg += " FOpts = " + (0, util_1.asHexString)(this.FOpts) + "\n";
msg += "\n";
msg += " Message Type = " + this.getMType() + "\n";
msg += " Direction = " + this.getDir() + "\n";
msg += " FCnt = " + this.getFCnt() + "\n";
msg += " FCtrl.ACK = " + this.getFCtrlACK() + "\n";
msg += " FCtrl.ADR = " + this.getFCtrlADR() + "\n";
if (this._getMType() == MType.CONFIRMED_DATA_DOWN || this._getMType() == MType.UNCONFIRMED_DATA_DOWN) {
msg += " FCtrl.FPending = " + this.getFCtrlFPending() + "\n";
}
else {
msg += " FCtrl.ADRACKReq = " + this.getFCtrlADRACKReq() + "\n";
}
}
return msg;
}
get JoinEUI() {
return this.AppEUI;
}
set JoinEUI(v) {
this.AppEUI = v;
}
get JoinNonce() {
return this.AppNonce;
}
set JoinNonce(v) {
this.AppNonce = v;
}
}
exports.default = LoraPacket;
//# sourceMappingURL=LoraPacket.js.map