public SpineObject decode(Node node, byte[] payload) throws PacketDecodingException {

    BufferedRawData data = new BufferedRawData();

    data.baseInit(node, payload);
    // data.setFunctionCode(SPINEFunctionConstants.BUFFERED_RAW_DATA);

    short pldIndex = 2;

    byte sensorCode = (byte) (payload[pldIndex] >> 4);
    data.setSensorCode(sensorCode);

    byte channelBitmask = (byte) (payload[pldIndex++] & 0x0F);
    data.setChannelBitmask(channelBitmask);

    byte dataWordLength = payload[pldIndex++];
    data.setDataWordLength(dataWordLength);

    pldIndex++; // skip MSB of the bufferSize because it will be always 0
    int bufferSize = payload[pldIndex++];

    int[][] values = new int[SPINESensorConstants.MAX_VALUE_TYPES][];

    // if the actual sensor readings data ((payload.length - pldIndex) bytes)
    // in the payload is less than what is declared (by channelBitmask, bufferSize, and
    // dataWordLength)
    // then this message is somehow malformed or corrupted.
    if (((payload.length - pldIndex)
        < (SPINESensorConstants.countChannelsInBitmask(channelBitmask)
            * bufferSize
            * dataWordLength)))
      throw new PacketDecodingException(
          "Malformed or corrupted BufferedRawData message received "
              + "[from node: "
              + node.getPhysicalID()
              + "]");

    byte[] dataTmp = new byte[4];
    for (int i = 0; i < SPINESensorConstants.MAX_VALUE_TYPES; i++) {
      if (SPINESensorConstants.chPresent(i, channelBitmask)) {
        values[i] = new int[bufferSize];
        for (int j = bufferSize - 1; j >= 0; j--) {
          if (dataWordLength == 1) {
            dataTmp[3] = payload[pldIndex++];
            dataTmp[2] = 0;
            dataTmp[1] = 0;
            dataTmp[0] = 0;

          } else if (dataWordLength == 2) {
            dataTmp[3] = payload[pldIndex++];
            dataTmp[2] = payload[pldIndex++];
            dataTmp[1] = 0;
            dataTmp[0] = 0;
          } else if (dataWordLength == 3) {
            dataTmp[3] = payload[pldIndex++];
            dataTmp[2] = payload[pldIndex++];
            dataTmp[1] = payload[pldIndex++];
            dataTmp[0] = 0;
          } else if (dataWordLength == 4) {
            dataTmp[3] = payload[pldIndex++];
            dataTmp[2] = payload[pldIndex++];
            dataTmp[1] = payload[pldIndex++];
            dataTmp[0] = payload[pldIndex++];
          }

          values[i][j] = Data.convertFourBytesToInt(dataTmp, 0);
        }
      }
    }

    data.setValues(values);

    return data;
  }
  public void discoveryCompleted(Vector activeNodes) {

    // we loop over the discovered nodes (hopefully, at least a node's showed up!)
    Node curr = null;
    for (int j = 0; j < activeNodes.size(); j++) {

      curr = (Node) activeNodes.elementAt(j);

      // we print for each node its details (nodeID, sensors and functions provided)
      System.out.println(curr);

      // for each node, we look for specific services
      for (int i = 0; i < curr.getSensorsList().size(); i++) {

        byte sensor = ((Sensor) curr.getSensorsList().elementAt(i)).getCode();

        // if the current node has an accelerometer, then...
        if (sensor == SPINESensorConstants.ACC_SENSOR) {

          // ... we first setup that sensor, specifying its sampling time and time scale; then ...
          SpineSetupSensor sss = new SpineSetupSensor();
          sss.setSensor(sensor);
          sss.setTimeScale(SPINESensorConstants.MILLISEC);
          sss.setSamplingTime(SAMPLING_TIME);
          manager.setup(curr, sss);

          // ... we can setup a specific function (in this case a Feature) on that sensor; then ...
          FeatureSpineSetupFunction ssf = new FeatureSpineSetupFunction();
          ssf.setSensor(sensor);
          ssf.setWindowSize(WINDOW_SIZE);
          ssf.setShiftSize(SHIFT_SIZE);
          manager.setup(curr, ssf);

          // ... we can activate that function with function specific parameters
          // (for Feature they are the desired feature extractors); we can also ...
          FeatureSpineFunctionReq sfr = new FeatureSpineFunctionReq();
          sfr.setSensor(sensor);
          sfr.add(
              new Feature(
                  SPINEFunctionConstants.MODE,
                  ((Sensor) curr.getSensorsList().elementAt(i)).getChannelBitmask()));
          sfr.add(
              new Feature(
                  SPINEFunctionConstants.MEDIAN,
                  ((Sensor) curr.getSensorsList().elementAt(i)).getChannelBitmask()));
          sfr.add(
              new Feature(
                  SPINEFunctionConstants.MAX,
                  ((Sensor) curr.getSensorsList().elementAt(i)).getChannelBitmask()));
          sfr.add(
              new Feature(
                  SPINEFunctionConstants.MIN,
                  ((Sensor) curr.getSensorsList().elementAt(i)).getChannelBitmask()));
          manager.activate(curr, sfr);

          // ... split a more complex activation in multiple activations
          // (if the specific function implementation in the node side allows that); of course we
          // always can ...
          sfr = new FeatureSpineFunctionReq();
          sfr.setSensor(sensor);
          sfr.add(
              new Feature(
                  SPINEFunctionConstants.MEAN,
                  ((Sensor) curr.getSensorsList().elementAt(i)).getChannelBitmask()));
          sfr.add(
              new Feature(
                  SPINEFunctionConstants.AMPLITUDE,
                  ((Sensor) curr.getSensorsList().elementAt(i)).getChannelBitmask()));
          manager.activate(curr, sfr);

          // SetUp Alarm Engine
          // Window and Shift may be set to value different from the feature engine ones.
          // here we use the same values for debigging proposes
          AlarmSpineSetupFunction ssf2 = new AlarmSpineSetupFunction();
          ssf2.setSensor(sensor);
          ssf2.setWindowSize(WINDOW_SIZE);
          ssf2.setShiftSize(SHIFT_SIZE);
          manager.setup(curr, ssf2);

          // Activate alarm on MAX value (one of the features computed above) on CH1
          // alarm sent when MAX > upperThresold
          AlarmSpineFunctionReq sfr2 = new AlarmSpineFunctionReq();

          int lowerThreshold = 20;
          int upperThreshold = 40;

          sfr2.setDataType(SPINEFunctionConstants.MAX);
          sfr2.setSensor(SPINESensorConstants.ACC_SENSOR);
          sfr2.setValueType((SPINESensorConstants.CH1_ONLY));
          sfr2.setLowerThreshold(lowerThreshold);
          sfr2.setUpperThreshold(upperThreshold);
          sfr2.setAlarmType(SPINEFunctionConstants.ABOVE_THRESHOLD);

          manager.activate(curr, sfr2);

          // Activate alarm on AMPLITUDE value (one of the features computed above) on CH2
          // alarm sent when AMPLITUDE < lowerThreshold

          lowerThreshold = 2000;
          upperThreshold = 1000;

          sfr2.setDataType(SPINEFunctionConstants.AMPLITUDE);
          sfr2.setSensor(SPINESensorConstants.ACC_SENSOR);
          sfr2.setValueType((SPINESensorConstants.CH2_ONLY));
          sfr2.setLowerThreshold(lowerThreshold);
          sfr2.setUpperThreshold(upperThreshold);
          sfr2.setAlarmType(SPINEFunctionConstants.BELOW_THRESHOLD);

          manager.activate(curr, sfr2);

        }
        // repeat this process for other desired sensors; after that we can finally ...
        else if (sensor == SPINESensorConstants.INTERNAL_TEMPERATURE_SENSOR) {
          SpineSetupSensor sss = new SpineSetupSensor();
          sss.setSensor(sensor);
          sss.setTimeScale(SPINESensorConstants.MILLISEC);
          sss.setSamplingTime(OTHER_SAMPLING_TIME);
          manager.setup(curr, sss);

          FeatureSpineSetupFunction ssf = new FeatureSpineSetupFunction();
          ssf.setSensor(sensor);
          ssf.setWindowSize(OTHER_WINDOW_SIZE);
          ssf.setShiftSize(OTHER_SHIFT_SIZE);
          manager.setup(curr, ssf);

          FeatureSpineFunctionReq sfr = new FeatureSpineFunctionReq();
          sfr.setSensor(sensor);
          sfr.add(
              new Feature(
                  SPINEFunctionConstants.MODE,
                  ((Sensor) curr.getSensorsList().elementAt(i)).getChannelBitmask()));
          sfr.add(
              new Feature(
                  SPINEFunctionConstants.MEDIAN,
                  ((Sensor) curr.getSensorsList().elementAt(i)).getChannelBitmask()));
          sfr.add(
              new Feature(
                  SPINEFunctionConstants.MAX,
                  ((Sensor) curr.getSensorsList().elementAt(i)).getChannelBitmask()));
          sfr.add(
              new Feature(
                  SPINEFunctionConstants.MIN,
                  ((Sensor) curr.getSensorsList().elementAt(i)).getChannelBitmask()));
          manager.activate(curr, sfr);

          // SetUp Alarm Engine
          // Same Window and Shift as before
          AlarmSpineSetupFunction ssf3 = new AlarmSpineSetupFunction();
          ssf3.setSensor(sensor);
          ssf3.setWindowSize(WINDOW_SIZE);
          ssf3.setShiftSize(SHIFT_SIZE);
          manager.setup(curr, ssf3);

          // Activate alarm on MIN value (one of the features computed above)on CH1
          // alarm sent when lowerThreshold < MIN < upperThreshold
          AlarmSpineFunctionReq sfr3 = new AlarmSpineFunctionReq();

          int lowerThreshold = 1000;
          int upperThreshold = 3000;

          sfr3.setDataType(SPINEFunctionConstants.MIN);
          sfr3.setSensor(sensor);
          sfr3.setValueType((SPINESensorConstants.CH1_ONLY));
          sfr3.setLowerThreshold(lowerThreshold);
          sfr3.setUpperThreshold(upperThreshold);
          sfr3.setAlarmType(SPINEFunctionConstants.IN_BETWEEN_THRESHOLDS);

          manager.activate(curr, sfr3);
        }
      }
    }

    // ... start the sensor network sensing and computing our aforeactivated services.
    if (activeNodes.size() > 0)
      manager.startWsn(
          true,
          true); // we can tune a few node parameters at run-time for reducing the power consumption
                 // and the packets drop.
  }