mirror of
https://github.com/oliexdev/openScale.git
synced 2025-08-14 04:34:18 +02:00
Support for 1byone scale (new version) (#820)
This commit is contained in:
@@ -77,6 +77,7 @@ public class OpenScale {
|
||||
public static boolean DEBUG_MODE = false;
|
||||
|
||||
public static final String DATABASE_NAME = "openScale.db";
|
||||
public static final float SMART_USER_ASSIGN_DEFAULT_RANGE = 15.0F;
|
||||
|
||||
private static OpenScale instance;
|
||||
|
||||
@@ -273,11 +274,7 @@ public class OpenScale {
|
||||
|
||||
// Check user id and do a smart user assign if option is enabled
|
||||
if (scaleMeasurement.getUserId() == -1) {
|
||||
if (prefs.getBoolean("smartUserAssign", false)) {
|
||||
scaleMeasurement.setUserId(getSmartUserAssignment(scaleMeasurement.getWeight(), 15.0f));
|
||||
} else {
|
||||
scaleMeasurement.setUserId(getSelectedScaleUser().getId());
|
||||
}
|
||||
scaleMeasurement.setUserId(getAssignableUser(scaleMeasurement.getWeight()));
|
||||
|
||||
// don't add scale data if no user is selected
|
||||
if (scaleMeasurement.getUserId() == -1) {
|
||||
@@ -360,6 +357,20 @@ public class OpenScale {
|
||||
return scaleMeasurement.getUserId();
|
||||
}
|
||||
|
||||
|
||||
public int getAssignableUser(float weight){
|
||||
// Not the best function name
|
||||
// Returns smart user assignment, if options allow it
|
||||
// Otherwise it returns the selected user
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
if (prefs.getBoolean("smartUserAssign", false)) {
|
||||
return getSmartUserAssignment(weight, SMART_USER_ASSIGN_DEFAULT_RANGE);
|
||||
} else {
|
||||
return getSelectedScaleUser().getId();
|
||||
}
|
||||
}
|
||||
|
||||
private int getSmartUserAssignment(float weight, float range) {
|
||||
List<ScaleUser> scaleUsers = getScaleUserList();
|
||||
Map<Float, Integer> inRangeWeights = new TreeMap<>();
|
||||
|
@@ -82,6 +82,10 @@ public class BluetoothFactory {
|
||||
if (name.equals("Health Scale".toLowerCase(Locale.US))) {
|
||||
return new BluetoothOneByone(context);
|
||||
}
|
||||
if(name.equals("1byone scale".toLowerCase(Locale.US))){
|
||||
return new BluetoothOneByoneNew(context);
|
||||
}
|
||||
|
||||
if (name.equals("SENSSUN FAT".toLowerCase(Locale.US))) {
|
||||
return new BluetoothSenssun(context);
|
||||
}
|
||||
|
@@ -0,0 +1,333 @@
|
||||
package com.health.openscale.core.bluetooth;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.health.openscale.core.OpenScale;
|
||||
import com.health.openscale.core.bluetooth.lib.OneByoneNewLib;
|
||||
import com.health.openscale.core.datatypes.ScaleMeasurement;
|
||||
import com.health.openscale.core.datatypes.ScaleUser;
|
||||
import com.health.openscale.core.utils.Converters;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class BluetoothOneByoneNew extends BluetoothCommunication{
|
||||
private final UUID WEIGHT_MEASUREMENT_SERVICE = BluetoothGattUuid.fromShortCode(0xffb0);
|
||||
private final UUID WEIGHT_MEASUREMENT_CHARACTERISTIC_BODY_COMPOSITION = BluetoothGattUuid.fromShortCode(0xffb2);
|
||||
private final UUID CMD_AFTER_MEASUREMENT = BluetoothGattUuid.fromShortCode(0xffb1);
|
||||
|
||||
private final int MSG_LENGTH = 20;
|
||||
private final byte[] HEADER_BYTES = { (byte)0xAB, (byte)0x2A };
|
||||
|
||||
private ScaleMeasurement currentMeasurement;
|
||||
|
||||
public BluetoothOneByoneNew(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBluetoothNotify(UUID characteristic, byte[] data){
|
||||
if(data == null){
|
||||
Timber.e("Received an empty message");
|
||||
return;
|
||||
}
|
||||
|
||||
Timber.i("Received %s", new BigInteger(1, data).toString(16));
|
||||
|
||||
if(data.length < MSG_LENGTH){
|
||||
Timber.e("Received a message too short");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!(data[0] == HEADER_BYTES[0] && data[1] == HEADER_BYTES[1])){
|
||||
Timber.e("Unrecognized message received from scale.");
|
||||
}
|
||||
|
||||
float weight;
|
||||
int impedance;
|
||||
|
||||
|
||||
switch(data[2]){
|
||||
case (byte)0x00:
|
||||
// real time measurement OR historic measurement
|
||||
// real time has the exact same format of 0x80, but we can ignore it
|
||||
// we want to capture the historic measures
|
||||
|
||||
// filter out real time measurments
|
||||
if (data[7] != (byte)0x80){
|
||||
Timber.i("Received real-time measurement. Skipping.");
|
||||
break;
|
||||
}
|
||||
|
||||
Date time = getTimestamp32(data, 3);
|
||||
weight = Converters.fromUnsignedInt24Be(data, 8) & 0x03ffff;
|
||||
weight /= 1000;
|
||||
impedance = Converters.fromUnsignedInt16Be(data, 15);
|
||||
|
||||
ScaleMeasurement historicMeasurement = new ScaleMeasurement();
|
||||
int assignableUserId = OpenScale.getInstance().getAssignableUser(weight);
|
||||
if(assignableUserId == -1){
|
||||
Timber.i("Discarding historic measurement: no user found with intelligent user recognition");
|
||||
break;
|
||||
}
|
||||
populateMeasurement(assignableUserId, historicMeasurement, impedance, weight);
|
||||
historicMeasurement.setDateTime(time);
|
||||
addScaleMeasurement(historicMeasurement);
|
||||
Timber.i("Added historic measurement. Weight: %s, impedance: %s, timestamp: %s", weight, impedance, time.toString());
|
||||
break;
|
||||
|
||||
case (byte)0x80:
|
||||
// final measurement
|
||||
currentMeasurement = new ScaleMeasurement();
|
||||
weight = Converters.fromUnsignedInt24Be(data, 3) & 0x03ffff;
|
||||
weight = weight / 1000;
|
||||
currentMeasurement.setWeight(weight);
|
||||
Timber.d("Weight: %s", weight);
|
||||
break;
|
||||
case (byte)0x01:
|
||||
impedance = Converters.fromUnsignedInt16Be(data, 4);
|
||||
Timber.d("impedance: %s", impedance);
|
||||
|
||||
if(currentMeasurement == null){
|
||||
Timber.e("Received impedance value without weight");
|
||||
break;
|
||||
}
|
||||
|
||||
float measurementWeight = currentMeasurement.getWeight();
|
||||
ScaleUser user = OpenScale.getInstance().getSelectedScaleUser();
|
||||
populateMeasurement(user.getId(), currentMeasurement, impedance, measurementWeight);
|
||||
addScaleMeasurement(currentMeasurement);
|
||||
resumeMachineState();
|
||||
break;
|
||||
default:
|
||||
Timber.e("Unrecognized message receveid");
|
||||
}
|
||||
}
|
||||
|
||||
private void populateMeasurement(int userId, ScaleMeasurement measurement, int impedance, float weight) {
|
||||
if(userId == -1){
|
||||
Timber.e("Discarding measurement population since invalid user");
|
||||
return;
|
||||
}
|
||||
ScaleUser user = OpenScale.getInstance().getScaleUser(userId);
|
||||
float cmHeight = Converters.fromCentimeter(user.getBodyHeight(), user.getMeasureUnit());
|
||||
OneByoneNewLib onebyoneLib = new OneByoneNewLib(getUserGender(user), user.getAge(), cmHeight, user.getActivityLevel().toInt());
|
||||
measurement.setUserId(userId);
|
||||
measurement.setWeight(weight);
|
||||
measurement.setDateTime(Calendar.getInstance().getTime());
|
||||
measurement.setFat(onebyoneLib.getBodyFatPercentage(weight, impedance));
|
||||
measurement.setWater(onebyoneLib.getWaterPercentage(weight, impedance));
|
||||
measurement.setBone(onebyoneLib.getBoneMass(weight, impedance));
|
||||
measurement.setVisceralFat(onebyoneLib.getVisceralFat(weight));
|
||||
measurement.setMuscle(onebyoneLib.getSkeletonMusclePercentage(weight, impedance));
|
||||
measurement.setLbm(onebyoneLib.getLBM(weight, impedance));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String driverName() {
|
||||
return "OneByoneNew";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onNextStep(int stepNr) {
|
||||
switch(stepNr){
|
||||
case 0:
|
||||
setNotificationOn(WEIGHT_MEASUREMENT_SERVICE, WEIGHT_MEASUREMENT_CHARACTERISTIC_BODY_COMPOSITION);
|
||||
break;
|
||||
case 1:
|
||||
// Setup notification on new weight
|
||||
sendWeightRequest();
|
||||
|
||||
// Update the user history on the scale
|
||||
// Priority given to the current user
|
||||
ScaleUser currentUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||
sendUsersHistory(currentUser.getId());
|
||||
|
||||
// We wait for the response
|
||||
stopMachineState();
|
||||
break;
|
||||
case 2:
|
||||
// After the measurement took place, we store the data and send back to the scale
|
||||
sendUsersHistory(OpenScale.getInstance().getSelectedScaleUserId());
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendWeightRequest() {
|
||||
byte[] msgSetup = new byte[MSG_LENGTH];
|
||||
setupMeasurementMessage(msgSetup, 0);
|
||||
writeBytes(WEIGHT_MEASUREMENT_SERVICE, CMD_AFTER_MEASUREMENT, msgSetup, true);
|
||||
}
|
||||
|
||||
private void sendUsersHistory(int priorityUser){
|
||||
List<ScaleUser> scaleUsers = OpenScale.getInstance().getScaleUserList();
|
||||
Collections.sort(scaleUsers, (ScaleUser u1, ScaleUser u2) -> {
|
||||
if(u1.getId() == priorityUser) return -9999;
|
||||
if(u2.getId() == priorityUser) return 9999;
|
||||
Date u1LastMeasureDate = OpenScale.getInstance().getLastScaleMeasurement(u1.getId()).getDateTime();
|
||||
Date u2LastMeasureDate = OpenScale.getInstance().getLastScaleMeasurement(u2.getId()).getDateTime();
|
||||
return u1LastMeasureDate.compareTo(u2LastMeasureDate);
|
||||
}
|
||||
);
|
||||
byte[] msg = new byte[MSG_LENGTH];
|
||||
int msgCounter = 0;
|
||||
for(int i = 0; i < scaleUsers.size(); i++){
|
||||
ScaleUser user = scaleUsers.get(i);
|
||||
ScaleMeasurement lastMeasure = OpenScale.getInstance().getLastScaleMeasurement(user.getId());
|
||||
float weight = 0;
|
||||
int impedance = 0;
|
||||
if(lastMeasure != null){
|
||||
weight = lastMeasure.getWeight();
|
||||
impedance = getImpedanceFromLBM(user, lastMeasure);
|
||||
}
|
||||
|
||||
int entryPosition = i % 2;
|
||||
|
||||
if (entryPosition == 0){
|
||||
msg = new byte[MSG_LENGTH];
|
||||
msgCounter ++;
|
||||
msg[0] = HEADER_BYTES[0];
|
||||
msg[1] = HEADER_BYTES[1];
|
||||
msg[2] = (byte) scaleUsers.size();
|
||||
msg[3] = (byte) msgCounter;
|
||||
}
|
||||
|
||||
setMeasurementEntry(msg, 4 + entryPosition * 7, i + 1,
|
||||
Math.round(user.getBodyHeight()),
|
||||
weight,
|
||||
getUserGender(user),
|
||||
user.getAge(),
|
||||
impedance,
|
||||
true);
|
||||
|
||||
if (entryPosition == 1 || i + 1 == scaleUsers.size()){
|
||||
msg[18] = (byte)0xD4;
|
||||
msg[19] = d4Checksum(msg, 0, MSG_LENGTH);
|
||||
writeBytes(WEIGHT_MEASUREMENT_SERVICE, CMD_AFTER_MEASUREMENT, msg, true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void setMeasurementEntry(byte[] msg, int offset, int entryNum, int height, float weight, int sex, int age, int impedance, boolean impedanceLe){
|
||||
// The scale wants a value rounded to the first decimal place
|
||||
// Otherwise we receive always a UP/DOWN arrow since we would communicate
|
||||
// AB.CX instead of AB.D0 where D0 is the approximation of CX and it is what the scale uses
|
||||
// to compute the UP/DOWN arrows
|
||||
int roundedWeight = Math.round( weight * 10) * 10;
|
||||
msg[offset] = (byte)(entryNum & 0xFF);
|
||||
msg[offset+1] = (byte)(height & 0xFF);
|
||||
Converters.toInt16Be(msg, offset+2, roundedWeight);
|
||||
msg[offset+4] = (byte)(((sex & 0xFF) << 7) + (age & 0x7F));
|
||||
|
||||
if(impedanceLe) {
|
||||
msg[offset + 5] = (byte) (impedance >> 8);
|
||||
msg[offset + 6] = (byte) impedance;
|
||||
} else {
|
||||
msg[offset + 5] = (byte) impedance;
|
||||
msg[offset + 6] = (byte) (impedance >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
private void setTimestamp32(byte[] msg, int offset){
|
||||
long timestamp = System.currentTimeMillis()/1000L;
|
||||
Converters.toInt32Be(msg, offset, timestamp);
|
||||
}
|
||||
|
||||
private Date getTimestamp32(byte[] msg, int offset){
|
||||
long timestamp = Converters.fromUnsignedInt32Be(msg, offset);
|
||||
return new Date(timestamp * 1000);
|
||||
}
|
||||
|
||||
private boolean setupMeasurementMessage(byte[] msg, int offset){
|
||||
if(offset + MSG_LENGTH > msg.length){
|
||||
return false;
|
||||
}
|
||||
|
||||
ScaleUser currentUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||
Converters.WeightUnit weightUnit = currentUser.getScaleUnit();
|
||||
|
||||
msg[offset] = HEADER_BYTES[0];
|
||||
msg[offset+1] = HEADER_BYTES[1];
|
||||
setTimestamp32(msg, offset+2);
|
||||
// This byte has been left empty in all the observations, unknown meaning
|
||||
msg[offset+6] = 0;
|
||||
msg[offset+7] = (byte) weightUnit.toInt();
|
||||
int userId = currentUser.getId();
|
||||
|
||||
|
||||
// We send the last measurement or if not present an empty one
|
||||
ScaleMeasurement lastMeasure = OpenScale.getInstance().getLastScaleMeasurement(userId);
|
||||
float weight = 0;
|
||||
int impedance = 0;
|
||||
if(lastMeasure != null){
|
||||
weight = lastMeasure.getWeight();
|
||||
impedance = getImpedanceFromLBM(currentUser, lastMeasure);
|
||||
}
|
||||
|
||||
setMeasurementEntry(msg, offset+8,
|
||||
userId,
|
||||
Math.round(currentUser.getBodyHeight()),
|
||||
weight,
|
||||
getUserGender(currentUser),
|
||||
currentUser.getAge(),
|
||||
impedance,
|
||||
false
|
||||
);
|
||||
|
||||
// Blank bytes after the empty measurement
|
||||
msg[offset + 18] = (byte) 0xD7;
|
||||
msg[offset+19] = d7Checksum(msg, offset+2, 17);
|
||||
return true;
|
||||
}
|
||||
|
||||
private int getUserGender(ScaleUser user){
|
||||
// Custom function since the toInt() gives the opposite values
|
||||
return user.getGender().isMale() ? 1 : 0;
|
||||
}
|
||||
|
||||
private byte d4Checksum(byte[] msg, int offset, int length){
|
||||
byte sum = sumChecksum(msg, offset + 2, length - 2);
|
||||
|
||||
// Remove impedance MSB first entry
|
||||
sum -= msg[offset+9];
|
||||
|
||||
// Remove second entry weight
|
||||
sum -= msg[offset+13];
|
||||
sum -= msg[offset+14];
|
||||
|
||||
// Remove impedance MSB second entry
|
||||
sum -= msg[offset+16];
|
||||
return sum;
|
||||
}
|
||||
|
||||
private byte d7Checksum(byte[] msg, int offset, int length){
|
||||
byte sum = sumChecksum(msg, offset+2, length-2);
|
||||
|
||||
// Remove impedance MSB
|
||||
sum -= msg[offset+14];
|
||||
return sum;
|
||||
}
|
||||
|
||||
// Since we need to send the impedance to the scale the next time,
|
||||
// we obtain it from the previous measurement using the LBM
|
||||
public int getImpedanceFromLBM(ScaleUser user, ScaleMeasurement measurement) {
|
||||
float finalLbm = measurement.getLbm();
|
||||
float postImpedanceLbm = finalLbm + user.getAge() * 0.0542F;
|
||||
float preImpedanceLbm = user.getBodyHeight() / 100 * user.getBodyHeight() / 100 * 9.058F + 12.226F + measurement.getWeight() * 0.32F;
|
||||
return Math.round((preImpedanceLbm - postImpedanceLbm) / 0.0068F);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,201 @@
|
||||
package com.health.openscale.core.bluetooth.lib;
|
||||
|
||||
// This class is similar to OneByoneLib, but the way measures are computer are slightly different
|
||||
public class OneByoneNewLib {
|
||||
|
||||
private int sex;
|
||||
private int age;
|
||||
private float height;
|
||||
private int peopleType; // low activity = 0; medium activity = 1; high activity = 2
|
||||
|
||||
public OneByoneNewLib(int sex, int age, float height, int peopleType) {
|
||||
this.sex = sex;
|
||||
this.age = age;
|
||||
this.height = height;
|
||||
this.peopleType = peopleType;
|
||||
}
|
||||
|
||||
public float getBMI(float weight) {
|
||||
float bmi = weight / (((height * height) / 100.0f) / 100.0f);
|
||||
return getBounded(bmi, 10, 90);
|
||||
}
|
||||
|
||||
public float getLBM(float weight, int impedance) {
|
||||
float lbmCoeff = height / 100 * height / 100 * 9.058F;
|
||||
lbmCoeff += 12.226;
|
||||
lbmCoeff += weight * 0.32;
|
||||
lbmCoeff -= impedance * 0.0068;
|
||||
lbmCoeff -= age * 0.0542;
|
||||
return lbmCoeff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public float getBMMRCoeff(float weight){
|
||||
int bmmrCoeff = 20;
|
||||
if(sex == 1){
|
||||
bmmrCoeff = 21;
|
||||
if(age < 0xd){
|
||||
bmmrCoeff = 36;
|
||||
} else if(age < 0x10){
|
||||
bmmrCoeff = 30;
|
||||
} else if(age < 0x12){
|
||||
bmmrCoeff = 26;
|
||||
} else if(age < 0x1e){
|
||||
bmmrCoeff = 23;
|
||||
} else if (age >= 0x32){
|
||||
bmmrCoeff = 20;
|
||||
}
|
||||
} else {
|
||||
if(age < 0xd){
|
||||
bmmrCoeff = 34;
|
||||
} else if(age < 0x10){
|
||||
bmmrCoeff = 29;
|
||||
} else if(age < 0x12){
|
||||
bmmrCoeff = 24;
|
||||
} else if(age < 0x1e){
|
||||
bmmrCoeff = 22;
|
||||
} else if (age >= 0x32){
|
||||
bmmrCoeff = 19;
|
||||
}
|
||||
}
|
||||
return bmmrCoeff;
|
||||
}
|
||||
|
||||
public float getBMMR(float weight){
|
||||
float bmmr;
|
||||
if(sex == 1){
|
||||
bmmr = (weight * 14.916F + 877.8F) - height * 0.726F;
|
||||
bmmr -= age * 8.976;
|
||||
} else {
|
||||
bmmr = (weight * 10.2036F + 864.6F) - height * 0.39336F;
|
||||
bmmr -= age * 6.204;
|
||||
}
|
||||
|
||||
return getBounded(bmmr, 500, 1000);
|
||||
}
|
||||
|
||||
public float getBodyFatPercentage(float weight, int impedance) {
|
||||
float bodyFat = getLBM(weight, impedance);
|
||||
|
||||
float bodyFatConst;
|
||||
if (sex == 0) {
|
||||
if (age < 0x32) {
|
||||
bodyFatConst = 9.25F;
|
||||
} else {
|
||||
bodyFatConst = 7.25F;
|
||||
}
|
||||
} else {
|
||||
bodyFatConst = 0.8F;
|
||||
}
|
||||
|
||||
bodyFat -= bodyFatConst;
|
||||
|
||||
if (sex == 0){
|
||||
if (weight < 50){
|
||||
bodyFat *= 1.02;
|
||||
} else if(weight > 60){
|
||||
bodyFat *= 0.96;
|
||||
}
|
||||
|
||||
if(height > 160){
|
||||
bodyFat *= 1.03;
|
||||
}
|
||||
} else {
|
||||
if (weight < 61){
|
||||
bodyFat *= 0.98;
|
||||
}
|
||||
}
|
||||
|
||||
return 100 * (1 - bodyFat / weight);
|
||||
}
|
||||
|
||||
public float getBoneMass(float weight, int impedance){
|
||||
float lbmCoeff = getLBM(weight, impedance);
|
||||
|
||||
float boneMassConst;
|
||||
if(sex == 1){
|
||||
boneMassConst = 0.18016894F;
|
||||
} else {
|
||||
boneMassConst = 0.245691014F;
|
||||
}
|
||||
|
||||
boneMassConst = lbmCoeff * 0.05158F - boneMassConst;
|
||||
float boneMass;
|
||||
if(boneMassConst <= 2.2){
|
||||
boneMass = boneMassConst - 0.1F;
|
||||
} else {
|
||||
boneMass = boneMassConst + 0.1F;
|
||||
}
|
||||
|
||||
return getBounded(boneMass, 0.5F, 8);
|
||||
}
|
||||
|
||||
public float getMuscleMass(float weight, int impedance){
|
||||
float muscleMass = weight - getBodyFatPercentage(weight, impedance) * 0.01F * weight;
|
||||
muscleMass -= getBoneMass(weight, impedance);
|
||||
return getBounded(muscleMass, 10, 120);
|
||||
}
|
||||
|
||||
public float getSkeletonMusclePercentage(float weight, int impedance){
|
||||
float skeletonMuscleMass = getWaterPercentage(weight, impedance);
|
||||
skeletonMuscleMass *= weight;
|
||||
skeletonMuscleMass *= 0.8422F * 0.01F;
|
||||
skeletonMuscleMass -= 2.9903;
|
||||
skeletonMuscleMass /= weight;
|
||||
return skeletonMuscleMass * 100;
|
||||
}
|
||||
|
||||
public float getVisceralFat(float weight){
|
||||
float visceralFat;
|
||||
if (sex == 1) {
|
||||
if (height < weight * 1.6 + 63.0) {
|
||||
visceralFat =
|
||||
age * 0.15F + ((weight * 305.0F) /((height * 0.0826F * height - height * 0.4F) + 48.0F) - 2.9F);
|
||||
}
|
||||
else {
|
||||
visceralFat = age * 0.15F + (weight * (height * -0.0015F + 0.765F) - height * 0.143F) - 5.0F;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (weight <= height * 0.5 - 13.0) {
|
||||
visceralFat = age * 0.07F + (weight * (height * -0.0024F + 0.691F) - height * 0.027F) - 10.5F;
|
||||
}
|
||||
else {
|
||||
visceralFat = age * 0.07F + ((weight * 500.0F) / ((height * 1.45F + height * 0.1158F * height) - 120.0F) - 6.0F);
|
||||
}
|
||||
}
|
||||
|
||||
return getBounded(visceralFat, 1, 50);
|
||||
}
|
||||
|
||||
public float getWaterPercentage(float weight, int impedance){
|
||||
float waterPercentage = (100 - getBodyFatPercentage(weight, impedance)) * 0.7F;
|
||||
if (waterPercentage > 50){
|
||||
waterPercentage *= 0.98;
|
||||
} else {
|
||||
waterPercentage *= 1.02;
|
||||
}
|
||||
|
||||
return getBounded(waterPercentage, 35, 75);
|
||||
}
|
||||
|
||||
public float getProteinPercentage(float weight, int impedance){
|
||||
return (
|
||||
(100.0F - getBodyFatPercentage(weight, impedance))
|
||||
- getWaterPercentage(weight, impedance) * 1.08F
|
||||
)
|
||||
- (getBoneMass(weight, impedance) / weight) * 100.0F;
|
||||
}
|
||||
|
||||
|
||||
private float getBounded(float value, float lowerBound, float upperBound){
|
||||
if(value < lowerBound){
|
||||
return lowerBound;
|
||||
} else if (value > upperBound){
|
||||
return upperBound;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user