javascriptreactjslightningchart

Lightningchart js, use different sampling rate in Medical Dashboard


I have ECG, Pulse rate, Respiratory Rate and NIBP to show in LightningChart JS Medical Dashboard which used a global sampling rate which is common to all channels [ecg,pulseRate,respRate,nibp]. But the sampling rate would change for each channel. ECG and Pulse Rate will have 256, and Respiratory rate will have 128.

I tried below but didn't work, as it now becomes 128 samples/second to all the channels.

Code:

const SAMPLE_RATE = 256;
const SAMPLE_RATE_NEW = 128;

Below is the function i used with my changes.

    let tSamplePos = window.performance.now();
    let tSamplePosNew = window.performance.now();
    let iSampleX = 0;

    const addData = () => {
      const tNow = window.performance.now();
      const seriesNewPoints = seriesList.map((_) => []);
      while (tNow > tSamplePos) {
        const x = tSamplePos;
        const xNew = tSamplePosNew;
        for (let i = 0; i < seriesList.length; i += 1) {
          const channel = channels[i];
          const dataSet = channel.dataSet;
          const sample = dataSet[iSampleX % dataSet.length];
           if (i !== 2) {
             // @ts-ignore
             seriesNewPoints[i].push({ x, y: sample });
           } else {
             // @ts-ignore
             seriesNewPoints[i].push({ xNew, y: sample });
           }
          // // @ts-ignore
          //seriesNewPoints[i].push({ x, y: sample });
          if (channel.name === "Electrocardiogram") {
            updateBpm(sample);
          }
        }
        tSamplePos += 1000 / SAMPLE_RATE;
        tSamplePosNew += 1000 / SAMPLE_RATE_NEW;

        iSampleX += 1;
      }
      seriesList.forEach((series, i) => series.add(seriesNewPoints[i]));
      channelIncomingDataPointsCount += seriesNewPoints[0].length;
      requestAnimationFrame(addData);

Setting the sampling rate:

    let channelIncomingDataPointsCount = 0;
let channelIncomingDataPointsLastUpdate = window.performance.now();
setInterval(() => {
  const tNow = window.performance.now();
  const chDataPointsPerSecond = Math.round(
    (channelIncomingDataPointsCount * 1000) /
      (tNow - channelIncomingDataPointsLastUpdate)
  );
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  const bpm = (beatsCount * 60 * 1000) / (tNow - tStart);

  uiList.forEach((ui, i) => {
    ui.labelSampleRate.setText(`${chDataPointsPerSecond} samples / second`);
  });
  channelIncomingDataPointsCount = 0;
  channelIncomingDataPointsLastUpdate = tNow;
}, 2000);

How do i get different sampling rates in different channel? I am using this medical dashboard Online Medical Dashboard

Entire code is above.

I have tried multiple different solution but none of them worked. Need guidance on how to do it.

I tried using different sampling rate and push the data inside array, but i am not getting different sampling rates. Why I want this? The charts formed are different in different devices (laptop/desktop).


Solution

  • Since you are asking how to get different sample rates, I assume your data source doesn't include timestamps (X). Here's one way of achieving this:

    The key here is preconfigured stream rate for each channel (128/256), which is supplied to PointLineAreaSet.appendSamples method as step parameter. Only incoming yValues are specified, leaving X coordinates filled in automatically.

    As long as the incoming data matches the configuration (e.g. ECG indeed receives 2x as many Y values as "NIPB"), then this works wonderfully.

    Zoom in to confirm that ECG & Pulse rate channels indeed have more data points than the other channels.

    const {
        AxisScrollStrategies,
        AxisTickStrategies,
        emptyFill,
        emptyLine,
        lightningChart,
        Themes,
    } = lcjs
    
    const chart = lightningChart().ChartXY({ theme: Themes.light, defaultAxisX: { type: 'linear-highPrecision' } })
        .setTitle('')
        .setCursorMode('show-all-interpolated')
    chart.axisX
        .setTickStrategy(AxisTickStrategies.Time)
        .setScrollStrategy(AxisScrollStrategies.progressive)
        .setDefaultInterval((state) => ({
            end: state.dataMax ?? 0,
            start: (state.dataMax ?? 0) - 10_000,
            stopAxisAfter: false,
        }))
    chart.axisY.dispose()
    
    const channels = [
        { name: 'ECG', rate: 256 },
        { name: 'Pulse rate', rate: 256 },
        { name: 'Resp rate', rate: 128 },
        { name: 'NIBP', rate: 128 },
    ].map((info, i) => {
        const axisY = chart.addAxisY({iStack: i})
            .setTitle(info.name)
            .setTitleRotation(0)
            .setChartInteractionZoomByWheel(false)
        const series = chart.addPointLineAreaSeries({ dataPattern: 'ProgressiveX', axisY })
            .setAreaFillStyle(emptyFill)
            .setStrokeStyle(stroke => stroke.setThickness(1))
            .setMaxSampleCount(100_000)
        return { ...info, axisY, series }
    })
    
    // Incoming data Y's only. X's automatically indexed according to known channel sample rate
    const handleIncomingSamples = (allYs) => {
        channels.forEach((ch, i) => {
            const ys = allYs[i]
            ch.series.appendSamples({ yValues: ys, step: 1000 / ch.rate })
        })
    }
    
    // NOTE: This demo code WILL run out of sync (time axis will progress slower/faster than actual time)
    // This is because `setInterval` may happen more or less frequently than requested.
    setInterval(() => {
        const allYs = channels.map((ch) => {
            const sampleCount = ch.rate / 128
            return new Array(sampleCount).fill(0).map(_ => performance.now() % 1000 + 100 * Math.random())
        })
        handleIncomingSamples(allYs)
    }, 1000/128)
    <script src="https://cdn.jsdelivr.net/npm/@lightningchart/lcjs@6.0.3/dist/lcjs.iife.js"></script>