stuartlynne / fitness_hrv

fitness_hrv release repository.
11 stars 0 forks source link

Are there plans to add millisecond timestamps to the ecg waveform acquisition? This would be of great value in achieving data synchronisation for multi-sensor data. #2

Open fixernhc opened 1 year ago

stuartlynne commented 1 year ago

Unfortunately, the Polar H10 just sends the data through two BLE channels with no associated timestamps and with no apparent correlation:

Over time, the sum of the RR intervals should accumulate and be reasonably correct WRT to overall elapsed time.

And we should see 130 samples of ECG data that again is reasonably accurate compared to the overall elapsed time.

But we are not given any reference between the two channels or an accurate time base.

The next release of fitnesshrv will capture ECG and save it (and everything else captured) into FIT files. Just finishing that now.

On Tue, Nov 22, 2022 at 6:32 AM awesome @.***> wrote:

— Reply to this email directly, view it on GitHub https://github.com/stuartlynne/fitness_hrv/issues/2, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACIUWKSN2NHX7EHUIJUZD3WJTKP7ANCNFSM6AAAAAASH4PYRI . You are receiving this because you are subscribed to this thread.Message ID: @.***>

-- __O____ -\<,____ ____()/()___


@.***>__604-518-1749(m)604-461-7532(h)

MedTechCD commented 1 year ago

Stuart, I don't know if or how you will implement breathing frequency but I'm also closely following the work of Inigo Tolosa on the Garmin IQ app AlphaHRV. They have breathing frequency derived from RR intervals. Underneath is a screenshot from my findings using Fitness-ECG alongside the Garmin IQ app. As you say above, the 130hz sampling rate for the Polar is quite accurate and when I plot the ECG in Excel with a time base based on that sampling rate I see something interesting. The screenshot has the ECG from the first and last 60sec of a AeT ramp test. A side-effect of chest strap ECG seems to be that the QRS amplitude changes with chest expansion/breathing. How feasible would it be to implement that? image

stuartlynne commented 1 year ago

Respiratory rate is definitely on my (long) list of things to do!

Just finishing FIT file creation and making sure I can get uVolts added to that in a sensible way.

Then need to move the ECG display into fitnesshrv, and then I can look at things like respiratory rate etc.

On Wed, Dec 14, 2022 at 6:52 AM MedTechCD @.***> wrote:

Stuart, I don't know if or how you will implement breathing frequency but I'm also closely following the work of Inigo Tolosa on the Garmin IQ app AlphaHRV. They have breathing frequency derived from RR intervals. Underneath is a screenshot from my findings using Fitness-ECG alongside the Garmin IQ app. As you say above, the 130hz sampling rate for the Polar is quite accurate and when I plot the ECG in Excel with a time base based on that sampling rate I see something interesting. The screenshot has the ECG from the first and last 60sec of a AeT ramp test. A side-effect of chest strap ECG seems to be that the QRS amplitude changes with chest expansion/breathing. How feasible would it be to implement that? [image: image] https://user-images.githubusercontent.com/85841362/207628385-8fb27861-9eb0-4a0f-bce5-c69352c510ee.png

— Reply to this email directly, view it on GitHub https://github.com/stuartlynne/fitness_hrv/issues/2#issuecomment-1351556731, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACIUWIJRHFBI7JNGZNLTFLWNHNJTANCNFSM6AAAAAASH4PYRI . You are receiving this because you commented.Message ID: @.***>

-- __O____ -\<,____ ____()/()___


@.***>__604-518-1749(m)604-461-7532(h)

MedTechCD commented 1 year ago

Extracting RespRate from the ECG is surprisingly easy. Here's an example with on top 20sec ECG recorded with your app. At the bottom 1 min with the extracted R-peaks (orange) and in green the chest movement = respiratory trace. image

Some simple Excel manipulations.

stuartlynne commented 1 year ago

Unfortunately Polar does not give us any timestamp information about the ECG data. It simply arrives in blocks of about 73 samples via Bluetooth.

From my perspective, a block of data (usually 73*3 bytes) simply arrives, at a relatively constant interval.

We have no way of knowing the exact time the data stream started. And there is lag due to buffering in the sensor (at least 73/130 seconds), and the time it takes for it to prepare the transfer and for it to be transferred.

