Added class to read in .mpr files

The class currently just reads the main data section and does not
process any of the headers or log information.
This commit is contained in:
Chris Kerr
2013-11-30 20:08:08 +00:00
parent d909305b74
commit 1f9c9653c5
3 changed files with 128 additions and 3 deletions

View File

@@ -5,6 +5,7 @@ __all__ = ['MPTfileCSV', 'MPTfile']
import re
import csv
from os import SEEK_SET, SEEK_CUR
import numpy as np
@@ -119,3 +120,77 @@ def MPTfileCSV(file_or_path):
raise ValueError("Unrecognised headers for MPT file format")
return mpt_csv, comments
VMPmodule_hdr = np.dtype([('shortname', 'S10'),
('longname', 'S25'),
('length', '<u8'),
('date', 'S8')])
VMPdata_dtype = np.dtype([('flags', 'u1'),
("time/s", '<f8'),
("control/V/mA", '<f4'),
("Ewe/V", '<f4'),
("dQ/mA.h", '<f8'),
("P/W", '<f4')])
def read_VMP_modules(fileobj, read_module_data=True):
"""Reads in module headers in the VMPmodule_hdr format. Yields a dict with
the headers and offset for each module.
N.B. the offset yielded is the offset to the start of the data i.e. after
the end of the header. The data runs from (offset) to (offset+length)"""
while True:
module_magic = fileobj.read(len(b'MODULE'))
if len(module_magic) == 0: # end of file
raise StopIteration
elif module_magic != b'MODULE':
raise ValueError("Found %r, expecting start of new VMP MODULE" % module_magic)
hdr_bytes = fileobj.read(VMPmodule_hdr.itemsize)
if len(hdr_bytes) < VMPmodule_hdr.itemsize:
raise IOError("Unexpected end of file while reading module header")
hdr = np.fromstring(hdr_bytes, dtype=VMPmodule_hdr, count=1)
hdr_dict = dict(((n, hdr[n][0]) for n in VMPmodule_hdr.names))
hdr_dict['offset'] = fileobj.tell()
if read_module_data:
hdr_dict['data'] = fileobj.read(hdr_dict['length'])
if len(hdr_dict['data']) != hdr_dict['length']:
raise IOError("""Unexpected end of file while reading data
current module: %s
length read: %d
length expected: %d""" % (hdr_dict['longname'],
len(hdr_dict['data']),
hdr_dict['length']))
yield hdr_dict
else:
yield hdr_dict
fileobj.seek(hdr_dict['offset'] + hdr_dict['length'], SEEK_SET)
class MPRfile:
"""Bio-Logic .mpr file
The file format is not specified anywhere and has therefore been reverse
engineered. Not all the fields are known.
"""
def __init__(self, file_or_path):
if isinstance(file_or_path, str):
mpr_file = open(file_or_path, 'rb')
else:
mpr_file = file_or_path
mpr_magic = b'BIO-LOGIC MODULAR FILE\x1a \x00\x00\x00\x00'
magic = mpr_file.read(len(mpr_magic))
if magic != mpr_magic:
raise ValueError('Invalid magic for .mpr file: %s' % magic)
modules = list(read_VMP_modules(mpr_file))
self.modules = modules
data_module, = (m for m in modules if m['shortname'] == b'VMP data ')
## There is 100 bytes of data before the main array starts
self.data = np.frombuffer(data_module['data'], dtype=VMPdata_dtype,
offset=100)

View File

@@ -1 +1 @@
from .BioLogic import MPTfile
from .BioLogic import MPTfile, MPRfile

View File

@@ -2,9 +2,12 @@
import os.path
import numpy as np
from numpy.testing import assert_array_almost_equal, assert_array_equal
from nose.tools import ok_, eq_, raises
from ..BioLogic import MPTfile, MPTfileCSV
from ..import MPTfile, MPRfile
from ..BioLogic import MPTfileCSV # not exported
testdata_dir = os.path.join(os.path.dirname(__file__), 'testdata')
@@ -35,3 +38,50 @@ def test_open_MPT_csv():
@raises(ValueError)
def test_open_MPT_csv_fails_for_bad_file():
mpt1 = MPTfileCSV(os.path.join(testdata_dir, 'bio-logic1.mpr'))
def test_open_MPR():
mpr1 = MPRfile(os.path.join(testdata_dir, 'bio-logic1.mpr'))
@raises(ValueError)
def test_open_MPR_fails_for_bad_file():
mpr1 = MPRfile(os.path.join(testdata_dir, 'arbin1.res'))
def test_MPR_matches_MPT():
mpr1 = MPRfile(os.path.join(testdata_dir, 'bio-logic1.mpr'))
mpt1, comments = MPTfile(os.path.join(testdata_dir, 'bio-logic1.mpt'))
assert_array_equal(mpr1.data["flags"] & 0x03, mpt1["mode"])
assert_array_equal(np.array(mpr1.data["flags"] & 0x04, dtype=np.bool_),
mpt1["ox/red"])
assert_array_equal(np.array(mpr1.data["flags"] & 0x08, dtype=np.bool_),
mpt1["error"])
assert_array_equal(np.array(mpr1.data["flags"] & 0x10, dtype=np.bool_),
mpt1["control changes"])
assert_array_equal(np.array(mpr1.data["flags"] & 0x20, dtype=np.bool_),
mpt1["Ns changes"])
## Nothing uses the 0x40 bit of the flags
assert_array_equal(np.array(mpr1.data["flags"] & 0x80, dtype=np.bool_),
mpt1["counter inc."])
assert_array_almost_equal(mpr1.data["time/s"],
mpt1["time/s"],
decimal=5) # 5 digits in CSV
assert_array_almost_equal(mpr1.data["control/V/mA"],
mpt1["control/V/mA"],
decimal=6) # 32 bit float precision
assert_array_almost_equal(mpr1.data["Ewe/V"],
mpt1["Ewe/V"],
decimal=6) # 32 bit float precision
assert_array_almost_equal(mpr1.data["dQ/mA.h"],
mpt1["dQ/mA.h"],
decimal=17) # 64 bit float precision
assert_array_almost_equal(mpr1.data["P/W"],
mpt1["P/W"],
decimal=10) # 32 bit float precision for 1.xxE-5