Hello,
Is it possible to join/append two jls files, via py script perhaps? I have one file I did a quick stop on and then continued the run in a new filename - can I join these files together in order to do a full analysis?
TIA
Hi @cherkey - A generalized JLS concatenate is not possible, since JLS files can hold totally incompatible content. JLS files recorded by the Joulescope UI are also not guaranteed to contain the same number of samples across all signals.
If JLS files contain the same sources & signals and we ignore the sample count (possibly resulting in time misalignment across siganls), then here is a script that may work for you:
#!/usr/bin/env python3
# Copyright 2025 Jetperch LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from pyjls import Reader, Writer
import argparse
import sys
def parser_config(p):
"""Concatenate JLS files."""
p.add_argument('--block-size',
default=10_000_000,
type=int,
help='Block processing size in samples')
p.add_argument('output',
help='The output jls file')
p.add_argument('inputs',
nargs='+',
help='The input jls files, concatentate in order.')
return p
def on_cmd(args):
signals = {}
with Writer(args.output) as w:
for idx, path in enumerate(args.inputs):
with Reader(path) as r:
# Configure sources and signals on first JLS file
if idx == 0:
for source in r.sources.values():
if source.source_id == 0:
continue
w.source_def_from_struct(source)
for signal in r.signals.values():
if signal.signal_id == 0:
continue
w.signal_def_from_struct(signal)
source_name = r.sources[signal.source_id].name
signals[f'{source_name}|{signal.name}'] = {
'signal_id': signal.signal_id,
'sample_rate': signal.sample_rate,
'offset': 0,
}
def on_utc(entries):
for sample_id, timestamp in entries:
w.utc(signal.signal_id, sample_id, timestamp)
break
return True
r.utc(signal.signal_id, -signal.sample_rate, on_utc)
# Match signals to first JLS file
signal_map = {}
for r_signal in r.signals.values():
if r_signal.signal_id == 0:
continue
source_name = r.sources[r_signal.source_id].name
signal_name = f'{source_name}|{r_signal.name}'
if signal_name in signals:
signal_state = signals[signal_name]
if r_signal.sample_rate != signal_state['sample_rate']:
print(f"Signal sample rate mismatch: {r_signal.sample_rate} != {signal_state['sample_rate']}")
return 1
signal_map[signal_name] = r_signal
else:
print(f'Signal {signal_name} unmatched: skip')
# Concatentate data for each signal
for signal_name, signal_state in signals.items():
if signal_name not in signal_map:
print(f'WARNING: Signal {signal_name} not in {path}: abort')
return 1
r_signal = signal_map[signal_name]
offset = 0
remaining = r_signal.length
while remaining:
sz = min(remaining, args.block_size)
data = r.fsr(r_signal.signal_id, offset, sz)
w.fsr(signal_state['signal_id'], signal_state['offset'], data)
signal_state['offset'] += sz
offset += sz
remaining -= sz
return 0
def run():
parser = argparse.ArgumentParser(description=parser_config.__doc__)
parser = parser_config(parser)
args = parser.parse_args()
return on_cmd(args)
if __name__ == '__main__':
sys.exit(run())
If you save this python script to jls_concat.py
, then you can use it like this:
python .\jls_concat.py out.jls src1.jls src2.jls src3.jls
Does this work for your case?
Awesome, that worked wonderfully, thx!
1 Like