As best as I can tell once it starts the data stream is complete. So the analysis of it should be OK, e.g. deriving respiration rate, heart rate, or RR values.

But we only know the offset to wall clock time to about 1-2 seconds accuracy for any point, although that offset should not change over the life of the datastream (although it may not be guaranteed.)

I think it will be possible to map some other data from the same sensor, for example, RR times and Artifact locations, to be able to look at the ECG for specific issues (what might have caused an Artifact.)

On Wed, Jan 25, 2023 at 7:50 AM MedTechCD @.***> wrote:

Extracting RespRate from the ECG is surprisingly easy. Here's an example with on top 20sec ECG recorded with your app. At the bottom 1 min with the extracted R-peaks (orange) and in green the chest movement = respiratory trace. [image: image] https://user-images.githubusercontent.com/85841362/214609574-94826434-a6aa-4953-8f97-a4c41a6f315a.png

Some simple Excel manipulations.

— Reply to this email directly, view it on GitHub https://github.com/stuartlynne/fitness_hrv/issues/2#issuecomment-1403830564, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACIUWIVGZDM7IUBRTYGUVDWUFDVNANCNFSM6AAAAAASH4PYRI . You are receiving this because you commented.Message ID: @.***>

-- __O____ -\<,____ ____()/()___


@.***>__604-518-1749(m)604-461-7532(h)

stuartlynne commented 1 year ago

I'm just finishing adding BLE support to my main fitnesshrv app, and saving data to FIT files.

Once I have that out one of the near term goals is to pull ECG into the app and look at deriving respiration rate from either (or both) RR data or ECG data.

On Wed, Jan 25, 2023 at 7:50 AM MedTechCD @.***> wrote:

Extracting RespRate from the ECG is surprisingly easy. Here's an example with on top 20sec ECG recorded with your app. At the bottom 1 min with the extracted R-peaks (orange) and in green the chest movement = respiratory trace. [image: image] https://user-images.githubusercontent.com/85841362/214609574-94826434-a6aa-4953-8f97-a4c41a6f315a.png

Some simple Excel manipulations.

— Reply to this email directly, view it on GitHub https://github.com/stuartlynne/fitness_hrv/issues/2#issuecomment-1403830564, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACIUWIVGZDM7IUBRTYGUVDWUFDVNANCNFSM6AAAAAASH4PYRI . You are receiving this because you commented.Message ID: @.***>

-- __O____ -\<,____ ____()/()___


@.***>__604-518-1749(m)604-461-7532(h)

MedTechCD commented 1 year ago

map some other data from the same sensor, for example, RR times

I've used the HR stream to sync data from multiple devices with resonable accuracy. And for resprate, a few seconds will not make a difference. But I understand the problems you're facing. Some thoughts: An artefact like a premature beat should show in both ECG and RR time. That would be pretty accurate but can't be invoked... I'm thinking of trying to hook up an ecg simulator and during recording, at specific time points, shortcircuit the signal. If that turns out to be consistent in timing difference, it could be part of a solution. But still, nothing guarantees that all H10 sensors have the same lag.

stuartlynne commented 1 year ago

Yes. Currently, I'm doing artifact detection and correction using the Lipponen/Tarvainen paper using the RR data.

Jukka A. Lipponen & Mika P. Tarvainen (2019) A robust algorithm for

heart

rate variability time series artefact correction using novel beat

classification, Journal of Medical

Engineering & Technology, 43:3, 173-181, DOI:

10.1080/03091902.2019.1640306

If getting the RR data via BLE you have essentially the same problem. It arrives, and hopefully is self-consistent over time, but no anchoring timestamp.

Via ANT+ we get a slightly better idea of when data is arriving because we are lower down in the stack, closer to the USB hardware and get a small message every 250mS that we can timestamp on arrival. Still not perfect, but better.

Effectively though, I suspect we should be able to show where the artifacts are on an ECG strip in a manner that puts them close to the ECG data for that interval. Probably +/- a few hundred mS.

This area is getting more interesting. I've just ordered a Movesense sensor. They sample at a higher rate (500hz I believe). I haven't dug into how they are delivering the RR and ECG data via BLE yet. But they appear to be open (and open as here is the sensor code!).

And there are rumours of Garmin announcing ECG support real soon now.

My email is at the bottom if you want to keep in touch.

