corems.transient.calc.TransientCalc

  1import gc
  2import warnings
  3
  4from numpy import (
  5    arange,
  6    blackman,
  7    ceil,
  8    fft,
  9    hamming,
 10    hanning,
 11    kaiser,
 12    linspace,
 13    log2,
 14    pi,
 15    power,
 16    sin,
 17    sqrt,
 18    where,
 19    zeros,
 20)
 21
 22__author__ = "Yuri E. Corilo"
 23__date__ = "Jun 12, 2019"
 24
 25
 26class TransientCalculations(object):
 27    """Transient Calculations
 28
 29    Parameters
 30    ----------
 31    parameters : corems.transient.parameters.TransientParameters
 32        The transient parameters
 33    bandwidth : float
 34        The bandwidth of the transient (Hz)
 35    number_data_points : int
 36        The number of data points of the transient
 37    exc_low_freq : float
 38        The low frequency of the excitation (Hz)
 39    exc_high_freq : float
 40        The high frequency of the excitation (Hz)
 41
 42    Attributes
 43    ----------
 44    parameters : corems.transient.parameters.TransientParameters
 45        The transient parameters
 46    bandwidth : float
 47        The bandwidth of the transient (Hz)
 48    number_data_points : int
 49        The number of data points of the transient
 50    exc_low_freq : float
 51        The low frequency of the excitation (Hz)
 52    exc_high_freq : float
 53        The high frequency of the excitation (Hz)
 54
 55    Methods
 56    -------
 57    * cal_transient_time().
 58        Calculate the time domain length of the transient
 59    * zero_fill(transient).
 60        Zero fill the transient
 61    * truncation(transient).
 62        Truncate the transient
 63    * apodization(transient).
 64        Apodization of the transient
 65    * calculate_frequency_domain(number_data_points).
 66        Calculate the frequency domain (axis) of the transient
 67    * cut_freq_domain(freqdomain_X, freqdomain_Y).
 68        Cut the frequency domain of the transient
 69    * phase_and_absorption_mode_ft().
 70        [Not Functional] Produce a phased absorption mode FT spectrum
 71    * magnitude_mode_ft(transient).
 72        Perform magnitude mode FT of the transient
 73    * correct_dc_offset().
 74        [Not Yet Implemented] Correct the DC offset of the transient
 75
 76    """
 77
 78    def cal_transient_time(self):
 79        """Calculate the time domain length of the transient
 80
 81        Returns
 82        -------
 83        float
 84            The time domain length of the transient (s)
 85        """
 86        return (1 / self.bandwidth) * ((self.number_data_points) / 2)
 87
 88    def zero_fill(self, transient):
 89        """Zero fill the transient
 90
 91        Parameters
 92        ----------
 93        transient : numpy.ndarray
 94            The transient data points
 95
 96        Returns
 97        -------
 98        numpy.ndarray
 99            The transient data points zerofilled
100
101        Notes
102        -----
103        The number of zero fills is defined by the transient parameter number_of_zero_fills.
104        The function first calculate the next power of two of the transient length and zero fills to that length, to take advantage of FFT algorithm.
105            If the parameter next_power_of_two is set to False, the function will zero fill to the length of the original transient times the number of zero fills
106
107        """
108        if self.parameters.next_power_of_two:
109            exponent = int(
110                ceil(log2(len(transient) * (self.parameters.number_of_zero_fills + 1)))
111            )
112            zeros_filled_transient = zeros(2**exponent)
113        else:
114            zeros_filled_transient = zeros(
115                len(transient) * (self.parameters.number_of_zero_fills + 1)
116            )
117
118        zeros_filled_transient[0 : len(transient)] = transient
119
120        del transient
121
122        gc.collect()
123
124        return zeros_filled_transient
125
126    def truncation(self, transient):
127        """Truncate the transient
128
129        Parameters
130        ----------
131        transient : numpy.ndarray
132            The transient data points
133
134        Returns
135        -------
136        numpy.ndarray
137            The truncated transient data points
138
139        Notes
140        -----
141        The number of truncations is defined by the transient parameter number_of_truncations
142        """
143
144        data_count = len(transient)
145
146        for _ in range(self.parameters.number_of_truncations):
147            data_count = int(data_count / 2)
148
149        time_domain_truncated = transient[0:data_count]
150
151        del transient
152
153        gc.collect()
154
155        return time_domain_truncated
156
157    def apodization(self, transient):
158        """Apodization of the transient
159
160        Parameters
161        ----------
162        transient : numpy.ndarray
163            The transient data points
164
165        Returns
166        -------
167        numpy.ndarray
168            The apodized transient data points
169
170        Notes
171        -----
172        The apodization method is defined by the transient parameter apodization_method.
173        The following apodization methods are available:
174            Hamming,
175            Hanning,
176            Blackman,
177            Full-Sine,
178            Half-Sine,
179            Kaiser,
180            Half-Kaiser.
181
182        For Kaiser and Half-Kaiser, an additional parameter 'beta' is required, set by the transient parameter kaiser_beta.
183
184        """
185
186        apodi_method = self.parameters.apodization_method
187        beta = self.parameters.kaiser_beta
188
189        length = len(transient)
190
191        if apodi_method == "Hamming":
192            H_function = hamming(length)
193        elif apodi_method == "Hanning":
194            H_function = hanning(length)
195        elif apodi_method == "Blackman":
196            H_function = blackman(length)
197        elif apodi_method == "Full-Sine":
198            H_function = sin(linspace(0, pi, num=length))
199        elif apodi_method == "Half-Sine":
200            H_function = sin(linspace((pi / 2), 0, num=length))
201        elif apodi_method == "Kaiser":
202            H_function = kaiser(length, beta)
203        elif apodi_method == "Half-Kaiser":
204            H_function = kaiser(length * 2, beta)[length:]
205
206        S_x = transient * H_function
207
208        del transient
209        gc.collect()
210
211        return S_x
212
213    def calculate_frequency_domain(self, number_data_points):
214        """Calculate the frequency domain (axis) of the transient
215
216        Parameters
217        ----------
218        number_data_points : int
219            The number of data points of the transient
220
221        Returns
222        -------
223        numpy.ndarray
224            The frequency domain of the transient (Hz)
225
226
227        """
228
229        qntpoints = arange(0, (number_data_points))
230
231        factor_distancy = (self.bandwidth) / (number_data_points)
232
233        frequency_domain = qntpoints * factor_distancy
234
235        del qntpoints
236        del factor_distancy
237        gc.collect()
238
239        return frequency_domain
240
241    def cut_freq_domain(self, freqdomain_X, freqdomain_Y):
242        """Cut the frequency domain of the transient
243
244        Parameters
245        ----------
246        freqdomain_X : numpy.ndarray
247            The frequency domain of the transient (Hz)
248        freqdomain_Y : numpy.ndarray
249            The frequency domain of the transient (Hz)
250
251        Returns
252        -------
253        numpy.ndarray
254            The frequency domain of the transient (Hz)
255        numpy.ndarray
256            The frequency domain of the transient (Hz)
257
258
259        """
260        # If the mw_low and mw_high are set, the frequency domain is cut to the mw range
261        # this accounts for the detection settings, not the excitation settings.
262        # TODO: Implement this - right now the f to mz function is in the ms class, not the transient class, so it doesnt work.
263        # if (self._mw_low != 0) & (self._mw_high != 0):
264        #    high_freq = self._f_to_mz(self._mw_high)
265        #    low_freq = self._f_to_mz(self._mw_low)
266        #
267        #    final =  where(freqdomain_X < high_freq)[-1][-1]
268        #      start =  where(freqdomain_X > low_freq)[0][0]
269        # else:
270        if self._qpd_enabled == 1:
271            low_freq = self._exc_low_freq * 2
272            high_freq = self._exc_high_freq * 2
273        else:
274            low_freq = self._exc_low_freq
275            high_freq = self._exc_high_freq
276
277        if self._exc_low_freq > self._exc_high_freq:
278            # TODO: This needs to be tested
279            # I'm not sure that this is relevant anyway - the excitation pulse is ramped in frequency but the detection is simulatenous
280            warnings.warn("This is not tested. Please check the results.")
281            final = where(freqdomain_X > low_freq)[0][0]
282            start = where(freqdomain_X > high_freq)[0][0]
283
284        else:
285            final = where(freqdomain_X < high_freq)[-1][-1]
286            start = where(freqdomain_X > low_freq)[0][0]
287
288        return freqdomain_X[start:final], freqdomain_Y[start:final]
289        # del freqdomain_X, freqdomain_Y
290        # gc.collect()
291
292    def phase_and_absorption_mode_ft(self):
293        """[Not Functional] Produce a phased absorption mode FT spectrum"""
294        # anyone wants to play with this part please make yourself comfortable. I will:
295        pass
296
297    def perform_magniture_mode_ft(self, transient):
298        """Perform magnitude mode FT of the transient
299
300        Parameters
301        ----------
302        transient : numpy.ndarray
303            The transient data points
304
305        Returns
306        -------
307        numpy.ndarray
308            The frequency domain of the transient (Hz)
309        numpy.ndarray
310            The magnitude of the transient (a.u.)
311
312
313        """
314
315        A = fft.rfft(transient)
316
317        # A = fft.fft(transient)
318        # A = A[0:int(len(A)/2)]
319
320        factor = int(self.parameters.number_of_zero_fills - 1)
321        if self.parameters.number_of_zero_fills:
322            if self.parameters.number_of_zero_fills == 1:
323                factor = 1 / 2
324
325            else:
326                factor = int(1 / self.parameters.number_of_zero_fills + 1)
327
328            Max_index = int(len(A) / factor)
329
330        else:
331            Max_index = int(len(A))
332
333        A = A[0:Max_index]
334
335        datapoints = len(A)
336
337        freqdomain_X = self.calculate_frequency_domain(datapoints)
338
339        magnitude_Y = sqrt((power(A.real, 2)) + (power(A.imag, 2)))
340
341        freqdomain_X_cut, magnitude_Y_cut = self.cut_freq_domain(
342            freqdomain_X, magnitude_Y
343        )
344
345        del transient
346        # del freqdomain_X
347        # del magnitude_Y
348        gc.collect()
349
350        return freqdomain_X_cut, magnitude_Y_cut
351
352    def correct_dc_offset(self):
353        """[Not Yet Implemented] Correct the DC offset of the transient
354
355        A simple baseline correction to compensate for a DC offset in the recorded transient.
356        Not implemented.
357
358        """
359        pass
class TransientCalculations:
 27class TransientCalculations(object):
 28    """Transient Calculations
 29
 30    Parameters
 31    ----------
 32    parameters : corems.transient.parameters.TransientParameters
 33        The transient parameters
 34    bandwidth : float
 35        The bandwidth of the transient (Hz)
 36    number_data_points : int
 37        The number of data points of the transient
 38    exc_low_freq : float
 39        The low frequency of the excitation (Hz)
 40    exc_high_freq : float
 41        The high frequency of the excitation (Hz)
 42
 43    Attributes
 44    ----------
 45    parameters : corems.transient.parameters.TransientParameters
 46        The transient parameters
 47    bandwidth : float
 48        The bandwidth of the transient (Hz)
 49    number_data_points : int
 50        The number of data points of the transient
 51    exc_low_freq : float
 52        The low frequency of the excitation (Hz)
 53    exc_high_freq : float
 54        The high frequency of the excitation (Hz)
 55
 56    Methods
 57    -------
 58    * cal_transient_time().
 59        Calculate the time domain length of the transient
 60    * zero_fill(transient).
 61        Zero fill the transient
 62    * truncation(transient).
 63        Truncate the transient
 64    * apodization(transient).
 65        Apodization of the transient
 66    * calculate_frequency_domain(number_data_points).
 67        Calculate the frequency domain (axis) of the transient
 68    * cut_freq_domain(freqdomain_X, freqdomain_Y).
 69        Cut the frequency domain of the transient
 70    * phase_and_absorption_mode_ft().
 71        [Not Functional] Produce a phased absorption mode FT spectrum
 72    * magnitude_mode_ft(transient).
 73        Perform magnitude mode FT of the transient
 74    * correct_dc_offset().
 75        [Not Yet Implemented] Correct the DC offset of the transient
 76
 77    """
 78
 79    def cal_transient_time(self):
 80        """Calculate the time domain length of the transient
 81
 82        Returns
 83        -------
 84        float
 85            The time domain length of the transient (s)
 86        """
 87        return (1 / self.bandwidth) * ((self.number_data_points) / 2)
 88
 89    def zero_fill(self, transient):
 90        """Zero fill the transient
 91
 92        Parameters
 93        ----------
 94        transient : numpy.ndarray
 95            The transient data points
 96
 97        Returns
 98        -------
 99        numpy.ndarray
100            The transient data points zerofilled
101
102        Notes
103        -----
104        The number of zero fills is defined by the transient parameter number_of_zero_fills.
105        The function first calculate the next power of two of the transient length and zero fills to that length, to take advantage of FFT algorithm.
106            If the parameter next_power_of_two is set to False, the function will zero fill to the length of the original transient times the number of zero fills
107
108        """
109        if self.parameters.next_power_of_two:
110            exponent = int(
111                ceil(log2(len(transient) * (self.parameters.number_of_zero_fills + 1)))
112            )
113            zeros_filled_transient = zeros(2**exponent)
114        else:
115            zeros_filled_transient = zeros(
116                len(transient) * (self.parameters.number_of_zero_fills + 1)
117            )
118
119        zeros_filled_transient[0 : len(transient)] = transient
120
121        del transient
122
123        gc.collect()
124
125        return zeros_filled_transient
126
127    def truncation(self, transient):
128        """Truncate the transient
129
130        Parameters
131        ----------
132        transient : numpy.ndarray
133            The transient data points
134
135        Returns
136        -------
137        numpy.ndarray
138            The truncated transient data points
139
140        Notes
141        -----
142        The number of truncations is defined by the transient parameter number_of_truncations
143        """
144
145        data_count = len(transient)
146
147        for _ in range(self.parameters.number_of_truncations):
148            data_count = int(data_count / 2)
149
150        time_domain_truncated = transient[0:data_count]
151
152        del transient
153
154        gc.collect()
155
156        return time_domain_truncated
157
158    def apodization(self, transient):
159        """Apodization of the transient
160
161        Parameters
162        ----------
163        transient : numpy.ndarray
164            The transient data points
165
166        Returns
167        -------
168        numpy.ndarray
169            The apodized transient data points
170
171        Notes
172        -----
173        The apodization method is defined by the transient parameter apodization_method.
174        The following apodization methods are available:
175            Hamming,
176            Hanning,
177            Blackman,
178            Full-Sine,
179            Half-Sine,
180            Kaiser,
181            Half-Kaiser.
182
183        For Kaiser and Half-Kaiser, an additional parameter 'beta' is required, set by the transient parameter kaiser_beta.
184
185        """
186
187        apodi_method = self.parameters.apodization_method
188        beta = self.parameters.kaiser_beta
189
190        length = len(transient)
191
192        if apodi_method == "Hamming":
193            H_function = hamming(length)
194        elif apodi_method == "Hanning":
195            H_function = hanning(length)
196        elif apodi_method == "Blackman":
197            H_function = blackman(length)
198        elif apodi_method == "Full-Sine":
199            H_function = sin(linspace(0, pi, num=length))
200        elif apodi_method == "Half-Sine":
201            H_function = sin(linspace((pi / 2), 0, num=length))
202        elif apodi_method == "Kaiser":
203            H_function = kaiser(length, beta)
204        elif apodi_method == "Half-Kaiser":
205            H_function = kaiser(length * 2, beta)[length:]
206
207        S_x = transient * H_function
208
209        del transient
210        gc.collect()
211
212        return S_x
213
214    def calculate_frequency_domain(self, number_data_points):
215        """Calculate the frequency domain (axis) of the transient
216
217        Parameters
218        ----------
219        number_data_points : int
220            The number of data points of the transient
221
222        Returns
223        -------
224        numpy.ndarray
225            The frequency domain of the transient (Hz)
226
227
228        """
229
230        qntpoints = arange(0, (number_data_points))
231
232        factor_distancy = (self.bandwidth) / (number_data_points)
233
234        frequency_domain = qntpoints * factor_distancy
235
236        del qntpoints
237        del factor_distancy
238        gc.collect()
239
240        return frequency_domain
241
242    def cut_freq_domain(self, freqdomain_X, freqdomain_Y):
243        """Cut the frequency domain of the transient
244
245        Parameters
246        ----------
247        freqdomain_X : numpy.ndarray
248            The frequency domain of the transient (Hz)
249        freqdomain_Y : numpy.ndarray
250            The frequency domain of the transient (Hz)
251
252        Returns
253        -------
254        numpy.ndarray
255            The frequency domain of the transient (Hz)
256        numpy.ndarray
257            The frequency domain of the transient (Hz)
258
259
260        """
261        # If the mw_low and mw_high are set, the frequency domain is cut to the mw range
262        # this accounts for the detection settings, not the excitation settings.
263        # TODO: Implement this - right now the f to mz function is in the ms class, not the transient class, so it doesnt work.
264        # if (self._mw_low != 0) & (self._mw_high != 0):
265        #    high_freq = self._f_to_mz(self._mw_high)
266        #    low_freq = self._f_to_mz(self._mw_low)
267        #
268        #    final =  where(freqdomain_X < high_freq)[-1][-1]
269        #      start =  where(freqdomain_X > low_freq)[0][0]
270        # else:
271        if self._qpd_enabled == 1:
272            low_freq = self._exc_low_freq * 2
273            high_freq = self._exc_high_freq * 2
274        else:
275            low_freq = self._exc_low_freq
276            high_freq = self._exc_high_freq
277
278        if self._exc_low_freq > self._exc_high_freq:
279            # TODO: This needs to be tested
280            # I'm not sure that this is relevant anyway - the excitation pulse is ramped in frequency but the detection is simulatenous
281            warnings.warn("This is not tested. Please check the results.")
282            final = where(freqdomain_X > low_freq)[0][0]
283            start = where(freqdomain_X > high_freq)[0][0]
284
285        else:
286            final = where(freqdomain_X < high_freq)[-1][-1]
287            start = where(freqdomain_X > low_freq)[0][0]
288
289        return freqdomain_X[start:final], freqdomain_Y[start:final]
290        # del freqdomain_X, freqdomain_Y
291        # gc.collect()
292
293    def phase_and_absorption_mode_ft(self):
294        """[Not Functional] Produce a phased absorption mode FT spectrum"""
295        # anyone wants to play with this part please make yourself comfortable. I will:
296        pass
297
298    def perform_magniture_mode_ft(self, transient):
299        """Perform magnitude mode FT of the transient
300
301        Parameters
302        ----------
303        transient : numpy.ndarray
304            The transient data points
305
306        Returns
307        -------
308        numpy.ndarray
309            The frequency domain of the transient (Hz)
310        numpy.ndarray
311            The magnitude of the transient (a.u.)
312
313
314        """
315
316        A = fft.rfft(transient)
317
318        # A = fft.fft(transient)
319        # A = A[0:int(len(A)/2)]
320
321        factor = int(self.parameters.number_of_zero_fills - 1)
322        if self.parameters.number_of_zero_fills:
323            if self.parameters.number_of_zero_fills == 1:
324                factor = 1 / 2
325
326            else:
327                factor = int(1 / self.parameters.number_of_zero_fills + 1)
328
329            Max_index = int(len(A) / factor)
330
331        else:
332            Max_index = int(len(A))
333
334        A = A[0:Max_index]
335
336        datapoints = len(A)
337
338        freqdomain_X = self.calculate_frequency_domain(datapoints)
339
340        magnitude_Y = sqrt((power(A.real, 2)) + (power(A.imag, 2)))
341
342        freqdomain_X_cut, magnitude_Y_cut = self.cut_freq_domain(
343            freqdomain_X, magnitude_Y
344        )
345
346        del transient
347        # del freqdomain_X
348        # del magnitude_Y
349        gc.collect()
350
351        return freqdomain_X_cut, magnitude_Y_cut
352
353    def correct_dc_offset(self):
354        """[Not Yet Implemented] Correct the DC offset of the transient
355
356        A simple baseline correction to compensate for a DC offset in the recorded transient.
357        Not implemented.
358
359        """
360        pass

