# -*- coding: utf-8 -*- """Code to read in data files from Bio-Logic instruments""" __all__ = ['MPTfileCSV', 'MPTfile'] import sys import re import csv from os import SEEK_SET, SEEK_CUR import time from datetime import date, datetime, timedelta from collections import OrderedDict from warnings import warn import numpy as np if sys.version_info.major <= 2: str3 = str else: str3 = lambda b: str(b, encoding='ascii') def fieldname_to_dtype(fieldname): """Converts a column header from the MPT file into a tuple of canonical name and appropriate numpy dtype""" if fieldname == 'mode': return ('mode', np.uint8) elif fieldname in ("ox/red", "error", "control changes", "Ns changes", "counter inc."): return (fieldname, np.bool_) elif fieldname in ("time/s", "Ewe/V", "P/W", "(Q-Qo)/mA.h", "x", "control/V", "control/V/mA", "(Q-Qo)/C"): return (fieldname, np.float_) elif fieldname in ("cycle number",): return (fieldname, np.int_) elif fieldname in ("dq/mA.h", "dQ/mA.h"): return ("dQ/mA.h", np.float_) elif fieldname in ("I/mA", "/mA"): return ("I/mA", np.float_) else: raise ValueError("Invalid column header: %s" % fieldname) def MPTfile(file_or_path): """Opens .mpt files as numpy record arrays Checks for the correct headings, skips any comments and returns a numpy record array object and a list of comments """ if isinstance(file_or_path, str): mpt_file = open(file_or_path, 'rb') else: mpt_file = file_or_path magic = next(mpt_file) if magic != b'EC-Lab ASCII FILE\r\n': raise ValueError("Bad first line for EC-Lab file: '%s'" % magic) nb_headers_match = re.match(b'Nb header lines : (\d+)\s*$', next(mpt_file)) nb_headers = int(nb_headers_match.group(1)) if nb_headers < 3: raise ValueError("Too few header lines: %d" % nb_headers) ## The 'magic number' line, the 'Nb headers' line and the column headers ## make three lines. Every additional line is a comment line. comments = [next(mpt_file) for i in range(nb_headers - 3)] fieldnames = str3(next(mpt_file)).strip().split('\t') record_type = np.dtype(list(map(fieldname_to_dtype, fieldnames))) mpt_array = np.loadtxt(mpt_file, dtype=record_type) return mpt_array, comments def MPTfileCSV(file_or_path): """Simple function to open MPT files as csv.DictReader objects Checks for the correct headings, skips any comments and returns a csv.DictReader object and a list of comments """ if isinstance(file_or_path, str): mpt_file = open(file_or_path, 'r') else: mpt_file = file_or_path magic = next(mpt_file) if magic.rstrip() != 'EC-Lab ASCII FILE': raise ValueError("Bad first line for EC-Lab file: '%s'" % magic) nb_headers_match = re.match('Nb header lines : (\d+)\s*$', next(mpt_file)) nb_headers = int(nb_headers_match.group(1)) if nb_headers < 3: raise ValueError("Too few header lines: %d" % nb_headers) ## The 'magic number' line, the 'Nb headers' line and the column headers ## make three lines. Every additional line is a comment line. comments = [next(mpt_file) for i in range(nb_headers - 3)] mpt_csv = csv.DictReader(mpt_file, dialect='excel-tab') expected_fieldnames = ( ["mode", "ox/red", "error", "control changes", "Ns changes", "counter inc.", "time/s", "control/V/mA", "Ewe/V", "dq/mA.h", "P/W", "/mA", "(Q-Qo)/mA.h", "x"], ['mode', 'ox/red', 'error', 'control changes', 'Ns changes', 'counter inc.', 'time/s', 'control/V', 'Ewe/V', 'dq/mA.h', '/mA', '(Q-Qo)/mA.h', 'x'], ["mode", "ox/red", "error", "control changes", "Ns changes", "counter inc.", "time/s", "control/V", "Ewe/V", "I/mA", "dQ/mA.h", "P/W"], ["mode", "ox/red", "error", "control changes", "Ns changes", "counter inc.", "time/s", "control/V", "Ewe/V", "/mA", "dQ/mA.h", "P/W"]) if mpt_csv.fieldnames not in expected_fieldnames: raise ValueError("Unrecognised headers for MPT file format") return mpt_csv, comments VMPmodule_hdr = np.dtype([('shortname', 'S10'), ('longname', 'S25'), ('length', ' 40000 and ole_timestamp1 < 50000: ole_timestamp = ole_timestamp1 elif ole_timestamp2 > 40000 and ole_timestamp2 < 50000: ole_timestamp = ole_timestamp2 ole_base = datetime(1899, 12, 30, tzinfo=None) ole_timedelta = timedelta(days=ole_timestamp[0]) self.timestamp = ole_base + ole_timedelta if self.startdate != self.timestamp.date(): raise ValueError("""Date mismatch: Start date: %s End date: %s Timestamp: %s""" % (self.startdate, self.enddate, self.timestamp))