On Wed, Jan 25, 2023 at 1:46 PM MedTechCD @.***> wrote:

map some other data from the same sensor, for example, RR times I've used the HR stream to sync data from multiple devices with resonable accuracy. And for resprate, a few seconds will not make a difference. But I understand the problems you're facing. Some thoughts: An artefact like a premature beat should show in both ECG and RR time. That would be pretty accurate but can't be invoked... I'm thinking of trying to hook up an ecg simulator and during recording, at specific time points, shortcircuit the signal. If that turns out to be consistent in timing difference, it could be part of a solution. But still, nothing guarantees that all H10 sensors have the same lag.

— Reply to this email directly, view it on GitHub https://github.com/stuartlynne/fitness_hrv/issues/2#issuecomment-1404267663, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACIUWNBDHJEWVFQLUYCT7TWUGNLFANCNFSM6AAAAAASH4PYRI . You are receiving this because you commented.Message ID: @.***>

-- __O____ -\<,____ ____()/()___


@.***>__604-518-1749(m)604-461-7532(h)

MedTechCD commented 1 year ago

Garmin announcing ECG support real soon now

It's released for the Venu 2 Plus in the US https://www.dcrainmaker.com/2023/01/garmin-ecg-app-devices-fully-explained.html

We do have RR from the same H10 via the ANT+ channel in Dashboard, don't we? To get as close as possible now (close enough for what I'm doing), I start both apps and if the ECG shows a stable trace, I hit the Start Record button when dasboard is exactly at 4 or 5 min. That gives me an ECG file with a creation time 4 or 5 min later then the dashboard files. I hit the button again at an exact minute when stopping. For a recording of 80-90 min, the number of samples in the ECG is very close (within 200-250 samples) to the number of seconds * 130. This off course does not take into account that the data coming from the sensor may be delayed.

stuartlynne commented 1 year ago

Thanks for the pointer! Hopefully, they will add to the HRM strap like Polar H10. Not holding my breath!

On Wed, Jan 25, 2023 at 2:16 PM MedTechCD @.***> wrote:

Garmin announcing ECG support real soon now

It's released for the Venu 2 Plus in the US

https://www.dcrainmaker.com/2023/01/garmin-ecg-app-devices-fully-explained.html

We do have RR from the same H10 via the ANT+ channel in Dashboard, don't we?

Yes, and once I get a bit more testing done the new FitnessHRV Dashboard will have BLE support for Hrm and if Polar H10 will allow capturing ECG as well.

The ECG data will be accessible from a CSV file (hmm, have to check that is the same as the ecg app....)

Also, the ECG data is available from the FIT file. Although I suspect it may take some time for other developers to add support for looking at it. :-)

I will provide some Python scripts for pulling apart the fit files. I am taking advantage of some FIT file techniques that allow saving data from multiple sensors of the same type.

That allows (for example) having two (or more) HRM straps and capturing the heartrate, RR and ECG data from each for comparison. And capturing both as ANT+ and BLE (so four sets of data.)

I am saving the uVolt data in the Record Message at the point received, each of those is timestamped (resolution of one second.)

One minor (!) issue with the current FIT SDK implementation for saving RR data is to put it into a separate stream of HRV Messages and they DO NOT have a timestamp. Again, hopefully, internally consistent, as in adding all RR intervals up will give you the correct elapsed time. But again, you don't have a lot of confidence in where in time the streams start.