Transient Calculations

Parameters
  • parameters (corems.transient.parameters.TransientParameters): The transient parameters
  • bandwidth (float): The bandwidth of the transient (Hz)
  • number_data_points (int): The number of data points of the transient
  • exc_low_freq (float): The low frequency of the excitation (Hz)
  • exc_high_freq (float): The high frequency of the excitation (Hz)
Attributes
  • parameters (corems.transient.parameters.TransientParameters): The transient parameters
  • bandwidth (float): The bandwidth of the transient (Hz)
  • number_data_points (int): The number of data points of the transient
  • exc_low_freq (float): The low frequency of the excitation (Hz)
  • exc_high_freq (float): The high frequency of the excitation (Hz)
Methods
  • cal_transient_time(). Calculate the time domain length of the transient
  • zero_fill(transient). Zero fill the transient
  • truncation(transient). Truncate the transient
  • apodization(transient). Apodization of the transient
  • calculate_frequency_domain(number_data_points). Calculate the frequency domain (axis) of the transient
  • cut_freq_domain(freqdomain_X, freqdomain_Y). Cut the frequency domain of the transient
  • phase_and_absorption_mode_ft(). [Not Functional] Produce a phased absorption mode FT spectrum
  • magnitude_mode_ft(transient). Perform magnitude mode FT of the transient
  • correct_dc_offset(). [Not Yet Implemented] Correct the DC offset of the transient
