Timestamps in joulescope viewer vs. pyjls

Dear Joulescope-Team!

I have recorded various measurements, resulting in different jls-files. Somehow I noticed that the timestamps of the joulescope viewer and the timestamps resulting from the pyjls function sample_id_to_timestamp() do not fit…I wonder whether I did something wrong…

Here you can see my python script. It should simply measure how long the duration of the jls file is:

import sys

import numpy as np

import pyjls.binding as jls

def load_signal(reader, signal):

samples = reader.fsr(signal.signal_id, 0, signal.length)

ids = np.arange(signal.length)

ts_ns = reader.sample_id_to_timestamp(signal.signal_id, ids)

ts_s = ts_ns.astype(float) \* 1e-9  # seconds

return ts_s, samples

def find_signal(signals, candidates):

for s in signals:

    if s.name.lower() in \[c.lower() for c in candidates\]:

        return s

return None

def compute_duration(filepath):

reader = jls.Reader(filepath)

\# read list of signals (compatible with both v1 and v2 pyjls)

signals = list(reader.signals.values()) if hasattr(reader.signals, "values") else list(reader.signals)

if not signals:

    print(f"{filepath}: No signals found in file.")

    reader.close()

    return

\# try to pick "power" signal, otherwise fall back to first one

sig = find_signal(signals, \["power", "p"\])

if sig is None:

    sig = signals\[0\]

    print(f"Warning: no 'power' signal found, using '{sig.name}'")

if sig.length == 0:

    print(f"{filepath}: Signal '{sig.name}' contains no samples.")

    reader.close()

    return

\# load timestamps via the same function your main script uses

time_s, \_ = load_signal(reader, sig)

reader.close()

if len(time_s) == 0:

    print(f"{filepath}: Could not read timestamps.")

    return

\# compute duration

duration_s = time_s\[-1\] - time_s\[0\]

print(f"File:      {filepath}")

print(f"Signal:    {sig.name}")

print(f"Samples:   {sig.length}")

print(f"Duration:  {duration_s:.6f} seconds")

print(f"Duration:  {duration_s\*1000:.3f} ms")

if _name_ == “_main_”:

if len(sys.argv) < 2:

    print("Usage: python checkTimeCalculation.py <file.jls>")

    sys.exit(1)

compute_duration(sys.argv\[1\])

When I execute this code on one of my jls-files, the duration calculates to 476 seconds. If i open the same file in the joulescope viewer, it shows a duration of 443 seconds…Do you know what the problem could be? I am working with pyjls version 0.15.0

best Anna

Hi @anna - The script you posted does not properly use sample_id_to_timestamp. The returned timestamp is in our time64 format which uses int64_t. The actual increment is 2**-30 = 9.313225746154785e-10, not 1 ns as the code assumes. You also want to keep using the int64 format until you compute the duration. 64-bit double precision floating point numbers only have a 53-bit mantissa. Converting to double before taking the difference can cause precision loss.

I have updated the checkTimeCalculation.py script to fix both issues:

import sys
import numpy as np
import pyjls.binding as jls
from pyjls import time64

def load_signal(reader, signal):
    samples = reader.fsr(signal.signal_id, 0, signal.length)
    ids = np.arange(signal.length)
    t64 = reader.sample_id_to_timestamp(signal.signal_id, ids)
    return t64, samples

def find_signal(signals, candidates):
    for s in signals:
        if s.name.lower() in [c.lower() for c in candidates]:
            return s
    return None

def compute_duration(filepath):
    reader = jls.Reader(filepath)
    # read list of signals (compatible with both v1 and v2 pyjls)
    signals = list(reader.signals.values()) if hasattr(reader.signals, "values") else list(reader.signals)
    if not signals:
        print(f"{filepath}: No signals found in file.")
        reader.close()
        return
    # try to pick "power" signal, otherwise fall back to first one
    sig = find_signal(signals, ["power", "p"])
    if sig is None:
        sig = signals[0]
        print(f"Warning: no 'power' signal found, using '{sig.name}'")
    if sig.length == 0:
        print(f"{filepath}: Signal '{sig.name}' contains no samples.")
        reader.close()
        return
    # load timestamps via the same function your main script uses
    t64, _ = load_signal(reader, sig)
    reader.close()
    if len(t64) < 2:
        print(f"{filepath}: Could not read timestamps.")
        return
    # compute duration
    duration_t64 = t64[-1] - t64[0]
    duration_s = duration_t64 / time64.SECOND
    print(f"File:      {filepath}")
    print(f"Signal:    {sig.name}")
    print(f"Samples:   {sig.length}")
    print(f"Duration:  {duration_s:.6f} seconds")
    print(f"Duration:  {duration_s * 1000:.3f} ms")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python checkTimeCalculation.py <file.jls>")
        sys.exit(1)
    compute_duration(sys.argv[1])

Does this now work for you?

It worked, thanks so much!! you are really offering great support here on this forum

:slightly_smiling_face:

1 Like