Since I am putting the uVolts into a separate Record Message that is specific to each sensor (and that won't interfere with the normal Record Message used by Strava, WKO, TrainingPeaks etc), I may add the RR data available to that Record Message as well. At that point we would at least have all the data received from the sensor at that point, in a timestamped message.

To get as close as possible now (close enough for what I'm doing), I start

both apps and if the ECG shows a stable trace, I hit the Start Record button when dasboard is exactly at 4 or 5 min. That gives me an ECG file with a creation time 4 or 5 min later then the dashboard files. I hit the button again at an exact minute when stopping. For a recording of 80-90 min, the number of samples in the ECG is very close (within 200-250 samples) to the number of minutes * 130. This off course does not take into account that the data coming from the sensor may be delayed.

Yes, the data from the other sensor is delayed. For ANT+ probably on the order of about 1-2 seconds. BLE may be higher and more variable.

— Reply to this email directly, view it on GitHub https://github.com/stuartlynne/fitness_hrv/issues/2#issuecomment-1404300884, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACIUWNOSTZRQIWUCVBOUL3WUGQ5TANCNFSM6AAAAAASH4PYRI . You are receiving this because you commented.Message ID: @.***>

-- __O____ -\<,____ ____()/()___


@.***>__604-518-1749(m)604-461-7532(h)

MedTechCD commented 1 year ago

separate stream of HRV Messages and they DO NOT have a timestamp

The way RR data is saved is pretty good. From the start of the recording, you look for the first R-peak. If it falls before one second has elapsed, the exact ms is stored with the first second by second data point. If not, it goes to the next data point. The following R-peaks are stored with a reference to the former R-peak in ms. If more then one R-peak occurs within the same second, all ms values are stored in that datapoint. The timeline for the R peaks can be reconstructed from this by adding up all inter-peak intervals. As an example, A, B, C are the usual metrics measured every second (HR, Power, Cadence, ...) and then comes the RR values in this simple representation: 1 A B C 800 2 A B C 820 3 A B C 652 4 A B C 480;430 5 A B C 330;295,280

And so on. Every R peak can be exactly positioned by adding up all inter-beat values.

MedTechCD commented 1 year ago

For timing consistency, I did the following test: Installed 2 little wires on the clips of the H10 sensor leading to a pushbutton to shortcircuit. With the desktop clock displayed, started the ECG recording at 16:26:00. After 30 sec and then 7 consecutive times with a 10 sec interval, pushed the button to shortcircuit the strap. Stopped the recording after another 30 sec, for a total recording time of 2 minutes. File creation time = 16:26:00, modified time = 16:28:00. Plotted in Excel: image

I think you're worrying to much about timing. From what I see, the ECG is recorded with negligible time errors. The display on the other hand is lagging somewhere in between 3 and 3.5 seconds. That's probably caused by extra filtering and video lag.

stuartlynne commented 1 year ago

Yes, I agree that the ECG is consistent. I.e. we can rely on the overall elapsed time to be correct.

We just don't have the exact offset to match to the other data (heart rate, RR etc.) And we have a similar issue with RR. And RR values should again be consistent.

And that is the source of the lag in the display.

And I like your hardware hack!

On Thu, Jan 26, 2023 at 7:46 AM MedTechCD @.***> wrote:

For timing consistency, I did the following test: Installed 2 little wires on the clips of the H10 sensor leading to a pushbutton to shortcircuit. With the desktop clock displayed, started the ECG recording at 16:26:00. After 30 sec and then 7 consecutive times with a 10 sec interval, pushed the button to shortcircuit the strap. Stopped the recording after another 30 sec, for a total recording time of 2 minutes. File creation time = 16:26:00, modified time = 16:28:00. Plotted in Excel: [image: image] https://user-images.githubusercontent.com/85841362/214880747-c83dced9-d2e9-4925-8314-d91afb0d2524.png

I think you're worrying to much about timing. From what I see, the ECG is recorded with negligible time errors. The display on the other hand is lagging somewhere in between 3 and 3.5 seconds. That's probably caused by extra filtering and video lag.

— Reply to this email directly, view it on GitHub https://github.com/stuartlynne/fitness_hrv/issues/2#issuecomment-1405209016, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACIUWPS7ELXFTWFAKGHP6LWUKL65ANCNFSM6AAAAAASH4PYRI . You are receiving this because you commented.Message ID: @.***>

-- __O____ -\<,____ ____()/()___


@.***>__604-518-1749(m)604-461-7532(h)

MedTechCD commented 1 year ago

We just don't have the exact offset to match to the other data

The ECG file creation time gives you a date-time stamp (which is already used in the filename). Every 130 samples make up a second. Then it is just a question to reference that to the date-time stamp of the regular datastream or am I overlooking something? One thing I'm absolutely missing in the actual dashboard app, is a Start button. You have to wait for the 2min data collection window for a1, but then you have no way to control when the Ra calculation starts because it starts immediately... I'm usualy doing other prep stuff (Power meter calibration, trainer control app setup, ...) while dashboard is already running. I would like a start button to control when data logging starts, and that button could start ECG collection at the same time.

stuartlynne commented 1 year ago

Yes, start/stop on the list. Also if you are using Zwift and have Sauce for Zwift installed, I connect to that to get world, location, speed, lap information etc. Generally switching worlds starts a new FIT file.

Once I have FIT file generation complete it will also make sense to be able to reload a FIT file to look at. So having a dialogue box to control start/stop or open file makes sense.

Do you want to play with an early beta of the new release? I don't have ECG display yet, but the capture is working, and the data is saved into the FIT file saved.

Not to get too much into the weeds on how FIT files are formatted, but effectively the two interesting messages are:

In my implementation, I am generating additional Record Messages for each sensor, and in the case of the H10 that will include the NN (RR data corrected) and the uVolts.

I'll have python scripts available for pulling apart the FIT file to get the raw data. In the case of NN or uVolts you'll have one seconds worth of data for each one-second resolution timestamp. And easily extracted into (for example) CSV format.

And of course, the original RR data is available. The placement of the RR data within the fit file is not guaranteed in the FIT specification (applications generating the data can place the data in any order WRT to the other messages).

In my implementation, the RR data is written intermixed with the Record Messages, so there will be reasonable correspondence to the preceding and following Record Messages with their timestamp.

On Fri, Jan 27, 2023 at 4:45 AM MedTechCD @.***> wrote:

We just don't have the exact offset to match to the other data

The ECG file creation time gives you a date-time stamp (which is already used in the filename). Every 130 samples make up a second. Then it is just a question to reference that to the date-time stamp of the regular datastream or am I overlooking something? One thing I'm absolutely missing in the actual dashboard app, is a Start button. You have to wait for the 2min data collection window for a1, but then you have no way to control when the Ra calculation starts because it starts immediately... I'm usualy doing other prep stuff (Power meter calibration, trainer control app setup, ...) while dashboard is already running. I would like a start button to control when data logging starts, and that button could start ECG collection at the same time.

— Reply to this email directly, view it on GitHub https://github.com/stuartlynne/fitness_hrv/issues/2#issuecomment-1406463331, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACIUWLL5OYXAFP4ODNVATLWUO7NNANCNFSM6AAAAAASH4PYRI . You are receiving this because you commented.Message ID: @.***>

-- __O____ -\<,____ ____()/()___


@.***>__604-518-1749(m)604-461-7532(h)

MedTechCD commented 1 year ago

Do you want to play with an early beta of the new release?

I would be glad to give you my opinion and eventual remarks ;-)

stuartlynne commented 1 year ago

I dub into the Polar SDK a bit more. It appears that Polar is sending us a timestamp.

Effectively I am getting a BLE message containing a microsecond timestamp and 73 data points every 560 mS.

I can put this into the saved CSV file.

Do you want every sample in the CSV file to have a timestamp? I.e. the recorded timestamp for the first sample and interpolated for the next 72?

Or just put the timestamp in once every 73 samples?

Also, on reviewing the Polar H10 data and comparing it to Movesense, I think the Polar is giving us millivolt samples, not microvolts.

The documentation in the Polar SDK says uV. But what I'm seeing looks like mV. I have to dig back into my ECG app further, but I don't see any conversion from the samples to display in mV.

On Sun, Jan 29, 2023 at 5:37 AM MedTechCD @.***> wrote:

Do you want to play with an early beta of the new release?

I would be glad to give you my opinion and eventual remarks ;-)