def cal_transient_time(self):
79    def cal_transient_time(self):
80        """Calculate the time domain length of the transient
81
82        Returns
83        -------
84        float
85            The time domain length of the transient (s)
86        """
87        return (1 / self.bandwidth) * ((self.number_data_points) / 2)

Calculate the time domain length of the transient

Returns
  • float: The time domain length of the transient (s)
def zero_fill(self, transient):
 89    def zero_fill(self, transient):
 90        """Zero fill the transient
 91
 92        Parameters
 93        ----------
 94        transient : numpy.ndarray
 95            The transient data points
 96
 97        Returns
 98        -------
 99        numpy.ndarray
100            The transient data points zerofilled
101
102        Notes
103        -----
104        The number of zero fills is defined by the transient parameter number_of_zero_fills.
105        The function first calculate the next power of two of the transient length and zero fills to that length, to take advantage of FFT algorithm.
106            If the parameter next_power_of_two is set to False, the function will zero fill to the length of the original transient times the number of zero fills
107
108        """
109        if self.parameters.next_power_of_two:
110            exponent = int(
111                ceil(log2(len(transient) * (self.parameters.number_of_zero_fills + 1)))
112            )
113            zeros_filled_transient = zeros(2**exponent)
114        else:
115            zeros_filled_transient = zeros(
116                len(transient) * (self.parameters.number_of_zero_fills + 1)
117            )
118
119        zeros_filled_transient[0 : len(transient)] = transient
120
121        del transient
122
123        gc.collect()
124
125        return zeros_filled_transient

