Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 11dc98a

Browse files
committed
Code to read in Tecmag TNMR .tnt files
1 parent 19b5d90 commit 11dc98a

3 files changed

Lines changed: 365 additions & 0 deletions

File tree

nmrglue/fileio/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import rnmrtk
66
import simpson
77
import sparky
8+
import tecmag
89
import varian
910
import varian as agilent # replicate varian namespace in agilent
1011
import table

nmrglue/fileio/tecmag.py

Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
"""
2+
Functions for reading and Tecmag .tnt data files
3+
"""
4+
__developer_info__ = """
5+
Tecmag .tnt file format information
6+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7+
8+
The Tecmag .tnt file format is documented with C pseudo-code in the file
9+
"A1 - TNMR File Format.doc" distributed with the TNMR software.
10+
11+
This file is based on the pytnt module available at
12+
https://www.github.com/chatcannon/pytnt
13+
Please inform upstream if you find a bug or want a new feature.
14+
15+
"""
16+
17+
import io
18+
import re
19+
from collections import OrderedDict
20+
21+
import numpy as np
22+
23+
from . import fileiobase
24+
25+
26+
tntMagic_re = re.compile(b"^TNT1\.\d\d\d$")
27+
28+
tntMagic = np.dtype('a8')
29+
tntTLV = np.dtype([('tag', 'a4'), ('bool', '<u4'), ('length', '<u4')])
30+
31+
tntTMAG = np.dtype([
32+
('npts', '<i4', 4),
33+
('actual_npts', '<i4', 4),
34+
('acq_points', '<i4'),
35+
('npts_start', '<i4', 4),
36+
('scans', '<i4'),
37+
('actual_scans', '<i4'),
38+
('dummy_scans', '<i4'),
39+
('repeat_times', '<i4'),
40+
('sadimension', '<i4'),
41+
('samode', '<i4'),
42+
# ('space1', 'a0'),
43+
44+
('magnet_field', '<f8'),
45+
('ob_freq', '<f8', 4),
46+
('base_freq', '<f8', 4),
47+
('offset_freq', '<f8', 4),
48+
('ref_freq', '<f8'),
49+
('NMR_frequency', '<f8'),
50+
('obs_channel', '<i2'),
51+
('space2', 'a42'),
52+
53+
('sw', '<f8', 4),
54+
('dwell', '<f8', 4),
55+
('filter', '<f8'),
56+
('experiment_time', '<f8'),
57+
('acq_time', '<f8'),
58+
('last_delay', '<f8'),
59+
('spectrum_direction', '<i2'),
60+
('hardware_sideband', '<i2'),
61+
('Taps', '<i2'),
62+
('Type', '<i2'),
63+
('bDigRec', '<u4'),
64+
('nDigitalCenter', '<i4'),
65+
('space3', 'a16'),
66+
67+
('transmitter_gain', '<i2'),
68+
('receiver_gain', '<i2'),
69+
('NumberOfReceivers', '<i2'),
70+
('RG2', '<i2'),
71+
('receiver_phase', '<f8'),
72+
('space4', 'a4'),
73+
74+
('set_spin_rate', '<u2'),
75+
('actual_spin_rate', '<u2'),
76+
77+
('lock_field', '<i2'),
78+
('lock_power', '<i2'),
79+
('lock_gain', '<i2'),
80+
('lock_phase', '<i2'),
81+
('lock_freq_mhz', '<f8'),
82+
('lock_ppm', '<f8'),
83+
('H2O_freq_ref', '<f8'),
84+
('space5', 'a16'),
85+
86+
('set_temperature', '<f8'),
87+
('actual_temperature', '<f8'),
88+
89+
('shim_units', '<f8'),
90+
('shims', '<i2', 36),
91+
('shim_FWHM', '<f8'),
92+
93+
('HH_dcpl_attn', '<i2'),
94+
('DF_DN', '<i2'),
95+
('F1_tran_mode', '<i2', 7),
96+
('dec_BW', '<i2'),
97+
('grd_orientation', 'a4'),
98+
('LatchLP', '<i4'),
99+
('grd_Theta', '<f8'),
100+
('grd_Phi', '<f8'),
101+
('space6', 'a264'),
102+
103+
('start_time', '<u4'),
104+
('finish_time', '<u4'),
105+
('elapsed_time', '<i4'),
106+
107+
('date', 'a32'),
108+
('nuclei', 'a16', 4),
109+
('sequence', 'a32'),
110+
('lock_solvent', 'a16'),
111+
('lock_nucleus', 'a16')
112+
])
113+
114+
115+
tntGridAndAxis = np.dtype([
116+
('majorTickInc', '<f8', 12),
117+
('minorIntNum', '<i2', 12),
118+
('labelPrecision', '<i2', 12),
119+
('gaussPerCentimeter', '<f8'),
120+
('gridLines', '<i2'),
121+
('axisUnits', '<i2'),
122+
('showGrid', '<u4'),
123+
('showGridLabels', '<u4'),
124+
('adjustOnZoom', '<u4'),
125+
('showDistanceUnits', '<u4'),
126+
('axisName', 'a32'),
127+
('space', 'a52'),
128+
])
129+
130+
131+
tntTMG2 = np.dtype([
132+
('real_flag', '<u4'),
133+
('imag_flag', '<u4'),
134+
('magn_flag', '<u4'),
135+
('axis_visible', '<u4'),
136+
('auto_scale', '<u4'),
137+
('line_display', '<u4'),
138+
('show_shim_units', '<u4'),
139+
140+
('integral_display', '<u4'),
141+
('fit_display', '<u4'),
142+
('show_pivot', '<u4'),
143+
('label_peaks', '<u4'),
144+
('keep_manual_peaks', '<u4'),
145+
('label_peaks_in_units', '<u4'),
146+
('integral_dc_average', '<u4'),
147+
('integral_show_multiplier', '<u4'),
148+
('Boolean_space', '<u4', 9),
149+
150+
('all_ffts_done', '<u4', 4),
151+
('all_phase_done', '<u4', 4),
152+
153+
('amp', '<f8'),
154+
('ampbits', '<f8'),
155+
('ampCtl', '<f8'),
156+
('offset', '<i4'),
157+
158+
('axis_set', tntGridAndAxis),
159+
160+
('display_units', '<i2', 4),
161+
('ref_point', '<i4', 4),
162+
('ref_value', '<f8', 4),
163+
('z_start', '<i4'),
164+
('z_end', '<i4'),
165+
('z_select_start', '<i4'),
166+
('z_select_end', '<i4'),
167+
('last_zoom_start', '<i4'),
168+
('last_zoom_end', '<i4'),
169+
('index_2D', '<i4'),
170+
('index_3D', '<i4'),
171+
('index_4D', '<i4'),
172+
173+
('apodization_done', '<i4', 4),
174+
('linebrd', '<f8', 4),
175+
('gaussbrd', '<f8', 4),
176+
('dmbrd', '<f8', 4),
177+
('sine_bell_shift', '<f8', 4),
178+
('sine_bell_width', '<f8', 4),
179+
('sine_bell_skew', '<f8', 4),
180+
('Trapz_point_1', '<i4', 4),
181+
('Trapz_point_2', '<i4', 4),
182+
('Trapz_point_3', '<i4', 4),
183+
('Trapz_point_4', '<i4', 4),
184+
('trafbrd', '<f8', 4),
185+
('echo_center', '<i4', 4),
186+
187+
('data_shift_points', '<i4'),
188+
('fft_flag', '<i2', 4),
189+
('unused', '<f8', 8),
190+
('pivot_point', '<i4', 4),
191+
('cumm_0_phase', '<f8', 4),
192+
('cumm_1_phase', '<f8', 4),
193+
('manual_0_phase', '<f8'),
194+
('manual_1_phase', '<f8'),
195+
('phase_0_value', '<f8'),
196+
('phase_1_value', '<f8'),
197+
('session_phase_0', '<f8'),
198+
('session_phase_1', '<f8'),
199+
200+
('max_index', '<i4'),
201+
('min_index', '<i4'),
202+
('peak_threshold', '<f4'),
203+
('peak_noise', '<f4'),
204+
('integral_dc_points', '<i2'),
205+
('integral_label_type', '<i2'),
206+
('integral_scale_factor', '<f4'),
207+
('auto_integrate_shoulder', '<i4'),
208+
('auto_integrate_noise', '<f8'),
209+
('auto_integrate_threshold', '<f8'),
210+
('s_n_peak', '<i4'),
211+
('s_n_noise_start', '<i4'),
212+
('s_n_noise_end', '<i4'),
213+
('s_n_calculated', '<f4'),
214+
215+
('Spline_point', '<i4', 14),
216+
('Spline_point_avr', '<i2'),
217+
('Poly_point', '<i4', 8),
218+
('Poly_point_avr', '<i2'),
219+
('Poly_order', '<i2'),
220+
221+
('space', 'a610'),
222+
223+
('line_simulation_name', 'a32'),
224+
('integral_template_name', 'a32'),
225+
('baseline_template_name', 'a32'),
226+
('layout_name', 'a32'),
227+
('relax_information_name', 'a32'),
228+
('username', 'a32'),
229+
('user_string_1', 'a16'),
230+
('user_string_2', 'a16'),
231+
('user_string_3', 'a16'),
232+
('user_string_4', 'a16')
233+
])
234+
235+
236+
def read(filename):
237+
"""
238+
Read a .tnt data file
239+
240+
Parameters
241+
----------
242+
filename : str
243+
Name of file to read from
244+
245+
Returns
246+
-------
247+
dic : dict
248+
Dictionary of Tecmag parameters.
249+
data : ndarray
250+
Array of NMR data.
251+
252+
"""
253+
tnt_sections = OrderedDict()
254+
255+
with open(filename, 'rb') as tntfile:
256+
257+
tntmagic = np.fromstring(tntfile.read(tntMagic.itemsize),
258+
tntMagic, count=1)[0]
259+
260+
if not tntMagic_re.match(tntmagic):
261+
raise ValueError("Invalid magic number (is '%s' really a TNMR file?): %s" % (filename, tntmagic))
262+
263+
##Read in the section headers
264+
tnthdrbytes = tntfile.read(tntTLV.itemsize)
265+
while(tntTLV.itemsize == len(tnthdrbytes)):
266+
TLV = np.fromstring(tnthdrbytes, tntTLV)[0]
267+
data_length = TLV['length']
268+
hdrdict = {'offset': tntfile.tell(),
269+
'length': data_length,
270+
'bool': bool(TLV['bool'])}
271+
if data_length <= 4096:
272+
hdrdict['data'] = tntfile.read(data_length)
273+
assert(len(hdrdict['data']) == data_length)
274+
else:
275+
tntfile.seek(data_length, io.SEEK_CUR)
276+
tnt_sections[TLV['tag']] = hdrdict
277+
tnthdrbytes = tntfile.read(tntTLV.itemsize)
278+
279+
assert(tnt_sections['TMAG']['length'] == tntTMAG.itemsize)
280+
TMAG = np.fromstring(tnt_sections['TMAG']['data'], tntTMAG, count=1)[0]
281+
282+
assert(tnt_sections['DATA']['length'] ==
283+
TMAG['actual_npts'].prod() * 8)
284+
## For some reason we can't set offset and shape together
285+
#DATA = np.memmap(tntfilename,np.dtype('<c8'), mode='r',
286+
# offset=self.tnt_sections['DATA']['offset'],
287+
# shape=self.TMAG['actual_npts'].tolist(),order='F')
288+
DATA = np.memmap(filename, np.dtype('<c8'), mode='c',
289+
offset=tnt_sections['DATA']['offset'],
290+
shape=TMAG['actual_npts'].prod())
291+
DATA = np.reshape(DATA, TMAG['actual_npts'], order='F')
292+
293+
assert(tnt_sections['TMG2']['length'] == tntTMG2.itemsize)
294+
TMG2 = np.fromstring(tnt_sections['TMG2']['data'], tntTMG2, count=1)[0]
295+
296+
dic = dict()
297+
for name in tntTMAG.names:
298+
if not name.startswith('space'):
299+
dic[name] = TMAG[name]
300+
for name in tntTMG2.names:
301+
if name not in ['Boolean_space', 'unused', 'space', 'axis_set']:
302+
dic[name] = TMG2[name]
303+
for name in tntGridAndAxis.names:
304+
dic[name] = TMG2['axis_set'][name]
305+
306+
return dic, DATA
307+
308+
309+
def guess_udic(dic, data):
310+
"""
311+
Guess parameters of universal dictionary from dic, data pair.
312+
313+
Parameters
314+
----------
315+
dic : dict
316+
Dictionary of Tecmag parameters.
317+
data : ndarray
318+
Array of NMR data.
319+
320+
Returns
321+
-------
322+
udic : dict
323+
Universal dictionary of spectral parameters.
324+
325+
"""
326+
327+
# create an empty universal dictionary
328+
udic = fileiobase.create_blank_udic(4)
329+
330+
# update default values
331+
for i in xrange(4):
332+
udic[i]["size"] = dic['actual_npts'][i]
333+
udic[i]["sw"] = dic['sw'][i]
334+
udic[i]["complex"] = True
335+
udic[i]["obs"] = dic['ob_freq'] * 1e6
336+
# Not sure what the difference is here
337+
# N.B. base_freq is some bogus value like 1.4e-13
338+
udic[i]["car"] = dic['ob_freq'] * 1e6
339+
udic[i]["time"] = not bool(dic['fft_flag'])
340+
udic[i]["freq"] = bool(dic['fft_flag'])
341+
342+
return udic

tests/test_tecmag.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import os
2+
3+
import numpy as np
4+
from numpy.testing import assert_allclose
5+
from numpy.testing import assert_array_almost_equal
6+
7+
import nmrglue as ng
8+
9+
from setup import DATA_DIR
10+
11+
12+
def test_tecmag_load_time_domain():
13+
ref1, data = ng.tecmag.read(os.path.join(DATA_DIR, 'tecmag', 'LiCl_ref1.tnt'))
14+
15+
real, imag, usec = np.loadtxt(os.path.join(DATA_DIR, 'tecmag', 'LiCl_ref1.txt'),
16+
skiprows=3, unpack=True)
17+
18+
assert_array_almost_equal(data.real.squeeze(), real, decimal=3)
19+
assert_array_almost_equal(data.imag.squeeze(), imag, decimal=3)
20+
assert_array_almost_equal(np.arange(ref1['npts'][0]) *
21+
ref1['dwell'][0] * 1e6,
22+
usec, decimal=3)

0 commit comments

Comments
 (0)