VC31 explanation

Espruino recently added a blob from the manufacturer of the HRM sensor, VCare, to improve its accuracy. This post will try to explain the Algo_Input function.

Decompilation

void Algo_Input(int* param_1, int param_2, int param_3, int param_4)

{
  undefined4 uVar1;
  int iVar2;
  int iVar3;
  int iVar4;
  int iVar5;
  int iVar6;
  uint uVar7;
  uint uVar8;
  int iVar9;
  uint uVar10;
  bool bVar11;
  bool bVar12;
  undefined4 uVar13;
  int* piVar14;
  int iVar15;
  undefined4 local_c0[3];
  int local_b4;
  undefined auStack_ac[96];
  undefined4 local_4c;
  undefined4 local_48[2];
  int local_40;
  int local_3c;
  undefined4 local_38;
  int* piStack_34;
  int iStack_30;
  int iStack_2c;
  int local_28;

  local_4c = 0;
  local_48[0] = 0;
  local_40 = 0;
  local_3c = 0;
  isBusy = 1;
  uVar8 = 0;
  uVar10 = 0;
  interTime = interTime + param_2;
  iVar2 = interCnt + 1;
  if (param_2 > 60) {
    iVar2 = interCnt;
  }
  interCnt = iVar2;
  piStack_34 = param_1;
  iStack_30 = param_2;
  iStack_2c = param_3;
  local_28 = param_4;
  if (interTime > 970) {
    uVar8 = 25 - interCnt;
    if ((int)uVar8 < 0) {
      uVar8 = 0;
    }
    interCnt = 0;
    interTime = 0;
    if ((int)uVar8 < 18) {
      if ((int)uVar8 < 1)
        goto label_6;
    } else {
      uVar8 = 18;
    }
    uVar10 = 1;
  }
  do {
    if (uVar8 == 0 || uVar10 == 0) {
      preRawdata._12_4_ = param_1[3];
      preRawdata._16_4_ = param_1[4];
      preRawdata._0_4_ = *param_1;
      preRawdata._4_4_ = param_1[1];
      preRawdata._8_4_ = param_1[2];
      if (param_3 == 18) {
        preRawdata._0_4_ = preRawdata._0_4_ << 2;
        preRawdata._4_4_ = preRawdata._4_4_ << 2;
        preRawdata._8_4_ = preRawdata._8_4_ << 2;
      }
    }
    if ((uVar8 == 0) && (uVar10 != 0)) {
      preRawdata._12_4_ = preRawdata._12_4_ + 4096;
    }
    modle5_2(preRawdata, local_c0, &local_3c);
    iVar2 = local_b4;
    if (preAccnorm == 0) {
      preAccnorm = local_3c;
    }
    iVar9 = local_3c - preAccnorm;
    bus = iVar9 * 16;
    if (bus < 0) {
      bus = iVar9 * -16;
    }
    prePPGUC = prePPGUC + ((bus - prePPGUC) * 261 + 16384 >> 15);
    if (prePPGUC > 255) {
      prePPGUC = 256;
    }
    if (prePPGUC < -255) {
      prePPGUC = -256;
    }
    if (iVar9 < 0) {
      iVar9 = -iVar9;
    }
    preAccnorm = local_3c;
    if (accGapMax <= iVar9) {
      accGapMax = iVar9;
    }
    if (param_3 == 1) {
      param_3 = 2;
    label_1:
      if (prePPGAfterFilter == 0) {
        prePPGAfterFilter = local_b4;
      }
      iVar3 = prePPGAfterFilter - local_b4;
      if (iVar3 < 0) {
        iVar3 = -iVar3;
      }
      if (iVar3 > 4799) {
        ppgPower25Hz = local_b4;
        local_b4 = prePPGAfterFilter;
      }
      prePPGAfterFilter = iVar2;
    } else if (param_3 == 2)
      goto label_1;
    uVar1 = modle5_11(local_b4);
    PPGpowerReal = modle5_10(local_b4);
    modle5_17(local_c0, param_3, iVar9);
    iVar2 = modle5_1();
    iVar9 = modle5_9(preRawdata);
    iVar3 = modle5_18(preRawdata, local_c0[0]);
    local_38 = modle5_7();
    iVar4 = modle5_3(iVar3);
    bigbus = iVar4;
    modle5_13(iVar2 << 4, RLCGroup, auStack_ac);
    oneSecondCnt = oneSecondCnt + '\x01';
    if (prePPGUC < 96) {
      absoluteStillCnt = absoluteStillCnt + 1;
    } else {
      absoluteStillCnt = 0;
    }
    if (local_28 == 0) {
      absoluteStillCnt = 0;
    } else if (199 < absoluteStillCnt) {
      absoluteStillCnt = 200;
    }
    if ('\x18' < oneSecondCnt) {
      oneSecondCnt = '\0';
      rlcSwitchCnt = rlcSwitchCnt + 1;
      modle5_15(auStack_ac, lockOn, firstLock, (undefined4)enhanceRange,
                enhanceRange._4_4_, (undefined4)dampingRange,
                dampingRange._4_4_);
      preRawheartRate = heartRate;
      preRawReliability = reliability;
      modle5_14(auStack_ac, &local_4c, &heartRate, local_48);
      reliability =
          modle5_8(auStack_ac, local_48[0], local_4c, iVar9, &PPGpowerFromSpec);
      if ((PPGpowerReal < 0) && (firstLock == '\0')) {
        reliability = 0;
      }
      uVar7 = (uint)(absoluteStillCnt == 200);
      if (runningAlltime < 30) {
        runningAlltime = runningAlltime + 1;
      }
      iVar5 = preRawReliability - reliability;
      iVar2 = iVar5;
      if (iVar5 < 0) {
        iVar2 = -iVar5;
      }
      if ((iVar2 * 100 + 128 >> 8 < 5) &&
          (79 < (reliability * 100 + 128) >> 8)) {
        signalSafeGuardCnt = signalSafeGuardCnt + 1;
        if (4 < signalSafeGuardCnt) {
          absoluteStillCnt = 0;
          uVar7 = 0;
          signalSafeGuardCnt = 5;
        }
      } else {
        signalSafeGuardCnt = 0;
      }
      if (((firstLock == '\0') && (15 < iVar5 * 100 + 128 >> 8)) &&
          (runningAlltime == 30)) {
        ProtectTic = 5;
        if ((param_3 == 0) && (local_40 == 1)) {
          ProtectTic = 300;
        }
      label_2:
        ProtectTic = ProtectTic + -1;
      } else if (ProtectTic != 0)
        goto label_2;
      iVar2 = preRawheartRate - heartRate;
      if (param_3 == 0) {
        if (ProtectTic == 0) {
          if (iVar2 + 12800 < 0 != SCARRY4(iVar2, 12800)) {
            bVar12 = SBORROW4(heartRate, 35840);
            iVar5 = heartRate + -35840;
            bVar11 = heartRate == 35840;
            if (heartRate > 35840)
              goto label_3;
          }
        } else if (iVar2 < 1) {
          if (preRawheartRate < 23041) {
            if (iVar2 + 2560 < 1280) {
              heartRate = preRawheartRate - iVar2 / 2;
            }
            if (iVar2 + 3840 < 1280) {
              heartRate = preRawheartRate + 512;
            }
            if (iVar2 + 3840 < 0 != SCARRY4(iVar2, 3840)) {
              heartRate = preRawheartRate;
            }
          }
          if (preRawheartRate - 23040U < 5121) {
            if (iVar2 + 2560U < 1280) {
              heartRate = preRawheartRate + 256;
            }
            if (iVar2 + 2560 < 0 != SCARRY4(iVar2, 2560)) {
              heartRate = preRawheartRate;
            }
          }
          if (28160 < preRawheartRate) {
            if (iVar2 + 3840U < 1280) {
              heartRate = preRawheartRate + 512;
            }
            if (iVar2 + 3840 < 0 != SCARRY4(iVar2, 3840))
              goto label_4;
          }
        } else if (preRawheartRate < 28161) {
          if (iVar2 - 2561U < 1280) {
            heartRate = preRawheartRate + -1280;
          }
          bVar12 = SBORROW4(iVar2, 3840);
          iVar5 = iVar2 + -3840;
          bVar11 = iVar2 == 3840;
        label_3:
          if (!bVar11 && iVar5 < 0 == bVar12)
            goto label_4;
        }
      } else if (ProtectTic != 0) {
        if ((iVar2 + 7680 < 0 != SCARRY4(iVar2, 7680)) &&
            (heartRate > 40960)) {
          heartRate = preRawheartRate;
        }
        if ((iVar2 > 5120) && (heartRate < 16640)) {
        label_4:
          heartRate = preRawheartRate;
        }
      }
      piVar14 = &local_40;
      iVar3 = iVar3 >> 4;
      if (iVar9 != 0) {
        iVar9 = 1;
      }
      uVar13 = local_38;
      iVar2 = iVar4;
      iVar5 = preHR;
      iVar15 = param_3;
      modle5_5(&HRlast);
      if (0 < badSignalCnt) {
        badSignalCnt = badSignalCnt + -1;
      }
      iVar6 = HRlast;
      if (accGapMax < 60000) {
        if ((0 < badSignalCnt) || ((ProtectTic != 0 && (param_3 == 0))))
          goto label_5;
      } else {
        badSignalCnt = 30;
      label_5:
        if (HRpatch != 0) {
          if (HRlast - HRpatch < 1280) {
            iVar6 = HRpatch + ((HRlast - HRpatch) * 405 >> 13);
          } else {
            iVar6 = HRpatch + 63;
          }
        }
      }
      HRpatch = iVar6;
      if ((iVar6 - preHR) + 256U < 513) {
        HRpatch = preHR;
      }
      if ((iVar4 < 81) && (37119 < HRlast)) {
        if (abnormalHighHRCnt < 30) {
          abnormalHighHRCnt = abnormalHighHRCnt + 1;
        }
      } else {
        abnormalHighHRCnt = 0;
      }
      if (firstLock != '\0') {
        param_3 = 1;
      }
      if (abnormalHighHRCnt == 30) {
        HRlast = HRlast >> 1;
        param_3 = 0;
      }
      preHR = HRpatch;
      if (rlcSwitchCnt == 1) {
        modle5_4(HRlast + 128, firstLock, iVar4, RLCGroup, local_40, param_3,
                  1, uVar13, iVar9, iVar3, uVar1, iVar2, iVar5, piVar14, iVar15,
                  uVar7);
        rlcSwitchCnt = 0;
      }
      if (firstLock != '\0') {
        iVar2 = 0;
        do {
          RLCGroup[iVar2] = 1;
          iVar2 = iVar2 + 1;
        } while (iVar2 < 12);
      }
      accGapMax = 0;
    }
    uVar8 = uVar8 - 1;
  label_6:
    if ((int)uVar8 < 0) {
      isBusy = 0;
      return;
    }
  } while (true);
}

