diff --git a/ShimmerDriver/src/main/java/com/shimmerresearch/bluetooth/ShimmerBluetooth.java b/ShimmerDriver/src/main/java/com/shimmerresearch/bluetooth/ShimmerBluetooth.java index db2beb4d..18bfc999 100644 --- a/ShimmerDriver/src/main/java/com/shimmerresearch/bluetooth/ShimmerBluetooth.java +++ b/ShimmerDriver/src/main/java/com/shimmerresearch/bluetooth/ShimmerBluetooth.java @@ -3341,7 +3341,11 @@ public void readPressureCalibrationCoefficients() { } } } else if (getHardwareVersion() == HW_ID.SHIMMER_3R) { - writeInstruction(GET_PRESSURE_CALIBRATION_COEFFICIENTS_COMMAND); + // BMP581 self-compensates and has no coefficients; its firmware NACKs + // GET_PRESSURE_CALIBRATION_COEFFICIENTS_COMMAND, so don't send it. + if (!isSupportedBmp581()) { + writeInstruction(GET_PRESSURE_CALIBRATION_COEFFICIENTS_COMMAND); + } } } diff --git a/ShimmerDriver/src/main/java/com/shimmerresearch/driver/Configuration.java b/ShimmerDriver/src/main/java/com/shimmerresearch/driver/Configuration.java index f8dc8c26..bf813e20 100644 --- a/ShimmerDriver/src/main/java/com/shimmerresearch/driver/Configuration.java +++ b/ShimmerDriver/src/main/java/com/shimmerresearch/driver/Configuration.java @@ -82,6 +82,7 @@ import com.shimmerresearch.sensors.bmpX80.SensorBMP180; import com.shimmerresearch.sensors.bmpX80.SensorBMP280; import com.shimmerresearch.sensors.bmpX80.SensorBMP390; +import com.shimmerresearch.sensors.bmpX80.SensorBMP581; import com.shimmerresearch.sensors.kionix.SensorKionixAccel; import com.shimmerresearch.sensors.kionix.SensorKionixKXRB52042; import com.shimmerresearch.sensors.lisxmdl.SensorLIS3MDL; @@ -733,6 +734,7 @@ public static class SENSOR_ID{ public static final int SHIMMER_LIS2MDL_MAG = 42; public static final int SHIMMER_LIS3MDL_MAG_ALT = 41; public static final int SHIMMER_BMP390_PRESSURE = 43; + public static final int SHIMMER_BMP581_PRESSURE = 44; // public static final int SHIMMER_EXG1_24BIT = 3; // public static final int SHIMMER_EXG2_24BIT = 4; @@ -924,6 +926,7 @@ public enum LABEL_SENSOR_TILE{ PRESSURE_TEMPERATURE_BMP180(SensorBMP180.LABEL_SENSOR_TILE.PRESSURE_TEMPERATURE), PRESSURE_TEMPERATURE_BMP280(SensorBMP280.LABEL_SENSOR_TILE.PRESSURE_TEMPERATURE), PRESSURE_TEMPERATURE_BMP390(SensorBMP390.LABEL_SENSOR_TILE.PRESSURE_TEMPERATURE), + PRESSURE_TEMPERATURE_BMP581(SensorBMP581.LABEL_SENSOR_TILE.PRESSURE_TEMPERATURE), BATTERY_MONITORING(SensorBattVoltage.LABEL_SENSOR_TILE.BATTERY_MONITORING), EXTERNAL_EXPANSION_ADC(SensorADC.LABEL_SENSOR_TILE.EXTERNAL_EXPANSION_ADC), INTERNAL_EXPANSION_ADC(SensorADC.LABEL_SENSOR_TILE.INTERNAL_EXPANSION_ADC), @@ -1478,7 +1481,15 @@ public static class CompatibilityInfoForMaps{ svoShimmer3RExgUnifiedLogAndStream, svoShimmer3RBrAmpUnifiedLogAndStream, svoShimmer3RProto3DeluxeLogAndStream, - svoShimmer4Stock); + svoShimmer4Stock); + + public static final List listOfCompatibleVersionInfoBMP581 = Arrays.asList( + svoShimmer3RImuLogAndStream, svoShimmer3RLogAndStream, + svoShimmer3RImuAnyExpBrdLogAndStream, + svoShimmer3RGsrUnifiedLogAndStream, + svoShimmer3RExgUnifiedLogAndStream, + svoShimmer3RBrAmpUnifiedLogAndStream, + svoShimmer3RProto3DeluxeLogAndStream); public static final List listOfCompatibleVersionInfoMPU9250 = Arrays.asList( svoNewImuSdLog, svoNewImuLogAndStream, svoShimmer3RLogAndStream, diff --git a/ShimmerDriver/src/main/java/com/shimmerresearch/driver/ShimmerObject.java b/ShimmerDriver/src/main/java/com/shimmerresearch/driver/ShimmerObject.java index db0905e2..4a6808c6 100644 --- a/ShimmerDriver/src/main/java/com/shimmerresearch/driver/ShimmerObject.java +++ b/ShimmerDriver/src/main/java/com/shimmerresearch/driver/ShimmerObject.java @@ -74,6 +74,7 @@ import com.shimmerresearch.sensors.bmpX80.SensorBMP180; import com.shimmerresearch.sensors.bmpX80.SensorBMP280; import com.shimmerresearch.sensors.bmpX80.SensorBMP390; +import com.shimmerresearch.sensors.bmpX80.SensorBMP581; import com.shimmerresearch.sensors.bmpX80.SensorBMPX80; import com.shimmerresearch.sensors.kionix.SensorKionixAccel; import com.shimmerresearch.sensors.kionix.SensorKionixKXRB52042; @@ -1995,8 +1996,13 @@ else if (isEXGUsingDefaultThreeUnipolarConfiguration()){ String signalNameBmpX80Temperature = SensorBMP180.ObjectClusterSensorName.TEMPERATURE_BMP180; String signalNameBmpX80Pressure = SensorBMP180.ObjectClusterSensorName.PRESSURE_BMP180; if (getHardwareVersion()==HW_ID.SHIMMER_3R) { - signalNameBmpX80Temperature = SensorBMP390.ObjectClusterSensorName.TEMPERATURE_BMP390; - signalNameBmpX80Pressure = SensorBMP390.ObjectClusterSensorName.PRESSURE_BMP390; + boolean isBmp581 = isSupportedBmp581(); + signalNameBmpX80Temperature = isBmp581 + ? SensorBMP581.ObjectClusterSensorName.TEMPERATURE_BMP581 + : SensorBMP390.ObjectClusterSensorName.TEMPERATURE_BMP390; + signalNameBmpX80Pressure = isBmp581 + ? SensorBMP581.ObjectClusterSensorName.PRESSURE_BMP581 + : SensorBMP390.ObjectClusterSensorName.PRESSURE_BMP390; } else if(isSupportedBmp280()){ signalNameBmpX80Temperature = SensorBMP280.ObjectClusterSensorName.TEMPERATURE_BMP280; signalNameBmpX80Pressure = SensorBMP280.ObjectClusterSensorName.PRESSURE_BMP280; @@ -3449,7 +3455,9 @@ else if ((byte)signalId[i-iTS]==(byte)0x1A){ packetSize=packetSize+2; enabledSensors |= SENSOR_BMPX80; }else if (getHardwareVersion()==HW_ID.SHIMMER_3R) { - String signalName = SensorBMP390.ObjectClusterSensorName.TEMPERATURE_BMP390; + String signalName = isSupportedBmp581() + ? SensorBMP581.ObjectClusterSensorName.TEMPERATURE_BMP581 + : SensorBMP390.ObjectClusterSensorName.TEMPERATURE_BMP390; signalNameArray[i+1]=signalName; signalDataTypeArray[i+1] = "u24"; packetSize=packetSize+3; @@ -3467,7 +3475,9 @@ else if ((byte)signalId[i-iTS]==(byte)0x1B){ packetSize=packetSize+3; enabledSensors |= SENSOR_BMPX80; }else if (getHardwareVersion()==HW_ID.SHIMMER_3R) { - String signalName=SensorBMP390.ObjectClusterSensorName.PRESSURE_BMP390; + String signalName = isSupportedBmp581() + ? SensorBMP581.ObjectClusterSensorName.PRESSURE_BMP581 + : SensorBMP390.ObjectClusterSensorName.PRESSURE_BMP390; signalNameArray[i+1]=signalName; signalDataTypeArray[i+1] = "u24"; packetSize=packetSize+3; @@ -5476,7 +5486,11 @@ else if(isSupportedNewImuSensors()){ } } else if(isShimmerGen3R()){ - mSensorBMPX80 = new SensorBMP390(this); + if(isSupportedBmp581()){ + mSensorBMPX80 = new SensorBMP581(this); + } else { + mSensorBMPX80 = new SensorBMP390(this); + } addSensorClass(mSensorBMPX80); mSensorLSM6DSV = new SensorLSM6DSV(this); @@ -10741,6 +10755,46 @@ public static boolean isSupportedBmp280(ShimmerVerObject svo, ExpansionBoardDeta return isSupportedNewImuSensors(svo, ebd); } + /** Returns true if this Shimmer3R uses the BMP581 pressure sensor (instead of + * the BMP390). The BMP581 is fitted to up-rev'd Shimmer3R boards; unlike the + * BMP390 it streams pre-compensated pressure/temperature, so no calibration + * coefficients are fetched or applied. Selection is gated on both the board + * SR number/revision and the firmware version that produces the new output + * format. + * + * @return true if the connected Shimmer3R board revision and firmware version indicate BMP581 output support + */ + public boolean isSupportedBmp581() { + return isSupportedBmp581(getShimmerVerObject(), getExpansionBoardDetails()); + } + + public static boolean isSupportedBmp581(ShimmerVerObject svo, ExpansionBoardDetails ebd) { + if(svo==null || ebd==null || svo.getHardwareVersion()!=HW_ID.SHIMMER_3R){ + return false; + } + + // SR48 (GSR+): BMP581 spans two bands - 7.2..7.x AND 8.2 and above. + // 7.0/7.1 and 8.0/8.1 are BMP390, so a single lexicographic ">=" can't + // express it (8.0/8.1 fall between the two BMP581 bands). + boolean sr48Bmp581 = + (ebd.getExpansionBoardId()==HW_ID_SR_CODES.EXP_BRD_GSR_UNIFIED + && ebd.getExpansionBoardRev()==7 + && ebd.getExpansionBoardRevSpecial()>=2) // SR48 7.2..7.x + || ebd.isSrNumberGte(HW_ID_SR_CODES.EXP_BRD_GSR_UNIFIED, 8, 2); // SR48 8.2 and above + + boolean boardEligible = + ebd.isSrNumberGte(HW_ID_SR_CODES.SHIMMER3, 11, 2) // SR31 >= 11.2 + || ebd.isSrNumberGte(HW_ID_SR_CODES.EXP_BRD_EXG_UNIFIED, 8, 2) // SR47 >= 8.2 + || sr48Bmp581 // SR48 7.2..7.x and 8.2+ + || ebd.isSrNumberGte(HW_ID_SR_CODES.EXP_BRD_BR_AMP_UNIFIED, 4, 2); // SR49 >= 4.2 + + // Format guard: BMP581 (pre-compensated) output only exists from + // LogAndStream_Shimmer3R v1.01.006 onwards. + boolean fwEligible = svo.compareVersions(FW_ID.LOGANDSTREAM, 1, 1, 6); + + return boardEligible && fwEligible; + } + /** Returns true if the Shimmer is using new sensors. These sensors are: *
  • Use BMP280 instead of BMP180 as barometer. *
  • Use MPU9250 instead of MPU9150 as IMU. diff --git a/ShimmerDriver/src/main/java/com/shimmerresearch/driverUtilities/ExpansionBoardDetails.java b/ShimmerDriver/src/main/java/com/shimmerresearch/driverUtilities/ExpansionBoardDetails.java index 33fe7d66..506bad6d 100644 --- a/ShimmerDriver/src/main/java/com/shimmerresearch/driverUtilities/ExpansionBoardDetails.java +++ b/ShimmerDriver/src/main/java/com/shimmerresearch/driverUtilities/ExpansionBoardDetails.java @@ -164,4 +164,29 @@ public int getExpansionBoardRevSpecial() { return mExpansionBoardRevSpecial; } + /** + * Lexicographic ">=" comparison over (revision, special revision) for a + * given SR number. The revision is the primary key: e.g. board SR48.8.0 is + * considered >= threshold SR48.7.2 because the revision (8) already exceeds + * the threshold revision (7), regardless of the special-rev values. + *

    + * NB: a naive {@code rev>=minRev && special>=minSpecial} check is WRONG + * (it would reject SR48.8.0 against threshold SR48.7.2) - keep this + * lexicographic. + * + * @param srNumber the SR number (expansion-board id) to match + * @param minRev minimum board revision + * @param minSpecial minimum special revision (only compared when rev is equal) + * @return true if this board's SR number matches and (rev, special) >= (minRev, minSpecial) + */ + public boolean isSrNumberGte(int srNumber, int minRev, int minSpecial) { + if (mExpansionBoardId != srNumber) { + return false; + } + if (mExpansionBoardRev != minRev) { + return mExpansionBoardRev > minRev; + } + return mExpansionBoardRevSpecial >= minSpecial; + } + } diff --git a/ShimmerDriver/src/main/java/com/shimmerresearch/sensors/AbstractSensor.java b/ShimmerDriver/src/main/java/com/shimmerresearch/sensors/AbstractSensor.java index 51df41f8..c1049b22 100644 --- a/ShimmerDriver/src/main/java/com/shimmerresearch/sensors/AbstractSensor.java +++ b/ShimmerDriver/src/main/java/com/shimmerresearch/sensors/AbstractSensor.java @@ -70,7 +70,8 @@ public enum SENSORS{ LSM6DSV("LSM6DSV"), VD6283("VD6283"), MLX90632("MLX90632"), - BMP390("BMP390"); + BMP390("BMP390"), + BMP581("BMP581"); private final String text; diff --git a/ShimmerDriver/src/main/java/com/shimmerresearch/sensors/bmpX80/CalibDetailsBmp581.java b/ShimmerDriver/src/main/java/com/shimmerresearch/sensors/bmpX80/CalibDetailsBmp581.java new file mode 100644 index 00000000..02245df2 --- /dev/null +++ b/ShimmerDriver/src/main/java/com/shimmerresearch/sensors/bmpX80/CalibDetailsBmp581.java @@ -0,0 +1,49 @@ +package com.shimmerresearch.sensors.bmpX80; + +import com.shimmerresearch.driver.calibration.CalibDetails.CALIB_READ_SOURCE; + +/** + * BMP581 streams already-compensated values, so there are no per-device trim + * coefficients to parse or store (unlike BMP180/BMP280/BMP390). This CalibDetails + * is therefore a pass-through scaler: + *

  • pressure = raw / 64 -> Pa + *
  • temperature = raw / 65536 -> deg C + * (see BST-BMP581-DS004). + * + * @author Shimmer + */ +public class CalibDetailsBmp581 extends CalibDetailsBmpX80 { + + private static final long serialVersionUID = 8046182982777461001L; + + public String mSensorMacID; + + public CalibDetailsBmp581(String mMacIdFromUart) { + mSensorMacID = mMacIdFromUart; + } + + @Override + public double[] calibratePressureSensorData(double UP, double UT) { + double[] caldata = new double[2]; + caldata[0] = UP / 64.0; // Pa (downstream /1000 -> kPa, same as BMP390 legacy path) + caldata[1] = UT / 65536.0; // deg C + return caldata; + } + + @Override + public byte[] generateCalParamByteArray() { + // No coefficients on BMP581. + return null; + } + + @Override + public void parseCalParamByteArray(byte[] bufferCalibrationParameters, CALIB_READ_SOURCE calibReadSource) { + // No-op: BMP581 self-compensates; no coefficient block exists. + } + + @Override + public void resetToDefaultParameters() { + // No calibration state to reset. + } + +} diff --git a/ShimmerDriver/src/main/java/com/shimmerresearch/sensors/bmpX80/SensorBMP581.java b/ShimmerDriver/src/main/java/com/shimmerresearch/sensors/bmpX80/SensorBMP581.java new file mode 100644 index 00000000..fb5abfcf --- /dev/null +++ b/ShimmerDriver/src/main/java/com/shimmerresearch/sensors/bmpX80/SensorBMP581.java @@ -0,0 +1,385 @@ +package com.shimmerresearch.sensors.bmpX80; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.ArrayUtils; + +import com.shimmerresearch.driver.Configuration.CHANNEL_UNITS; +import com.shimmerresearch.driver.Configuration.COMMUNICATION_TYPE; +import com.shimmerresearch.driver.Configuration.Shimmer3.CompatibilityInfoForMaps; +import com.shimmerresearch.driver.shimmer2r3.ConfigByteLayoutShimmer3; +import com.shimmerresearch.bluetooth.BtCommandDetails; +import com.shimmerresearch.driver.ConfigByteLayout; +import com.shimmerresearch.driver.Configuration; +import com.shimmerresearch.driver.FormatCluster; +import com.shimmerresearch.driver.ObjectCluster; +import com.shimmerresearch.driver.ShimmerDevice; +import com.shimmerresearch.driverUtilities.ChannelDetails; +import com.shimmerresearch.driverUtilities.ConfigOptionDetailsSensor; +import com.shimmerresearch.driverUtilities.SensorDetails; +import com.shimmerresearch.driverUtilities.SensorDetailsRef; +import com.shimmerresearch.driverUtilities.SensorGroupingDetails; +import com.shimmerresearch.driverUtilities.ShimmerVerObject; +import com.shimmerresearch.driverUtilities.ChannelDetails.CHANNEL_DATA_ENDIAN; +import com.shimmerresearch.driverUtilities.ChannelDetails.CHANNEL_DATA_TYPE; +import com.shimmerresearch.driverUtilities.ChannelDetails.CHANNEL_TYPE; +import com.shimmerresearch.sensors.AbstractSensor; +import com.shimmerresearch.sensors.ActionSetting; +import com.shimmerresearch.sensors.bmpX80.SensorBMPX80.GuiLabelConfig; +import com.shimmerresearch.sensors.bmpX80.SensorBMPX80.GuiLabelSensors; +import com.shimmerresearch.sensors.bmpX80.SensorBMPX80.LABEL_SENSOR_TILE; + +/** + * Pressure/Temperature sensor class for the Bosch BMP581, fitted to up-rev'd + * Shimmer3R boards in place of the BMP390. + * + * Unlike BMP180/BMP280/BMP390, the BMP581 streams already-compensated pressure + * (Pa) and temperature (deg C) directly, so there are no calibration + * coefficients to fetch, store or apply (see {@link CalibDetailsBmp581}). The + * on-wire packet layout (3 bytes pressure + 3 bytes temperature, both 24-bit) + * matches the BMP390, so channel sizes/offsets are unchanged. + * + * @author Shimmer + */ +public class SensorBMP581 extends SensorBMPX80 { + + private static final long serialVersionUID = 5921564795919021001L; + + //--------- Sensor specific variables start -------------- + public static class DatabaseChannelHandles { + public static final String PRESSURE_BMP581 = "BMP581_Pressure"; + public static final String TEMPERATURE_BMP581 = "BMP581_Temperature"; + } + + public static final class DatabaseConfigHandle { + public static final String PRESSURE_PRECISION_BMP581 = "BMP581_Pressure_Precision"; + public static final String PRESSURE_RATE = "BMP581_Pres_Rate"; + // NB: no PAR_* calibration handles - BMP581 has no downloadable coefficients. + } + + public static final class ObjectClusterSensorName { + public static final String TEMPERATURE_BMP581 = "Temperature_BMP581"; + public static final String PRESSURE_BMP581 = "Pressure_BMP581"; + } + //--------- Sensor specific variables end -------------- + + //--------- Constructors for this class start -------------- + public SensorBMP581(ShimmerVerObject svo) { + super(SENSORS.BMP581, svo); + initialise(); + } + + public SensorBMP581(ShimmerDevice shimmerDevice) { + super(SENSORS.BMP581, shimmerDevice); + initialise(); + } + //--------- Constructors for this class end -------------- + + //--------- Bluetooth commands start -------------- + // Oversampling only. There is deliberately NO GET_PRESSURE_CALIBRATION_COEFFICIENTS + // command - the BMP581 firmware NACKs it because the sensor self-compensates. + public static final byte SET_PRESSURE_OVERSAMPLING_RATIO_COMMAND = (byte) 0x52; + public static final byte PRESSURE_OVERSAMPLING_RATIO_RESPONSE = (byte) 0x53; + public static final byte GET_PRESSURE_OVERSAMPLING_RATIO_COMMAND = (byte) 0x54; + + public static final Map mBtGetCommandMap; + static { + Map aMap = new LinkedHashMap(); + aMap.put(GET_PRESSURE_OVERSAMPLING_RATIO_COMMAND, new BtCommandDetails(GET_PRESSURE_OVERSAMPLING_RATIO_COMMAND, "GET_PRESSURE_OVERSAMPLING_RATIO_COMMAND", PRESSURE_OVERSAMPLING_RATIO_RESPONSE)); + mBtGetCommandMap = Collections.unmodifiableMap(aMap); + } + + public static final Map mBtSetCommandMap; + static { + Map aMap = new LinkedHashMap(); + aMap.put(SET_PRESSURE_OVERSAMPLING_RATIO_COMMAND, new BtCommandDetails(SET_PRESSURE_OVERSAMPLING_RATIO_COMMAND, "SET_PRESSURE_OVERSAMPLING_RATIO_COMMAND")); + mBtSetCommandMap = Collections.unmodifiableMap(aMap); + } + //--------- Bluetooth commands end -------------- + + //--------- Configuration options start -------------- + // BMP581 gains two extra oversampling steps (x64, x128) vs the BMP390 (0..5). + public static final String[] ListofPressureResolutionBMP581 = {"Lowest Power","Low","Standard","High","High Res","Very High Res","Ultra High Res","Highest Res"}; + public static final Integer[] ListofPressureResolutionConfigValuesBMP581 = {0,1,2,3,4,5,6,7}; + public static final String[] ListofPressureRateBMP581 = SensorBMP390.ListofPressureRateBMP390; + public static final Integer[] ListofPressureRateConfigValuesBMP581 = SensorBMP390.ListofPressureRateConfigValuesBMP390; + + public static final ConfigOptionDetailsSensor configOptionPressureResolutionBMP581 = new ConfigOptionDetailsSensor( + SensorBMPX80.GuiLabelConfig.PRESSURE_RESOLUTION, + DatabaseConfigHandle.PRESSURE_PRECISION_BMP581, + ListofPressureResolutionBMP581, + ListofPressureResolutionConfigValuesBMP581, + ConfigOptionDetailsSensor.GUI_COMPONENT_TYPE.COMBOBOX, + CompatibilityInfoForMaps.listOfCompatibleVersionInfoBMP581); + + public static final ConfigOptionDetailsSensor configOptionPressureRateBMP581 = new ConfigOptionDetailsSensor( + SensorBMPX80.GuiLabelConfig.PRESSURE_RATE, + DatabaseConfigHandle.PRESSURE_RATE, + ListofPressureRateBMP581, + ListofPressureRateConfigValuesBMP581, + ConfigOptionDetailsSensor.GUI_COMPONENT_TYPE.COMBOBOX, + CompatibilityInfoForMaps.listOfCompatibleVersionInfoBMP581); + //--------- Configuration options end -------------- + + //--------- Sensor info start -------------- + public static final SensorDetailsRef sensorBmp581 = new SensorDetailsRef( + 0x04 << (2 * 8), + 0x04 << (2 * 8), + GuiLabelSensors.PRESS_TEMP_BMPX80, + CompatibilityInfoForMaps.listOfCompatibleVersionInfoBMP581, + Arrays.asList(GuiLabelConfig.PRESSURE_RESOLUTION), + Arrays.asList(ObjectClusterSensorName.TEMPERATURE_BMP581, + ObjectClusterSensorName.PRESSURE_BMP581)); + + public static final Map mSensorMapRef; + static { + Map aMap = new LinkedHashMap(); + aMap.put(Configuration.Shimmer3.SENSOR_ID.SHIMMER_BMP581_PRESSURE, sensorBmp581); + mSensorMapRef = Collections.unmodifiableMap(aMap); + } + + public static final SensorGroupingDetails sensorGroupBmp581 = new SensorGroupingDetails( + LABEL_SENSOR_TILE.PRESSURE_TEMPERATURE, + Arrays.asList(Configuration.Shimmer3.SENSOR_ID.SHIMMER_BMP581_PRESSURE), + CompatibilityInfoForMaps.listOfCompatibleVersionInfoBMP581); + //--------- Sensor info end -------------- + + //--------- Channel info start -------------- + public static final ChannelDetails channelBmp581Press = new ChannelDetails( + ObjectClusterSensorName.PRESSURE_BMP581, + ObjectClusterSensorName.PRESSURE_BMP581, + DatabaseChannelHandles.PRESSURE_BMP581, + CHANNEL_DATA_TYPE.UINT24, 3, CHANNEL_DATA_ENDIAN.MSB, + CHANNEL_UNITS.KPASCAL, + Arrays.asList(CHANNEL_TYPE.CAL, CHANNEL_TYPE.UNCAL)); + + public static final ChannelDetails channelBmp581Temp = new ChannelDetails( + ObjectClusterSensorName.TEMPERATURE_BMP581, + ObjectClusterSensorName.TEMPERATURE_BMP581, + DatabaseChannelHandles.TEMPERATURE_BMP581, + CHANNEL_DATA_TYPE.UINT24, 3, CHANNEL_DATA_ENDIAN.MSB, + CHANNEL_UNITS.DEGREES_CELSIUS, + Arrays.asList(CHANNEL_TYPE.CAL, CHANNEL_TYPE.UNCAL)); + + public static final Map mChannelMapRef; + static { + Map aMap = new LinkedHashMap(); + aMap.put(ObjectClusterSensorName.PRESSURE_BMP581, channelBmp581Press); + aMap.put(ObjectClusterSensorName.TEMPERATURE_BMP581, channelBmp581Temp); + mChannelMapRef = Collections.unmodifiableMap(aMap); + } + //--------- Channel info end -------------- + + @Override + public void setPressureResolution(int i) { + if (ArrayUtils.contains(ListofPressureResolutionConfigValuesBMP581, i)) { + mPressureResolution = i; + } + updateCurrentPressureCalibInUse(); + } + + @Override + public List getPressTempConfigValuesLegacy() { + // BMP581 has no calibration coefficients to expose. + return new ArrayList(); + } + + @Override + public void generateSensorMap() { + super.createLocalSensorMapWithCustomParser(mSensorMapRef, mChannelMapRef); + } + + @Override + public void generateConfigOptionsMap() { + addConfigOption(configOptionPressureResolutionBMP581); + addConfigOption(configOptionPressureRateBMP581); + } + + @Override + public void generateSensorGroupMapping() { + mSensorGroupingMap = new LinkedHashMap(); + if (mShimmerVerObject.isShimmerGen3R()) { + mSensorGroupingMap.put(Configuration.Shimmer3.LABEL_SENSOR_TILE.PRESSURE_TEMPERATURE_BMP581.ordinal(), sensorGroupBmp581); + } + super.updateSensorGroupingMap(); + } + + @Override + public ObjectCluster processDataCustom(SensorDetails sensorDetails, byte[] rawData, COMMUNICATION_TYPE commType, + ObjectCluster objectCluster, boolean isTimeSyncEnabled, double pctimeStampMs) { + objectCluster = sensorDetails.processDataCommon(rawData, commType, objectCluster, isTimeSyncEnabled, pctimeStampMs); + + for (ChannelDetails channelDetails : sensorDetails.mListOfChannels) { + if (channelDetails.mObjectClusterName.equals(ObjectClusterSensorName.PRESSURE_BMP581)) { + double raw = ((FormatCluster) ObjectCluster.returnFormatCluster(objectCluster.getCollectionOfFormatClusters(ObjectClusterSensorName.PRESSURE_BMP581), channelDetails.mChannelFormatDerivedFromShimmerDataPacket.toString())).mData; + // BMP581 pressure is pre-compensated: raw/64 = Pa, /1000 -> kPa + double calPressure = raw / 64000.0; + objectCluster.addCalData(channelDetails, calPressure, objectCluster.getIndexKeeper() - 2); + objectCluster.incrementIndexKeeper(); + } + if (channelDetails.mObjectClusterName.equals(ObjectClusterSensorName.TEMPERATURE_BMP581)) { + double raw = ((FormatCluster) ObjectCluster.returnFormatCluster(objectCluster.getCollectionOfFormatClusters(ObjectClusterSensorName.TEMPERATURE_BMP581), channelDetails.mChannelFormatDerivedFromShimmerDataPacket.toString())).mData; + // BMP581 temperature is pre-compensated: raw/65536 = deg C + double calTemp = raw / 65536.0; + objectCluster.addCalData(channelDetails, calTemp, objectCluster.getIndexKeeper() - 1); + objectCluster.incrementIndexKeeper(); + } + } + + super.consolePrintChannelsCal(objectCluster, Arrays.asList( + new String[]{ObjectClusterSensorName.PRESSURE_BMP581, CHANNEL_TYPE.UNCAL.toString()}, + new String[]{ObjectClusterSensorName.TEMPERATURE_BMP581, CHANNEL_TYPE.UNCAL.toString()}, + new String[]{ObjectClusterSensorName.PRESSURE_BMP581, CHANNEL_TYPE.CAL.toString()}, + new String[]{ObjectClusterSensorName.TEMPERATURE_BMP581, CHANNEL_TYPE.CAL.toString()})); + + return objectCluster; + } + + @Override + public void generateCalibMap() { + mCalibDetailsBmpX80 = new CalibDetailsBmp581(mShimmerDevice.mMacIdFromUart); + super.generateCalibMap(); + } + + @Override + public void checkShimmerConfigBeforeConfiguring() { + if (!isSensorEnabled(Configuration.Shimmer3.SENSOR_ID.SHIMMER_BMP581_PRESSURE)) { + setDefaultBmp581PressureSensorConfig(false); + } + } + + @Override + public Object setConfigValueUsingConfigLabel(Integer sensorId, String configLabel, Object valueToSet) { + Object returnValue = null; + switch (configLabel) { + case (GuiLabelConfig.PRESSURE_RESOLUTION): + setPressureResolution((int) valueToSet); + returnValue = valueToSet; + break; + } + return returnValue; + } + + @Override + public Object getConfigValueUsingConfigLabel(Integer sensorId, String configLabel) { + Object returnValue = null; + switch (configLabel) { + case (GuiLabelConfig.PRESSURE_RESOLUTION): + returnValue = getPressureResolution(); + break; + } + return returnValue; + } + + @Override + public void setSensorSamplingRate(double samplingRateHz) { + // TODO Auto-generated method stub + } + + @Override + public boolean setDefaultConfigForSensor(int sensorId, boolean isSensorEnabled) { + if (mSensorMap.containsKey(sensorId)) { + if (sensorId == Configuration.Shimmer3.SENSOR_ID.SHIMMER_BMP581_PRESSURE) { + setDefaultBmp581PressureSensorConfig(isSensorEnabled); + return true; + } + } + return false; + } + + @Override + public boolean checkConfigOptionValues(String stringKey) { + return mConfigOptionsMap.containsKey(stringKey); + } + + @Override + public Object getSettings(String componentName, COMMUNICATION_TYPE commType) { + // TODO Auto-generated method stub + return null; + } + + @Override + public ActionSetting setSettings(String componentName, Object valueToSet, COMMUNICATION_TYPE commType) { + ActionSetting actionSetting = new ActionSetting(commType); + switch (componentName) { + case (GuiLabelConfig.PRESSURE_RESOLUTION): + setPressureResolution((int) valueToSet); + break; + } + return actionSetting; + } + + @Override + public boolean processResponse(int responseCommand, Object parsedResponse, COMMUNICATION_TYPE commType) { + // TODO Auto-generated method stub + return false; + } + + @Override + public LinkedHashMap generateConfigMap() { + LinkedHashMap mapOfConfig = new LinkedHashMap(); + mapOfConfig.put(DatabaseConfigHandle.PRESSURE_PRECISION_BMP581, getPressureResolution()); + // No PAR_* rows - BMP581 has no calibration coefficients. + return mapOfConfig; + } + + @Override + public void parseConfigMap(LinkedHashMap mapOfConfigPerShimmer) { + if (mapOfConfigPerShimmer.containsKey(DatabaseConfigHandle.PRESSURE_PRECISION_BMP581)) { + setPressureResolution(((Double) mapOfConfigPerShimmer.get(DatabaseConfigHandle.PRESSURE_PRECISION_BMP581)).intValue()); + } + } + + //--------- Sensor specific methods start -------------- + private void setDefaultBmp581PressureSensorConfig(boolean isSensorEnabled) { + if (isSensorEnabled) { + } else { + mPressureResolution = 0; + } + } + + public static String parseFromDBColumnToGUIChannel(String databaseChannelHandle) { + return AbstractSensor.parseFromDBColumnToGUIChannel(mChannelMapRef, databaseChannelHandle); + } + + public static String parseFromGUIChannelsToDBColumn(String objectClusterName) { + return AbstractSensor.parseFromGUIChannelsToDBColumn(mChannelMapRef, objectClusterName); + } + //--------- Sensor specific methods end -------------- + + // Config-byte pack/parse is identical to BMP390: the resolution field is already + // 3 bits wide (LSB 2 bits + MSB 1 bit), so it holds the BMP581 range 0..7 unchanged. + @Override + public void configBytesParse(ShimmerDevice shimmerDevice, byte[] configBytes, COMMUNICATION_TYPE commType) { + ConfigByteLayout configByteLayout = shimmerDevice.getConfigByteLayout(); + if (configByteLayout instanceof ConfigByteLayoutShimmer3) { + ConfigByteLayoutShimmer3 configByteLayoutCast = (ConfigByteLayoutShimmer3) configByteLayout; + + int lsbPressureResolution = (configBytes[configByteLayoutCast.idxConfigSetupByte3] + >> configByteLayoutCast.bitShiftBMPX80PressureResolution) + & configByteLayoutCast.maskBMPX80PressureResolution; + + int msbPressureResolution = (configBytes[configByteLayoutCast.idxConfigSetupByte4] + >> configByteLayoutCast.bitShiftBMP390PressureResolution) + & configByteLayoutCast.maskBMP390PressureResolution; + + setPressureResolution(((msbPressureResolution << 2) | lsbPressureResolution)); + } + } + + @Override + public void configBytesGenerate(ShimmerDevice shimmerDevice, byte[] configBytes, COMMUNICATION_TYPE commType) { + ConfigByteLayout configByteLayout = shimmerDevice.getConfigByteLayout(); + if (configByteLayout instanceof ConfigByteLayoutShimmer3) { + ConfigByteLayoutShimmer3 configByteLayoutCast = (ConfigByteLayoutShimmer3) configByteLayout; + configBytes[configByteLayoutCast.idxConfigSetupByte4] |= (byte) (((getPressureResolution() >> 2) & configByteLayoutCast.maskBMP390PressureResolution) << configByteLayoutCast.bitShiftBMP390PressureResolution); + configBytes[configByteLayoutCast.idxConfigSetupByte3] |= (byte) ((getPressureResolution() & configByteLayoutCast.maskBMPX80PressureResolution) << configByteLayoutCast.bitShiftBMPX80PressureResolution); + } + } + +} diff --git a/ShimmerPCBasicExamples/src/main/java/com/shimmerresearch/simpleexamples/SensorMapsExample.java b/ShimmerPCBasicExamples/src/main/java/com/shimmerresearch/simpleexamples/SensorMapsExample.java index 0d9ee9f9..a9666ee8 100644 --- a/ShimmerPCBasicExamples/src/main/java/com/shimmerresearch/simpleexamples/SensorMapsExample.java +++ b/ShimmerPCBasicExamples/src/main/java/com/shimmerresearch/simpleexamples/SensorMapsExample.java @@ -10,6 +10,7 @@ import com.shimmerresearch.driver.Configuration.COMMUNICATION_TYPE; import com.shimmerresearch.driver.ObjectCluster; import com.shimmerresearch.driver.ShimmerDevice; +import com.shimmerresearch.driver.ShimmerObject; import com.shimmerresearch.driver.ShimmerMsg; import com.shimmerresearch.driverUtilities.AssembleShimmerConfig; import com.shimmerresearch.driverUtilities.BluetoothDeviceDetails; @@ -45,6 +46,8 @@ import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JCheckBox; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; import javax.swing.JTextPane; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; @@ -71,6 +74,7 @@ public class SensorMapsExample extends BasicProcessWithCallBack { private JFrame frame; private JTextField textField; JTextPane textPaneStatus; + static JTextArea textAreaDeviceSpec; static ShimmerDevice shimmerDevice; static BasicShimmerBluetoothManagerPc btManager = new BasicShimmerBluetoothManagerPc(); BasicPlotManagerPC plotManager = new BasicPlotManagerPC(); @@ -396,6 +400,18 @@ public void actionPerformed(ActionEvent e) { comboBox = new JComboBox<>(comboModel); comboBox.setBounds(164, 59, 116, 22); frame.getContentPane().add(comboBox); + + JLabel lblDeviceSpec = new JLabel("Device Specifications"); + lblDeviceSpec.setBounds(830, 34, 200, 16); + frame.getContentPane().add(lblDeviceSpec); + + textAreaDeviceSpec = new JTextArea(); + textAreaDeviceSpec.setEditable(false); + textAreaDeviceSpec.setLineWrap(false); + textAreaDeviceSpec.setText("Not connected"); + JScrollPane deviceSpecScrollPane = new JScrollPane(textAreaDeviceSpec); + deviceSpecScrollPane.setBounds(830, 52, 354, 122); + frame.getContentPane().add(deviceSpecScrollPane); JButton btnNewButton = new JButton("Sync (Verisense)"); btnNewButton.addActionListener(new ActionListener() { @@ -515,6 +531,58 @@ public void run() { + /** + * Builds a multi-line summary of the connected device's specifications + * (HW/FW versions, expansion/daughter board, MAC, etc.) for display on the UI. + */ + static String buildDeviceSpecString(ShimmerDevice device) { + if (device == null) { + return "Not connected"; + } + + StringBuilder sb = new StringBuilder(); + sb.append("Name:\t").append(device.getShimmerUserAssignedName()).append("\n"); + sb.append("MAC:\t").append(device.getMacId()).append("\n"); + sb.append("HW:\t").append(device.getHardwareVersionParsed()) + .append(" (id ").append(device.getHardwareVersion()).append(")\n"); + sb.append("FW:\t").append(device.getFirmwareVersionParsed()).append("\n"); + + if (device instanceof VerisenseDevice) { + return sb.toString(); + } + + // Shimmer3/3R specifics: daughter (expansion) board + BMP581 eligibility + sb.append("Exp board:\t").append(device.getExpansionBoardParsedWithVer()).append("\n"); + sb.append("Exp board id:\t").append(device.getExpansionBoardId()) + .append(", rev ").append(device.getExpansionBoardRev()) + .append(".").append(device.getExpansionBoardRevSpecial()).append("\n"); + sb.append("Sampling rate:\t").append(device.getSamplingRateShimmer()).append(" Hz\n"); + + if (device instanceof ShimmerObject) { + sb.append("BMP581 supported:\t").append(((ShimmerObject) device).isSupportedBmp581()).append("\n"); + } + return sb.toString(); + } + + /** Refresh the on-screen device spec panel from the currently connected device. */ + static void updateDeviceSpec() { + if (textAreaDeviceSpec == null) { + return; + } + final ShimmerDevice device = btManager.getShimmerDeviceBtConnected(btComport); + final String specString = buildDeviceSpecString(device); + javax.swing.SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (textAreaDeviceSpec == null) { + return; + } + textAreaDeviceSpec.setText(specString); + textAreaDeviceSpec.setCaretPosition(0); + } + }); + } + @Override protected void processMsgFromCallback(ShimmerMsg shimmerMSG) { // TODO Auto-generated method stub @@ -535,6 +603,7 @@ protected void processMsgFromCallback(ShimmerMsg shimmerMSG) { timer = new Timer(); } textPaneStatus.setText("connected"); + updateDeviceSpec(); //shimmer = (ShimmerPC) btManager.getShimmerDeviceBtConnected(btComport); // shimmerDevice = btManager.getShimmerDeviceBtConnected(btComport); @@ -555,7 +624,10 @@ protected void processMsgFromCallback(ShimmerMsg shimmerMSG) { timer.cancel(); timer = null; } - textPaneStatus.setText("disconnected"); + textPaneStatus.setText("disconnected"); + if (textAreaDeviceSpec != null) { + textAreaDeviceSpec.setText("Not connected"); + } }else if (callbackObject.mState == BT_STATE.STREAMING) { if (timer!=null){ timer.cancel(); @@ -572,6 +644,7 @@ protected void processMsgFromCallback(ShimmerMsg shimmerMSG) { int msg = callbackObject.mIndicator; if (msg== ShimmerPC.NOTIFICATION_SHIMMER_FULLY_INITIALIZED){ textPaneStatus.setText("device fully initialized"); + updateDeviceSpec(); ShimmerDevice device = btManager.getShimmerDeviceBtConnectedFromMac(btComport); if (btComport.contains(PREFIX_COM) || btComport.contains(PREFIX_DEV)) { device = btManager.getShimmerDeviceBtConnected(btComport);