Start ioscopy script from config file
[oscopy.git] / oscopy / readers / spice3raw_reader.py
blobdf0af6d815578f6b6ee53da32a343f4934ddd564
1 from __future__ import with_statement
3 import re
4 from oscopy import Signal
5 from reader import Reader, ReadError
6 import struct
8 # ivar: independent variable (Time, Frequency)
9 # dvar: dependent variables (Signals)
11 class Spice3rawReader(Reader):
12 """ Read Spice3 output files ascii and binary
14 Spice3 files can be binary or ascii. Both types have a plain text header
15 containing various informations. This Reader uses 'Flags', 'No. Variables',
16 'No. Points' and 'Variables'.
18 Supports real and complex numbers
20 Note: It supports only one simulation per file
22 For more details see http://www.rvq.fr/linux/gawfmt.php
23 """
24 _types_to_unit = {'Time': 's', 'voltage': 'V', 'current': 'A', 'frequency': 'Hz'}
25 _blocks = ['Plotname', 'Flags', 'No. Variables', 'No. Points', 'Command',
26 'Variables', 'Title', 'Date']
27 _flags = ['complex', 'real']
29 def detect(self, fn):
30 """ Look at the header if it contains the keyword 'Title:' at first line
31 and 'Data:' at second line
33 Parameter
34 ---------
35 fn: string
36 Path to the file to test
38 Returns
39 -------
40 bool
41 True if the file can be handled by this reader
42 """
43 self._check(fn)
44 blocks = self._blocks
45 nvar = 0
46 with open(fn) as f:
47 lines = iter(f)
48 if lines.next().startswith('Title') and lines.next().startswith('Date'):
49 return True
50 return False
52 def _read_signals(self):
53 """ Read the signals from the file
55 First get the reader and extract number, names and types of variables
56 Then read the data values, and finally, assign the abscisse and
57 data to each signal.
59 Parameter
60 ---------
61 fn: string
62 The filename
64 Returns
65 -------
66 Dict of Signals
67 The list of Signals read from the file
69 Raises
70 ------
71 ReaderError
72 In case of invalid path or unsupported file format
73 """
74 read_fun = None
75 blocks = self._blocks
76 data_read_fun = {'Binary': self._read_binary, 'Values': self._read_ascii}
77 signals = []
78 types = []
79 names = []
80 pos = 0 # Position of the data start
81 with open(self._fn) as f:
82 for line in f:
83 pos = pos + len(line)
84 # Parse the file
85 words = line.split()
86 word = words[0].rstrip(':')
87 value = words[1] if len(words) > 1 else None
88 if word.startswith('No.'):
89 # Lines starting with 'No. '
90 word = ' '.join((word, words[1].rstrip(':')))
91 value = words[2]
92 if word in blocks:
93 # Header word
94 if word == 'No. Variables':
95 nvar = int(words[2])
96 elif word == 'Variables':
97 # Initialize Signals
98 for i in xrange(nvar):
99 x = f.next()
100 pos = pos + len(x)
101 x = x.split()
102 name = x[1].replace('(', '').replace(')', '')\
103 .replace('#', '').replace('+', '')\
104 .replace('-', '')
106 signals.append(Signal(name, self._types_to_unit.get(x[2], 'a.u')))
108 names.append(name)
109 n = blocks.index(word)
110 self.info[word] = value
111 elif word in data_read_fun.keys():
112 # Header processing finished
113 break
114 else:
115 raise ReadError(_('Spice3raw_reader: unexpected keyword in header: \'%s\'') % word)
117 # Can now read the data
118 data = [[] for x in xrange(len(signals))]
119 append = [x.append for x in data]
121 data_read_fun[word](pos, append)
123 ref = signals[0]
124 ref.data = data[0]
125 for i, s in enumerate(signals[1:]):
126 s.ref = ref
127 s.data = data[i + 1]
129 self._signals = dict(zip(names[1:], signals[1:]))
130 return self._signals
132 def _read_binary(self, pos, append):
133 """ Read the data from the file in binary mode.
135 Data is read for the number of points defined in 'No. Points'.
136 Data type is assumed to be 'double' whatever the type is (complex or
137 real). This is in disagreement with http://www.rvq.fr/linux/gawfmt.php.
139 Parameter
140 ---------
141 pos: integer
142 The offset where to start reading the data
144 append: list of append() methods
145 List to use to store the data
147 Returns
148 -------
149 append: list of append() methods
151 if self._info['Flags'] not in self._flags:
152 raise ReadError(_('Spice3raw_reader: unexpected value for keyword \'Flags\': %s')% (self._info['Flags']))
154 is_complex = (self._info['Flags'] == 'complex')
155 nvars = int(self._info['No. Variables']) * (2 if is_complex else 1)
156 n = int(self._info['No. Points']) # Data counter
158 with open(self._fn) as f:
159 # print pos, '(0x%04x)'% pos
160 f.seek(pos)
161 while f and n:
162 tmp = f.read(8 * nvars)
163 if len(tmp) < 8 * nvars: break
164 values = struct.unpack('<%dd' % nvars, tmp)
165 n = n - 1
166 if is_complex:
167 for i, v in enumerate(values[::2]):
168 append[i](complex(v, values[2*i+1]))
169 else:
170 for i, v in enumerate(values):
171 append[i](v)
172 return append
174 def _read_ascii(self, pos, append):
175 """ Read the data from the file in ascii mode.
177 Data is read for the number of points defined in 'No. Points'.
179 Parameter
180 ---------
181 pos: integer
182 The offset where to start reading the data
184 append: list of append() methods
185 List to use to store the data
187 Returns
188 -------
189 append: list of append() methods
191 is_complex = (self._info['Flags'] == 'complex')
192 nvars = int(self._info['No. Variables']) * (2 if is_complex else 1)
193 n = int(self._info['No. Points']) # Data counter
195 with open(self._fn) as f:
196 f.seek(pos)
197 while f and n:
198 values = f.next()
199 x = values.split()
200 if len(x) > 1:
201 # Get the ivar
202 append[0](float(x[1]))
203 n = n - 1
204 for i in xrange(1, nvars):
205 # And the dvars
206 values = f.next().split()
207 if is_complex:
208 append[i](complex(float(values[0]), float(values[1])))
209 else:
210 append[i](float(values[0]))
211 return append