— Reply to this email directly, view it on GitHub https://github.com/stuartlynne/fitness_hrv/issues/2#issuecomment-1407666040, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACIUWO7VCAHWADGUAJQ7JLWUZXCLANCNFSM6AAAAAASH4PYRI . You are receiving this because you commented.Message ID: @.***>

-- __O____ -\<,____ ____()/()___


@.***>__604-518-1749(m)604-461-7532(h)

MedTechCD commented 1 year ago

Also, on reviewing the Polar H10 data and comparing it to Movesense, I think the Polar is giving us millivolt samples, not microvolts.

The values in the recorded csv file from Fitness ECG app are definitely µV. Common p2p amplitudes for ECG are around 1-2,5mV. The csv, for my recordings, has max values around 2500. That can only be µV. But I don't know if they are already manipulated by your app.

Effectively I am getting a BLE message containing a microsecond timestamp and 73 data points every 560 mS.

Guess you mean a millisecond timestamp? That would be slightly higher then 130Hz resulting in 1286 samples extra per hour compared to the 130 Hz I used now. Or about 10 sec. Not entirely convinced here...

Or just put the timestamp in once every 73 samples?

I think just putting in what you actually get from the Polar SDK is the safest thing to do. Interpolating isn't difficult to do afterwards.

MedTechCD commented 1 year ago