Zero fill the transient

Parameters
  • transient (numpy.ndarray): The transient data points
Returns
  • numpy.ndarray: The transient data points zerofilled
Notes

The number of zero fills is defined by the transient parameter number_of_zero_fills. The function first calculate the next power of two of the transient length and zero fills to that length, to take advantage of FFT algorithm. If the parameter next_power_of_two is set to False, the function will zero fill to the length of the original transient times the number of zero fills

def truncation(self, transient):
127    def truncation(self, transient):
128        """Truncate the transient
129
130        Parameters
131        ----------
132        transient : numpy.ndarray
133            The transient data points
134
135        Returns
136        -------
137        numpy.ndarray
138            The truncated transient data points
139
140        Notes
141        -----
142        The number of truncations is defined by the transient parameter number_of_truncations
143        """
144
145        data_count = len(transient)
146
147        for _ in range(self.parameters.number_of_truncations):
148            data_count = int(data_count / 2)
149
150        time_domain_truncated = transient[0:data_count]
151
152        del transient
153
154        gc.collect()
155
156        return time_domain_truncated

Truncate the transient

Parameters
  • transient (numpy.ndarray): The transient data points
Returns
  • numpy.ndarray: The truncated transient data points
Notes

The number of truncations is defined by the transient parameter number_of_truncations

