gmode_iv.py 8.66 KB
Newer Older
Somnath, Suhas's avatar
Somnath, Suhas committed
1
2
3
4
5
6
7
# -*- coding: utf-8 -*-
"""
Created on Sun May 29 17:58:35 2016

@author: Suhas Somnath
"""

8
from __future__ import division, print_function, absolute_import, unicode_literals
9

10
from os import path, remove  # File Path formatting
Somnath, Suhas's avatar
Somnath, Suhas committed
11
from warnings import warn
12

Somnath, Suhas's avatar
Somnath, Suhas committed
13
import h5py
14
import numpy as np  # For array operations
15

16
17
18
from sidpy.sid import Translator
from sidpy.hdf.hdf_utils import write_simple_attrs

19
from pyUSID.io.write_utils import Dimension
20
from pyUSID.io.hdf_utils import write_main_dataset, create_indexed_group
21

Somnath, Suhas's avatar
Somnath, Suhas committed
22

23
class GIVTranslator(Translator):
Somnath, Suhas's avatar
Somnath, Suhas committed
24
25
26
    """
    Translates G-mode Fast IV datasets from .mat files to .h5
    """
Unknown's avatar
Unknown committed
27
28
29
    def __init__(self, *args, **kwargs):
        super(GIVTranslator, self).__init__(*args, **kwargs)
        self.raw_datasets = None
30

31
    def _parse_file_path(self, input_path):
32
33
        pass

Somnath, Suhas's avatar
Somnath, Suhas committed
34
35
36
37
38
39
40
41
42
43
44
45
46
47
    def translate(self, parm_path):
        """      
        The main function that translates the provided file into a .h5 file
        
        Parameters
        ------------
        parm_path : string / unicode
            Absolute file path of the parameters .mat file. 
            
        Returns
        ----------
        h5_path : string / unicode
            Absolute path of the translated h5 file
        """
48
        parm_path = path.abspath(parm_path)
Somnath, Suhas's avatar
Somnath, Suhas committed
49
        parm_dict, excit_wfm = self._read_parms(parm_path)
Somnath, Suhas's avatar
Somnath, Suhas committed
50
51
        folder_path, base_name = path.split(parm_path)
        waste, base_name = path.split(folder_path)
52
53
54
55

        # Until a better method is provided....
        with h5py.File(path.join(folder_path, 'line_1.mat'), 'r') as h5_mat_line_1:
            num_ai_chans = h5_mat_line_1['data'].shape[1]
Somnath, Suhas's avatar
Somnath, Suhas committed
56
        
57
        h5_path = path.join(folder_path, base_name+'.h5')
Somnath, Suhas's avatar
Somnath, Suhas committed
58
59
        if path.exists(h5_path):
            remove(h5_path)
Somnath, Suhas's avatar
Somnath, Suhas committed
60

61
        with h5py.File(h5_path) as h5_f:
Somnath, Suhas's avatar
Somnath, Suhas committed
62

63
            h5_meas_grp = create_indexed_group(h5_f, 'Measurement')
64
            global_parms = dict()
65
66
            global_parms.update({'data_type': 'gIV', 'translator': 'gIV'})
            write_simple_attrs(h5_meas_grp, global_parms)
Somnath, Suhas's avatar
Somnath, Suhas committed
67

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
            # Only prepare the instructions for the dimensions here
            spec_dims = Dimension('Bias', 'V', excit_wfm)
            pos_dims = Dimension('Y', 'm', np.linspace(0, parm_dict['grid_scan_height_[m]'],
                                                       parm_dict['grid_num_rows']))

            self.raw_datasets = list()

            for chan_index in range(num_ai_chans):

                h5_chan_grp = create_indexed_group(h5_meas_grp, 'Channel')
                write_simple_attrs(h5_chan_grp, parm_dict)
                """
                Minimize file size to the extent possible.
                DAQs are rated at 16 bit so float16 should be most appropriate.
                For some reason, compression is effective only on time series data
                """
                h5_raw = write_main_dataset(h5_chan_grp, (parm_dict['grid_num_rows'], excit_wfm.size), 'Raw_Data',
                                            'Current',
                                            '1E-{} A'.format(parm_dict['IO_amplifier_gain']), pos_dims, spec_dims,
                                            dtype=np.float16, chunks=(1, excit_wfm.size), compression='gzip')

                self.raw_datasets.append(h5_raw)

            # Now that the N channels have been made, populate them with the actual data....
            self._read_data(parm_dict, folder_path)
Somnath, Suhas's avatar
Somnath, Suhas committed
93
94
95
96
97

        return h5_path

    def _read_data(self, parm_dict, folder_path):
        """
98
        Reads raw data and populates the h5 datasets
Somnath, Suhas's avatar
Somnath, Suhas committed
99
100
101
102
103
104
105
106

        Parameters
        ----------
        parm_dict : Dictionary
            dictionary containing parameters for this data
        folder_path : string / unicode
            Absolute path of folder containing the data
        """
107
108
109
110
111
        if parm_dict['excitation_extra_pts'] == 0:
            main_data = slice(parm_dict['excitation_pulse_points'], None)
        else:
            main_data = slice(parm_dict['excitation_pulse_points'], -1 * parm_dict['excitation_extra_pts'])

112
        for line_ind in range(parm_dict['grid_num_rows']):
Somnath, Suhas's avatar
Somnath, Suhas committed
113
            if line_ind % np.round(parm_dict['grid_num_rows']/10) == 0:
114
115
                print('Reading data in line {} of {}'.format(line_ind+1, parm_dict['grid_num_rows']))
            file_path = path.join(folder_path, 'line_'+str(line_ind+1)+'.mat')
Somnath, Suhas's avatar
Somnath, Suhas committed
116
            if path.exists(file_path):
117
118
119
120
121
122
123
124
125
                with h5py.File(file_path, 'r') as h5_f:
                    h5_data = h5_f['data']
                    if h5_data.shape[0] >= parm_dict['excitation_length'] and \
                            h5_data.shape[1] == len(self.raw_datasets):
                        for chan, h5_chan in enumerate(self.raw_datasets):
                            h5_chan[line_ind, :] = np.float16(h5_data[main_data, chan])
                            h5_chan.file.flush()
                    else:
                        warn('No data found for Line '+str(line_ind))
Somnath, Suhas's avatar
Somnath, Suhas committed
126
127
            else:
                warn('File not found for: line '+str(line_ind))
128
        print('Finished reading all data!')
129

Somnath, Suhas's avatar
Somnath, Suhas committed
130
    @staticmethod
Somnath, Suhas's avatar
Somnath, Suhas committed
131
    def _read_parms(parm_path):
Somnath, Suhas's avatar
Somnath, Suhas committed
132
133
134
135
        """
        Copies experimental parameters from the .mat file to a dictionary
        
        Parameters
136
        ----------
Somnath, Suhas's avatar
Somnath, Suhas committed
137
138
139
140
        parm_path : string / unicode
            Absolute path of the parameters file
        
        Returns 
141
        -------
Somnath, Suhas's avatar
Somnath, Suhas committed
142
143
144
145
146
        parm_dict : dictionary
            Dictionary containing all relevant parameters
        excit_wfm : 1d numpy float array
            Excitation waveform
        """
147
148
149
150
151
152
153
        with h5py.File(parm_path, 'r') as h5_f:
            parm_dict = dict()

            parm_dict['IO_samp_rate_[Hz]'] = np.uint32(h5_f['samp_rate'][0][0])
            parm_dict['IO_amplifier_gain'] = np.uint32(h5_f['amp_gain'][0][0])

            parm_dict['excitation_frequency_[Hz]'] = np.float32(h5_f['frequency'][0][0])
154
155
156
            #parm_dict['excitation_amplitude_[V]'] = np.float32(h5_f['amplitude'][0][0])
            #parm_dict['excitation_offset_[V]'] = np.float32(h5_f['offset'][0][0])

157
            excit_wfm = np.float32(np.squeeze(h5_f['excit_wfm'].value))
158
159
160
161
162
            #in case these keys are not present (in some versions they are not)
            try:
                parm_dict['excitation_amplitude_[V]'] = np.float32(h5_f['amplitude'][0][0])
                parm_dict['excitation_offset_[V]'] = np.float32(h5_f['offset'][0][0])
            except KeyError:
ramav87's avatar
ramav87 committed
163
164
                parm_dict['excitation_offset_[V]'] = excit_wfm[0] #need a better way to handle this.
                parm_dict['excitation_amplitude_[V]'] = np.max(excit_wfm[:]) #risky for non sine waves
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

            # Make sure to truncate the data to the point when the
            pts_per_cycle = int(np.round(1.0*parm_dict['IO_samp_rate_[Hz]']/parm_dict['excitation_frequency_[Hz]']))
            extra_pts = len(excit_wfm) % pts_per_cycle
            parm_dict['excitation_extra_pts'] = extra_pts

            """ New versions could have used a pulse at the beginning of the line to "wake up" the material.
            This pulse is always an integer number of cycles long
            For now, let's remove this section from the excitation waveform and data"""

            # This pulse may or may not have been used:
            try:
                pulse_duration = np.float32(h5_f['pulse_duration'][0][0])
                pulse_height = np.float32(h5_f['pulse_height'][0][0])
            except KeyError:
                pulse_duration = 0.0
                pulse_height = 0.0
            if pulse_duration == 0.0:
                pulse_height = 0.0

            parm_dict['excitation_pulse_height_[V]'] = np.float32(pulse_height)
            parm_dict['excitation_pulse_time_[sec]'] = np.float32(pulse_duration)
            pulse_points = int(np.round(pulse_duration * parm_dict['IO_samp_rate_[Hz]']))
            parm_dict['excitation_pulse_points'] = np.uint32(pulse_points)

            line_time = np.float32(h5_f['line_time'][0][0]) - pulse_duration
            excess_time = line_time - 1.0*extra_pts/parm_dict['IO_samp_rate_[Hz]']
            parm_dict['excitation_duration_[sec]'] = line_time - excess_time
            if extra_pts > 0:
                excit_wfm = excit_wfm[pulse_points:-extra_pts]
            else:
                excit_wfm = excit_wfm[pulse_points:]
            parm_dict['excitation_length'] = len(excit_wfm)
198

199
200
            parm_dict['grid_num_rows'] = np.uint32(h5_f['num_lines'][0][0])
            parm_dict['grid_num_cols'] = np.uint32(np.floor(len(excit_wfm) / pts_per_cycle))
Somnath, Suhas's avatar
Somnath, Suhas committed
201

202
203
204
            parm_dict['grid_scan_height_[m]'] = np.float32(h5_f['scan_height'][0][0])
            parm_dict['grid_scan_width_[m]'] = np.float32(h5_f['scan_width'][0][0])
            parm_dict['grid_scan_speed_[ms-1]'] = np.float32(h5_f['scan_speed'][0][0])
205
206

        return parm_dict, excit_wfm