This could be valuable information to understand the H10 Timestamps!

https://github.com/polarofficial/polar-ble-sdk/blob/master/technical_documentation/TimeSystemExplained.md

stuartlynne commented 1 year ago

On Tue, Feb 7, 2023 at 3:39 AM MedTechCD @.***> wrote:

Also, on reviewing the Polar H10 data and comparing it to Movesense, I think the Polar is giving us millivolt samples, not microvolts.

The values in the recorded csv file from Fitness ECG app are definitely µV. Common p2p amplitudes for ECG are around 1-2,5mV. The csv, for my recordings, has max values around 2500. That can only be µV. But I don't know if they are already manipulated by your app.

Let me get collect some raw data and send it to you. Not being an ECG Domain expert I can get lost in the weeds sometimes.

There is definitely a difference in nomenclature between Polar and Movesense. Polar specifically says uV and sends data as 3-byte integers. Movesense says mV and sends the data as 2-byte integers.

But at first glance, they appear the same :-(

So a second opinion would be useful to sort it out!

Effectively I am getting a BLE message containing a microsecond timestamp

and 73 data points every 560 mS.

Guess you mean a millisecond timestamp? That would be slightly higher then 130Hz resulting in 1286 samples extra per hour compared to the 130 Hz I used now. Or about 10 sec. Not entirely convinced here...

Polar is sampling at 130 Hz, about every 7.69 mS, and sends 73 samples at a time. That is presumably all they can fit in the maximum BLE data packet for their device.

That works out to the 560 mS.

The timestamp provided has a lot more precision (about nanoseconds), I don't know how accurate it is. I can push as much of that across as is needed.

I think (but have not verified) that Movesense is only proving millisecond accuracy on their timestamps. They send data in smaller packets, 16 samples each and support a number of different sample rates (500 Hz, 250 Hz, 125 Hz and 100 Hz.)

That should have the effect of smoothing the display updates, unlike the Polar where you get a half second of data to display every half second etc.

Or just put the timestamp in once every 73 samples?

I think just putting in what you actually get from the Polar SDK is the safest thing to do. Interpolating isn't difficult to do afterwards.

— Reply to this email directly, view it on GitHub https://github.com/stuartlynne/fitness_hrv/issues/2#issuecomment-1420634199, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACIUWPNB6TAQOHCLA7MDV3WWIX5ZANCNFSM6AAAAAASH4PYRI . You are receiving this because you commented.Message ID: @.***>

-- __O____ -\<,____ ____()/()___


@.***>__604-518-1749(m)604-461-7532(h)

MedTechCD commented 1 year ago

The timestamp provided has a lot more precision (about nanoseconds), I don't know how accurate it is. I can push as much of that across as is needed.

There is absolutely no need to have that kind of precision. But this info allows correct interpretation of the timestamp. Rounding to milliseconds (eventally µsec) should be more the enough. Plotting ECG is in my opinion best done with the sample index and not with a timeline. Then visualy display a line every second or 10 seconds which aligns with 130 or 1300 samples. Calculating interbeat intervals is the number of samples divided by the sampling rate and multiplied by 1000 to have those in milliseconds. If my interpretation is correct, for the Polar H10 you would get a very large number as a date timestamp, because it is the number of nanoseconds counted from the default date 2019-01-01 - 00:00:00. Your local system date/time minus the default date/time is an offset to display with time-of day. Once you have the precise start-time to sync with other sensor sources, things can be displayed with elapsed time. Or, at start of a recording, you send the system time to the H10 with the setlocaltime command and then the timestamps should be elapsed time in nanosecs.

The first time-stamp is the most important one to make syncing with other sources possible. After that, it's just a matter of checking that no samples were lost every x msec (560?). But the timing should be very consistent by simply using the sample index.

Regarding the amplitudes of the ECG signal: if you get any number in the stream that is higher then ~500, it must be µV because the electrical signals from the heart simply don't have amplitudes of 0.5V and higher.