def apodization(self, transient):
158    def apodization(self, transient):
159        """Apodization of the transient
160
161        Parameters
162        ----------
163        transient : numpy.ndarray
164            The transient data points
165
166        Returns
167        -------
168        numpy.ndarray
169            The apodized transient data points
170
171        Notes
172        -----
173        The apodization method is defined by the transient parameter apodization_method.
174        The following apodization methods are available:
175            Hamming,
176            Hanning,
177            Blackman,
178            Full-Sine,
179            Half-Sine,
180            Kaiser,
181            Half-Kaiser.
182
183        For Kaiser and Half-Kaiser, an additional parameter 'beta' is required, set by the transient parameter kaiser_beta.
184
185        """
186
187        apodi_method = self.parameters.apodization_method
188        beta = self.parameters.kaiser_beta
189
190        length = len(transient)
191
192        if apodi_method == "Hamming":
193            H_function = hamming(length)
194        elif apodi_method == "Hanning":
195            H_function = hanning(length)
196        elif apodi_method == "Blackman":
197            H_function = blackman(length)
198        elif apodi_method == "Full-Sine":
199            H_function = sin(linspace(0, pi, num=length))
200        elif apodi_method == "Half-Sine":
201            H_function = sin(linspace((pi / 2), 0, num=length))
202        elif apodi_method == "Kaiser":
203            H_function = kaiser(length, beta)
204        elif apodi_method == "Half-Kaiser":
205            H_function = kaiser(length * 2, beta)[length:]
206
207        S_x = transient * H_function
208
209        del transient
210        gc.collect()
211
212        return S_x

