/// <summary> /// Initializes a new instance of the <see cref="Beacon"/> class. /// Copy constructor. /// </summary> /// <param name="otherBeacon"> /// Other beacon to be copied /// </param> /// <see cref="Beacon" /> protected Beacon(Beacon otherBeacon) { this.identifiers = new List <Identifier>(otherBeacon.identifiers); this.dataFields = new List <long>(otherBeacon.dataFields); this.distance = otherBeacon.distance; this.runningAverageRssi = otherBeacon.runningAverageRssi; this.Rssi = otherBeacon.Rssi; this.TxPower = otherBeacon.TxPower; this.BluetoothAddress = otherBeacon.BluetoothAddress; this.BeaconTypeCode = otherBeacon.BeaconTypeCode; this.ServiceUuid = otherBeacon.ServiceUuid; this.BluetoothName = otherBeacon.BluetoothName; }
public void TestReEncodesBeacon() { byte[] bytes = HexStringToByteArray("02011a1aff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c509"); BeaconParser parser = new BeaconParser(); parser.SetBeaconLayout("m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"); Beacon beacon = parser.FromScanData(bytes, -55, null); byte[] regeneratedBytes = parser.GetBeaconAdvertisementData(beacon); byte[] expectedMatch = new byte[bytes.Length - 7]; Array.Copy(bytes, 7, expectedMatch, 0, expectedMatch.Length); CollectionAssert.AreEqual(expectedMatch, regeneratedBytes, "beacon advertisement bytes should be the same after re-encoding"); }
public void TestReEncodesLittleEndianBeacon() { byte[] bytes = HexStringToByteArray("02011a1aff1801beac0102030405060708090a0b0c0d0e0f1011121314c509"); BeaconParser parser = new BeaconParser(); parser.SetBeaconLayout("m:2-3=beac,i:4-9,i:10-15l,i:16-23,p:24-24,d:25-25"); Beacon beacon = parser.FromScanData(bytes, -55, null); byte[] regeneratedBytes = parser.GetBeaconAdvertisementData(beacon); byte[] expectedMatch = new byte[bytes.Length - 7]; Array.Copy(bytes, 7, expectedMatch, 0, expectedMatch.Length); CollectionAssert.AreEqual(expectedMatch, regeneratedBytes, "beacon advertisement bytes should be the same after re-encoding"); }
public void TestRecognizeBeacon() { byte[] bytes = HexStringToByteArray("02011a1aff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c509"); BeaconParser parser = new BeaconParser(); parser.SetBeaconLayout("m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"); Beacon beacon = parser.FromScanData(bytes, -55, null); Assert.AreEqual(-55, beacon.Rssi, "mRssi should be as passed in"); Assert.AreEqual("2f234454-cf6d-4a0f-adf2-f4911ba9ffa6", beacon.Identifiers[0].ToString(), "uuid should be parsed"); Assert.AreEqual("1", beacon.Identifiers[1].ToString(), "id2 should be parsed"); Assert.AreEqual("2", beacon.Identifiers[2].ToString(), "id3 should be parsed"); Assert.AreEqual(-59, beacon.TxPower, "txPower should be parsed"); Assert.AreEqual(0x118, beacon.Manufacturer, "manufacturer should be parsed"); }
public void TestLittleEndianIdentifierParsing() { byte[] bytes = HexStringToByteArray("02011a1aff1801beac0102030405060708090a0b0c0d0e0f1011121314c509"); BeaconParser parser = new BeaconParser(); parser.SetBeaconLayout("m:2-3=beac,i:4-9,i:10-15l,i:16-23,p:24-24,d:25-25"); Beacon beacon = parser.FromScanData(bytes, -55, null); Assert.AreEqual(-55, beacon.Rssi, "mRssi should be as passed in"); Assert.AreEqual("0x010203040506", beacon.Identifiers[0].ToString(), "id1 should be big endian"); Assert.AreEqual("0x0c0b0a090807", beacon.Identifiers[1].ToString(), "id2 should be little endian"); Assert.AreEqual("0x0d0e0f1011121314", beacon.Identifiers[2].ToString(), "id3 should be big endian"); Assert.AreEqual(-59, beacon.TxPower, "txPower should be parsed"); Assert.AreEqual(0x118, beacon.Manufacturer, "manufacturer should be parsed"); }
/// <summary> /// Get BLE advertisement bytes for a Beacon /// </summary> /// <param name="beacon">the beacon containing the data to be transmitted</param> /// <returns>the byte array of the advertisement</returns> public byte[] GetBeaconAdvertisementData(Beacon beacon) { byte[] advertisingBytes; int lastIndex = -1; if (this.matchingBeaconTypeCodeEndOffset != null && this.matchingBeaconTypeCodeEndOffset.Value > lastIndex) { lastIndex = this.matchingBeaconTypeCodeEndOffset.Value; } if (this.powerEndOffset != null && this.powerEndOffset > lastIndex) { lastIndex = this.powerEndOffset.Value; } for (int identifierNum = 0; identifierNum < this.identifierStartOffsets.Count; identifierNum++) { if (this.identifierEndOffsets[identifierNum] != null && this.identifierEndOffsets[identifierNum] > lastIndex) { lastIndex = this.identifierEndOffsets[identifierNum].Value; } } for (int identifierNum = 0; identifierNum < this.dataEndOffsets.Count; identifierNum++) { if (this.dataEndOffsets[identifierNum] != null && this.dataEndOffsets[identifierNum] > lastIndex) { lastIndex = this.dataEndOffsets[identifierNum].Value; } } advertisingBytes = new byte[lastIndex + 1 - 2]; long beaconTypeCode = this.MatchingBeaconTypeCode; // set type code for (int index = this.matchingBeaconTypeCodeStartOffset.Value; index <= this.matchingBeaconTypeCodeEndOffset; index++) { byte value = (byte)(this.MatchingBeaconTypeCode >> (8 * (this.matchingBeaconTypeCodeEndOffset - index)) & 0xff); advertisingBytes[index - 2] = value; } // set identifiers for (int identifierNum = 0; identifierNum < this.identifierStartOffsets.Count; identifierNum++) { byte[] identifierBytes = beacon.Identifiers[identifierNum] .ToByteArrayOfSpecifiedEndianness(this.identifierLittleEndianFlags[identifierNum]); for (int index = this.identifierStartOffsets[identifierNum].Value; index <= this.identifierEndOffsets[identifierNum]; index++) { int identifierByteIndex = this.identifierEndOffsets[identifierNum].Value - index; if (identifierByteIndex < identifierBytes.Length) { advertisingBytes[index - 2] = identifierBytes[this.identifierEndOffsets[identifierNum].Value - index]; } else { advertisingBytes[index - 2] = 0; } } } // set power for (int index = this.powerStartOffset.Value; index <= this.powerEndOffset; index++) { advertisingBytes[index - 2] = (byte)(beacon.TxPower >> (8 * (index - this.powerStartOffset)) & 0xff); } // set data fields for (int dataFieldNum = 0; dataFieldNum < this.dataStartOffsets.Count; dataFieldNum++) { long dataField = beacon.DataFields[dataFieldNum]; for (int index = this.dataStartOffsets[dataFieldNum].Value; index <= this.dataEndOffsets[dataFieldNum]; index++) { int endianCorrectedIndex = index; if (this.dataLittleEndianFlags[dataFieldNum]) { endianCorrectedIndex = this.dataEndOffsets[dataFieldNum].Value - index; } advertisingBytes[endianCorrectedIndex - 2] = (byte)(dataField >> (8 * (index - this.dataStartOffsets[dataFieldNum])) & 0xff); } } return(advertisingBytes); }
/// <summary> /// Construct a Beacon from a Bluetooth LE packet collected by Android's Bluetooth APIs, /// including the raw bluetooth device info /// </summary> /// <param name="scanData"> /// The actual packet bytes /// </param> /// <param name="rssi"> /// The measured signal strength of the packet /// </param> /// <param name="device"> /// The bluetooth device that was detected /// </param> /// <param name="beaconBuilder"> /// Beacon Builder /// </param> /// <returns> /// An instance of a <code>Beacon</code> /// </returns> protected Beacon FromScanData(byte[] scanData, int rssi, BluetoothDevice device, Beacon.Builder beaconBuilder) { int startByte = 2; bool patternFound = false; int matchingBeaconSize = this.matchingBeaconTypeCodeEndOffset.Value - this.matchingBeaconTypeCodeStartOffset.Value + 1; byte[] typeCodeBytes = LongToByteArray(this.MatchingBeaconTypeCode, matchingBeaconSize); while (startByte <= 5) { if (AreByteArraysMatch(scanData, startByte + this.matchingBeaconTypeCodeStartOffset.Value, typeCodeBytes, 0)) { patternFound = true; break; } startByte++; } if (patternFound == false) { // This is not a beacon // TODO LogManager /*if (this.ServiceUuid == null) { TODO LogManager if (LogManager.isVerboseLoggingEnabled()) { LogManager.d(TAG, "This is not a matching Beacon advertisement. " + "(Was expecting %s. The bytes I see are: %s", byteArrayToString(typeCodeBytes), bytesToHex(scanData)); } } else { if (LogManager.isVerboseLoggingEnabled()) { LogManager.d(TAG, "This is not a matching Beacon advertisement. " + "(Was expecting %s and %s. The bytes I see are: %s", byteArrayToString(serviceUuidBytes), byteArrayToString(typeCodeBytes), bytesToHex(scanData)); } }*/ return null; } else { //// TODO LogManager //// if (LogManager.isVerboseLoggingEnabled()) //// { //// LogManager.d(TAG, "This is a recognized beacon advertisement -- %s seen", //// byteArrayToString(typeCodeBytes)); //// } //// //// TODO LogManager //// LogManager.d(Tag, "This is a recognized beacon advertisement -- " + //// getMatchingBeaconTypeCode().ToString("x4") + " seen"); } List<Identifier> identifiers = new List<Identifier>(); for (int i = 0; i < this.identifierEndOffsets.Count; i++) { Identifier identifier = Identifier.FromBytes( scanData, this.identifierStartOffsets[i].Value + startByte, this.identifierEndOffsets[i].Value + startByte + 1, this.identifierLittleEndianFlags[i]); identifiers.Add(identifier); } List<long> dataFields = new List<long>(); for (int i = 0; i < this.dataEndOffsets.Count; i++) { string dataString = ByteArrayToFormattedString( scanData, this.dataStartOffsets[i].Value + startByte, this.dataEndOffsets[i].Value + startByte, this.dataLittleEndianFlags[i]); dataFields.Add(long.Parse(dataString)); //// TODO LogManager //// LogManager.d(Tag, "parsing found data field " + i); //// TODO: error handling needed here on the parse } int txPower = 0; string powerString = ByteArrayToFormattedString( scanData, this.powerStartOffset.Value + startByte, this.powerEndOffset.Value + startByte, false); txPower = int.Parse(powerString); // make sure it is a signed integer if (txPower > 127) { txPower -= 256; } // TODO: error handling needed on the parse int beaconTypeCode = 0; string beaconTypeString = ByteArrayToFormattedString( scanData, this.matchingBeaconTypeCodeStartOffset.Value + startByte, this.matchingBeaconTypeCodeEndOffset.Value + startByte, false); beaconTypeCode = int.Parse(beaconTypeString); // TODO: error handling needed on the parse int manufacturer = 0; string manufacturerString = ByteArrayToFormattedString(scanData, startByte, startByte + 1, true); manufacturer = int.Parse(manufacturerString); string macAddress = null; string name = null; if (device != null) { macAddress = device.Address; name = device.Name; } return beaconBuilder .SetIdentifiers(identifiers) .SetDataFields(dataFields) .SetTxPower(txPower) .SetRssi(rssi) .SetBeaconTypeCode(beaconTypeCode) .SetBluetoothAddress(macAddress) .SetBluetoothAddress(macAddress) .SetManufacturer(manufacturer) .Build(); }
/// <summary> /// Get BLE advertisement bytes for a Beacon /// </summary> /// <param name="beacon">the beacon containing the data to be transmitted</param> /// <returns>the byte array of the advertisement</returns> public byte[] GetBeaconAdvertisementData(Beacon beacon) { byte[] advertisingBytes; int lastIndex = -1; if (this.matchingBeaconTypeCodeEndOffset != null && this.matchingBeaconTypeCodeEndOffset.Value > lastIndex) { lastIndex = this.matchingBeaconTypeCodeEndOffset.Value; } if (this.powerEndOffset != null && this.powerEndOffset > lastIndex) { lastIndex = this.powerEndOffset.Value; } for (int identifierNum = 0; identifierNum < this.identifierStartOffsets.Count; identifierNum++) { if (this.identifierEndOffsets[identifierNum] != null && this.identifierEndOffsets[identifierNum] > lastIndex) { lastIndex = this.identifierEndOffsets[identifierNum].Value; } } for (int identifierNum = 0; identifierNum < this.dataEndOffsets.Count; identifierNum++) { if (this.dataEndOffsets[identifierNum] != null && this.dataEndOffsets[identifierNum] > lastIndex) { lastIndex = this.dataEndOffsets[identifierNum].Value; } } advertisingBytes = new byte[lastIndex + 1 - 2]; long beaconTypeCode = this.MatchingBeaconTypeCode; // set type code for (int index = this.matchingBeaconTypeCodeStartOffset.Value; index <= this.matchingBeaconTypeCodeEndOffset; index++) { byte value = (byte)(this.MatchingBeaconTypeCode >> (8 * (this.matchingBeaconTypeCodeEndOffset - index)) & 0xff); advertisingBytes[index - 2] = value; } // set identifiers for (int identifierNum = 0; identifierNum < this.identifierStartOffsets.Count; identifierNum++) { byte[] identifierBytes = beacon.Identifiers[identifierNum] .ToByteArrayOfSpecifiedEndianness(this.identifierLittleEndianFlags[identifierNum]); for (int index = this.identifierStartOffsets[identifierNum].Value; index <= this.identifierEndOffsets[identifierNum]; index++) { int identifierByteIndex = this.identifierEndOffsets[identifierNum].Value - index; if (identifierByteIndex < identifierBytes.Length) { advertisingBytes[index - 2] = identifierBytes[this.identifierEndOffsets[identifierNum].Value - index]; } else { advertisingBytes[index - 2] = 0; } } } // set power for (int index = this.powerStartOffset.Value; index <= this.powerEndOffset; index++) { advertisingBytes[index - 2] = (byte)(beacon.TxPower >> (8 * (index - this.powerStartOffset)) & 0xff); } // set data fields for (int dataFieldNum = 0; dataFieldNum < this.dataStartOffsets.Count; dataFieldNum++) { long dataField = beacon.DataFields[dataFieldNum]; for (int index = this.dataStartOffsets[dataFieldNum].Value; index <= this.dataEndOffsets[dataFieldNum]; index++) { int endianCorrectedIndex = index; if (this.dataLittleEndianFlags[dataFieldNum]) { endianCorrectedIndex = this.dataEndOffsets[dataFieldNum].Value - index; } advertisingBytes[endianCorrectedIndex - 2] = (byte)(dataField >> (8 * (index - this.dataStartOffsets[dataFieldNum])) & 0xff); } } return advertisingBytes; }
/// <summary> /// Initializes a new instance of the <see cref="Beacon.Builder"/> class. /// Creates a builder instance. /// </summary> public Builder() { this.beacon = new Beacon(); }
/// <summary> /// Initializes a new instance of the <see cref="Beacon.Builder"/> class. /// Creates a builder instance. /// </summary> public Builder() { this.beacon = new Beacon(); }
/// <summary> /// Initializes a new instance of the <see cref="Beacon"/> class. /// Copy constructor. /// </summary> /// <param name="otherBeacon"> /// Other beacon to be copied /// </param> /// <see cref="Beacon" /> protected Beacon(Beacon otherBeacon) { this.identifiers = new List<Identifier>(otherBeacon.identifiers); this.dataFields = new List<long>(otherBeacon.dataFields); this.distance = otherBeacon.distance; this.runningAverageRssi = otherBeacon.runningAverageRssi; this.Rssi = otherBeacon.Rssi; this.TxPower = otherBeacon.TxPower; this.BluetoothAddress = otherBeacon.BluetoothAddress; this.BeaconTypeCode = otherBeacon.BeaconTypeCode; this.ServiceUuid = otherBeacon.ServiceUuid; this.BluetoothName = otherBeacon.BluetoothName; }