#!/usr/bin/env python3
# pylint: disable=invalid-name
# pylint: disable=missing-function-docstring
# pylint: disable=unused-argument
# pylint: disable=consider-using-f-string
"""
Command line utility to convert Rigol .wfm files.
Examples::
prompt> wfmconvert E info DS1102E-A.wfm
prompt> wfmconvert E csv DS1102E-A.wfm
prompt> wfmconvert E wav DS1102E-A.wfm
"""
import re
import os
import sys
import shutil
import argparse
import subprocess
import textwrap
import RigolWFM
import RigolWFM.wfm
[docs]
def info(args, scope_data, infile):
"""Create a string that describes content of .wfm file."""
s = scope_data.describe()
print(s)
[docs]
def csv(args, scope_data, infile):
"""Create a file with comma separated values."""
csv_name = os.path.splitext(infile)[0] + '.csv'
if os.path.isfile(csv_name) and not args.force:
print("'%s' exists, use --force to overwrite" % csv_name)
return
s = scope_data.csv()
with open(csv_name, 'wb') as f:
b = s.encode(encoding='utf-8')
f.write(b)
[docs]
def vcsv(args, scope_data, infile):
"""Create a file with comma separated values (full volts)."""
csv_name = os.path.splitext(infile)[0] + '.csv'
if os.path.isfile(csv_name) and not args.force:
print("'%s' exists, use --force to overwrite" % csv_name)
return
s = scope_data.sigrokcsv()
with open(csv_name, 'wb') as f:
b = s.encode(encoding='utf-8')
f.write(b)
[docs]
def wav(args, scope_data, infile):
"""Create an audible .wav file for use in LTspice."""
wav_name = os.path.splitext(infile)[0] + '.wav'
if os.path.isfile(wav_name) and not args.force:
print("'%s' exists, use --force to overwrite" % wav_name)
return
scope_data.wav(wav_name, autoscale=args.autoscale)
[docs]
def sigrok(args, scope_data, infile):
"""Create a Sigrok (.sr) file."""
sigrok_name = os.path.splitext(infile)[0] + '.sr'
if os.path.isfile(sigrok_name) and not args.force:
print(f"'{sigrok_name}' exists, use --force to overwrite", file=sys.stderr)
return False
s = scope_data.sigrokcsv()
# Check if sigrok-cli is installed and accessible
if not shutil.which("sigrok-cli"):
print("sigrok-cli is not installed or not found in PATH.", file=sys.stderr)
print("See https://sigrok.org/wiki/Sigrok-cli for more information.", file=sys.stderr)
return False
# sigrok-cli reports a warning about /dev/stdin not being a regular file,
# but the conversion works fine.
try:
p = subprocess.run(
['sigrok-cli', '-I', 'csv:start_line=2:column_formats=t,1a',
'-i', '/dev/stdin', '-o', sigrok_name],
input=s,
check=True,
stderr=subprocess.PIPE,
text=True)
except subprocess.CalledProcessError as e:
print(f"sigrok-cli failed with error: {e.stderr}", file=sys.stderr)
print("See https://sigrok.org/wiki/Sigrok-cli for more information.", file=sys.stderr)
return False
except Exception as e:
print(f"An unexpected error occurred: {e}", file=sys.stderr)
return False
if p.returncode != 0:
print(f"sigrok-cli returned non-zero exit code: {p.returncode}", file=sys.stderr)
print("See https://sigrok.org/wiki/Sigrok-cli for more information.", file=sys.stderr)
return False
return True
[docs]
def main():
"""Parse console command line arguments."""
parser = argparse.ArgumentParser(
prog='wfmconvert',
description='Convert Rigol WFM files to another format.',
formatter_class=argparse.RawTextHelpFormatter,
epilog=textwrap.dedent("""\
examples:
wfmconvert E info DS1102E.wfm
wfmconvert --channel 2 E csv DS1102E.wfm
wfmconvert --channel 124 E vcsv DS1102E.wfm
wfmconvert --channel 34 --autoscale E wav DS1102E.wfm
""") + RigolWFM.wfm.valid_scope_list()
)
parser.add_argument(
'--force',
action='store_true',
default=False,
help="overwrite existing output files"
)
parser.add_argument(
'--autoscale',
action='store_true',
default=False,
help=textwrap.dedent("""\
autoscale each channel to full range. Used when creating
.wav files so signal goes from 0-255.
""")
)
parser.add_argument(
'--channel',
type=str,
default='1234',
help=textwrap.dedent("""\
select channel(s) to process. `--channel 1` outputs only contents of
the first channel. `--channel 34` outputs contents of channels 3 and 4.
The default is `--channel 1234`.
""")
)
parser.add_argument(
'model',
type=str,
choices=['B', 'C', 'D', 'E', 'Z', '2', '4', '6'],
help='oscilloscope model. See list below.'
)
parser.add_argument(
'--version',
action='version',
version='%(prog)s {version}'.format(version=RigolWFM.__version__)
)
parser.add_argument(
dest='action',
choices=['csv', 'info', 'wav', 'vcsv', 'sigrok'],
help=textwrap.dedent("""\
csv: convert to a file with comma separated values
info: show the various scope settings for a .wfm file
wav: convert to a WAV sound format file for use with Audacity
or Sigrok Pulseview. If a single channel is specified then
the .wav file can be used with LTspice.
vcsv: convert to a file with comma separated values with raw voltages
sigrok: convert to a sigrok file
""")
)
parser.add_argument(
'infile',
type=str,
nargs='+',
help="the WFM file to be converted"
)
args = parser.parse_args()
# strip anything that is not a possible channel number
good = re.sub(r'[^1234]', '', args.channel)
# remove duplicates keeping order
selected = "".join(dict.fromkeys(good))
if len(selected) == 0:
print('\nwfmconvert error')
print('No valid channels were passed after --channel')
print('Channels are identified by number and must be a combination of 1, 2, 3, or 4')
print('You used "--channel %s"' % args.channel)
sys.exit()
if len(selected) > 4:
print('\nwfmconvert error')
print('Only 1-2 channel (monophonic) or 3-4 channels (stereo) ', end='')
print('can be used when creating a .wav file')
print('You used "--channel %s"' % args.channel)
sys.exit()
actionMap = {"info": info, "csv": csv, "wav": wav, "vcsv": vcsv, "sigrok": sigrok}
for filename in args.infile:
try:
scope_data = RigolWFM.wfm.Wfm.from_file(filename, args.model, selected)
actionMap[args.action](args, scope_data, filename)
except RigolWFM.wfm.Parse_WFM_Error as e:
print("File contents do not follow the format for", end='', file=sys.stderr)
print("for the Rigol Oscilloscope Model %s." % args.model, file=sys.stderr)
print("To help with development, please report this error\n", file=sys.stderr)
print("as an issue to https://github.com/scottprahl/RigolWFM\n", file=sys.stderr)
print(e, file=sys.stderr)
sys.exit()
if __name__ == "__main__":
main()