Apodization of the transient

Parameters
  • transient (numpy.ndarray): The transient data points
Returns
  • numpy.ndarray: The apodized transient data points
Notes

The apodization method is defined by the transient parameter apodization_method. The following apodization methods are available: Hamming, Hanning, Blackman, Full-Sine, Half-Sine, Kaiser, Half-Kaiser.

For Kaiser and Half-Kaiser, an additional parameter 'beta' is required, set by the transient parameter kaiser_beta.

def calculate_frequency_domain(self, number_data_points):
214    def calculate_frequency_domain(self, number_data_points):
215        """Calculate the frequency domain (axis) of the transient
216
217        Parameters
218        ----------
219        number_data_points : int
220            The number of data points of the transient
221
222        Returns
223        -------
224        numpy.ndarray
225            The frequency domain of the transient (Hz)
226
227
228        """
229
230        qntpoints = arange(0, (number_data_points))
231
232        factor_distancy = (self.bandwidth) / (number_data_points)
233
234        frequency_domain = qntpoints * factor_distancy
235
236        del qntpoints
237        del factor_distancy
238        gc.collect()
239
240        return frequency_domain

Calculate the frequency domain (axis) of the transient

Parameters
  • number_data_points (int): The number of data points of the transient
Returns
  • numpy.ndarray: The frequency domain of the transient (Hz)