Source: Ghidra (Dogbolt) + http://format.krzaq.cc + unhexing by me

Explanation by GPT-4

(This function is quite complex without a concrete context and uses many variables and functions that are not defined within its scope. I will explain as much as possible based on the variables and logic provided within the function itself.)

The input to the function Algo_Input is four parameters.

This function appears to be part of a heart rate monitoring system, with some algorithm calculating beats per minute (BPM) from raw input data.

The function first does some initial calculation and preparation using param_2 and param_3. These likely represent some raw data inputs. These values are used to calculate an interTime and interCnt, which might represent some type of intermediate calculation.

A major part of the function seems to be a loop, probably processing a batch of new heart rate data. The function seems to be in readiness to calculate bpm even if the interTime value is greater than a threshold (970), which is probably some time duration.

Within this loop, the function performs preprocessing operations on raw input, via operations like shifting or adding depending on param_3. Then various methods are called that handle the data further, likely filtering and normalising it. These are likely correlated to heart rate signal processing methods which are commonly used in heart rate sensor algorithms. Variables like preAccnorm, bus, prePPGUC, etc are used, to indicate these might be values linked to accelerometer data and PPG (photoplethysmography) sensor data.

There is seemingly an accumulative process where the cumulative sum of absoluteStillCnt is computed whenever param_3 is nonzero, which may indicate the device being still. This implies the algorithm recognizes user's motion state based on the acceleration norm value provided in param_1 and modifies its behavior accordingly.

There are also several operations checking and adjusting the heart rate value (heartRate) if certain conditions apply. The measures of reliability are considered and safety checks on the signal are in place (signalSafeGuardCnt), ensuring the data's integrity isn't compromised.

Towards the end of the function, adjustments are made to the final heart rate (HR) output according to certain conditions (HRpatch, HRlast), for example, abnormal HR is detected and adjusted by halving the HRlast.

Overall, this code seems to be a mix of processes related to signal processing, safety checks, and adjustments based on real-time information and measurements, adapting accordingly.

Disclaimer: This is a qualified guess based on the provided code. Some parts may not be accurate due to a lack of specific details and context.