def cut_freq_domain(self, freqdomain_X, freqdomain_Y):
242    def cut_freq_domain(self, freqdomain_X, freqdomain_Y):
243        """Cut the frequency domain of the transient
244
245        Parameters
246        ----------
247        freqdomain_X : numpy.ndarray
248            The frequency domain of the transient (Hz)
249        freqdomain_Y : numpy.ndarray
250            The frequency domain of the transient (Hz)
251
252        Returns
253        -------
254        numpy.ndarray
255            The frequency domain of the transient (Hz)
256        numpy.ndarray
257            The frequency domain of the transient (Hz)
258
259
260        """
261        # If the mw_low and mw_high are set, the frequency domain is cut to the mw range
262        # this accounts for the detection settings, not the excitation settings.
263        # TODO: Implement this - right now the f to mz function is in the ms class, not the transient class, so it doesnt work.
264        # if (self._mw_low != 0) & (self._mw_high != 0):
265        #    high_freq = self._f_to_mz(self._mw_high)
266        #    low_freq = self._f_to_mz(self._mw_low)
267        #
268        #    final =  where(freqdomain_X < high_freq)[-1][-1]
269        #      start =  where(freqdomain_X > low_freq)[0][0]
270        # else:
271        if self._qpd_enabled == 1:
272            low_freq = self._exc_low_freq * 2
273            high_freq = self._exc_high_freq * 2
274        else:
275            low_freq = self._exc_low_freq
276            high_freq = self._exc_high_freq
277
278        if self._exc_low_freq > self._exc_high_freq:
279            # TODO: This needs to be tested
280            # I'm not sure that this is relevant anyway - the excitation pulse is ramped in frequency but the detection is simulatenous
281            warnings.warn("This is not tested. Please check the results.")
282            final = where(freqdomain_X > low_freq)[0][0]
283            start = where(freqdomain_X > high_freq)[0][0]
284
285        else:
286            final = where(freqdomain_X < high_freq)[-1][-1]
287            start = where(freqdomain_X > low_freq)[0][0]
288
289        return freqdomain_X[start:final], freqdomain_Y[start:final]
290        # del freqdomain_X, freqdomain_Y
291        # gc.collect()

Cut the frequency domain of the transient

Parameters
  • freqdomain_X (numpy.ndarray): The frequency domain of the transient (Hz)
  • freqdomain_Y (numpy.ndarray): The frequency domain of the transient (Hz)
Returns
  • numpy.ndarray: The frequency domain of the transient (Hz)
  • numpy.ndarray: The frequency domain of the transient (Hz)
def phase_and_absorption_mode_ft(self):
293    def phase_and_absorption_mode_ft(self):
294        """[Not Functional] Produce a phased absorption mode FT spectrum"""
295        # anyone wants to play with this part please make yourself comfortable. I will:
296        pass

[Not Functional] Produce a phased absorption mode FT spectrum

def perform_magniture_mode_ft(self, transient):
298    def perform_magniture_mode_ft(self, transient):
299        """Perform magnitude mode FT of the transient
300
301        Parameters
302        ----------
303        transient : numpy.ndarray
304            The transient data points
305
306        Returns
307        -------
308        numpy.ndarray
309            The frequency domain of the transient (Hz)
310        numpy.ndarray
311            The magnitude of the transient (a.u.)
312
313
314        """
315
316        A = fft.rfft(transient)
317
318        # A = fft.fft(transient)
319        # A = A[0:int(len(A)/2)]
320
321        factor = int(self.parameters.number_of_zero_fills - 1)
322        if self.parameters.number_of_zero_fills:
323            if self.parameters.number_of_zero_fills == 1:
324                factor = 1 / 2
325
326            else:
327                factor = int(1 / self.parameters.number_of_zero_fills + 1)
328
329            Max_index = int(len(A) / factor)
330
331        else:
332            Max_index = int(len(A))
333
334        A = A[0:Max_index]
335
336        datapoints = len(A)
337
338        freqdomain_X = self.calculate_frequency_domain(datapoints)
339
340        magnitude_Y = sqrt((power(A.real, 2)) + (power(A.imag, 2)))
341
342        freqdomain_X_cut, magnitude_Y_cut = self.cut_freq_domain(
343            freqdomain_X, magnitude_Y
344        )
345
346        del transient
347        # del freqdomain_X
348        # del magnitude_Y
349        gc.collect()
350
351        return freqdomain_X_cut, magnitude_Y_cut

Perform magnitude mode FT of the transient

Parameters
  • transient (numpy.ndarray): The transient data points
Returns
  • numpy.ndarray: The frequency domain of the transient (Hz)
  • numpy.ndarray: The magnitude of the transient (a.u.)
def correct_dc_offset(self):
353    def correct_dc_offset(self):
354        """[Not Yet Implemented] Correct the DC offset of the transient
355
356        A simple baseline correction to compensate for a DC offset in the recorded transient.
357        Not implemented.
358
359        """
360        pass

[Not Yet Implemented] Correct the DC offset of the transient

A simple baseline correction to compensate for a DC offset in the recorded transient. Not implemented.