Ejemplo n.º 1
0
def compute_noise(minfreq, maxfreq, data_parent_folder, meas_folder, en_fig):

    # variables to be input
    # data_parent_folder : the folder for all datas
    # meas_folder        : the specific folder for one measurement
    # en_fig            : enable figure

    file_name_prefix = 'dat_'
    data_folder = (data_parent_folder + '/' + meas_folder + '/')
    fig_num = 200

    # variables from NMR settings
    (param_list, value_list) = data_parser.parse_info(data_folder,
                                                      'acqu.par')  # read file
    adcFreq = int(data_parser.find_value('adcFreq', param_list, value_list))
    nrPnts = int(data_parser.find_value('nrPnts', param_list, value_list))
    total_scan = int(
        data_parser.find_value('nrIterations', param_list, value_list))

    # parse file and remove DC component
    # data = np.zeros(nrPnts)
    for m in range(1, total_scan + 1):
        file_path = (data_folder + file_name_prefix + '{0:03d}'.format(m))
        # read the data from the file and store it in numpy array format
        one_scan = np.array(data_parser.read_data(file_path))
        one_scan = (one_scan - np.mean(one_scan)) / \
            total_scan  # remove DC component
        # data = data + one_scan

    spectx, specty = nmr_fft(one_scan, adcFreq, 0)
    fft_range = [
        i for i, value in enumerate(spectx)
        if (value >= minfreq and value <= maxfreq)
    ]  # limit fft display
    # standard deviation of the fft
    print('\t\tNOISE RMS = ' + '{0:.5f}'.format(np.std(specty[fft_range])))

    if en_fig:
        plt.ion()
        fig = plt.figure(fig_num)
        fig.clf()
        ax = fig.add_subplot(2, 1, 1)

        line1, = ax.plot(spectx[fft_range], specty[fft_range], 'r-')
        # ax.set_ylim(-50, 0)
        ax.set_xlabel('Frequency [MHz]')
        ax.set_ylabel('Amplitude [a.u.]')
        ax.set_title("Noise spectrum")
        ax.grid()

        ax = fig.add_subplot(2, 1, 2)
        line1, = ax.plot(one_scan, 'r-')
        ax.set_xlabel('Time')
        ax.set_ylabel('Amplitude [a.u.]')
        ax.set_title("Noise amplitude")
        ax.grid()

        fig.canvas.draw()
        fig.canvas.flush_events()
def compute_iterate(nmrObj, data_parent_folder, meas_folder, en_ext_param,
                    thetaref, echoref_avg, direct_read, datain, en_fig):

    data_folder = (data_parent_folder + '/' + meas_folder + '/')
    # variables from NMR settings
    (param_list, value_list) = data_parser.parse_info(data_folder,
                                                      'acqu.par')  # read file
    SpE = int(data_parser.find_value('nrPnts', param_list, value_list))
    NoE = int(data_parser.find_value('nrEchoes', param_list, value_list))
    en_ph_cycle_proc = data_parser.find_value('usePhaseCycle', param_list,
                                              value_list)
    tE = data_parser.find_value('echoTimeRun', param_list, value_list)
    Sf = data_parser.find_value('adcFreq', param_list, value_list) * 1e6
    Df = data_parser.find_value('b1Freq', param_list, value_list) * 1e6
    total_scan = int(
        data_parser.find_value('nrIterations', param_list, value_list))
    file_name_prefix = 'dat_'
    # en_ext_param = 0
    # thetaref = 0
    # echoref_avg = 0

    if (direct_read):
        (a, a_integ, a0, snr, T2, noise, res, theta, data_filt, echo_avg,
         t_echospace) = compute_multiple(nmrObj, data_parent_folder,
                                         meas_folder, file_name_prefix, Df, Sf,
                                         tE, total_scan, en_fig, en_ext_param,
                                         thetaref, echoref_avg, direct_read,
                                         datain)
    else:
        (a, a_integ, a0, snr, T2, noise, res, theta, data_filt, echo_avg,
         t_echospace) = compute_multiple(nmrObj, data_parent_folder,
                                         meas_folder, file_name_prefix, Df, Sf,
                                         tE, total_scan, en_fig, en_ext_param,
                                         thetaref, echoref_avg, 0, datain)

    # print(snr, T2)
    return a, a_integ, a0, snr, T2, noise, res, theta, data_filt, echo_avg, Df, t_echospace
def compute_stats(minfreq, maxfreq, data_parent_folder, meas_folder, plotname,
                  en_fig):

    # variables to be input
    # data_parent_folder : the folder for all datas
    # meas_folder        : the specific folder for one measurement
    # en_fig            : enable figure

    # compute settings
    process_sum_data = 1  # otherwise process raw data

    file_name_prefix = 'dat_'
    data_folder = (data_parent_folder + '/' + meas_folder + '/')
    fig_num = 200

    # variables from NMR settings
    (param_list, value_list) = data_parser.parse_info(data_folder,
                                                      'acqu.par')  # read file
    adcFreq = data_parser.find_value('adcFreq', param_list, value_list)
    nrPnts = int(data_parser.find_value('nrPnts', param_list, value_list))
    total_scan = int(
        data_parser.find_value('nrIterations', param_list, value_list))

    # parse file and remove DC component
    nmean = 0
    if process_sum_data:
        file_path = (data_folder + 'asum')
        one_scan_raw = np.array(data_parser.read_data(file_path))
        nmean = np.mean(one_scan_raw)
        one_scan = (one_scan_raw - nmean) / total_scan

    else:
        for m in range(1, total_scan + 1):
            file_path = (data_folder + file_name_prefix + '{0:03d}'.format(m))
            # read the data from the file and store it in numpy array format
            one_scan_raw = np.array(data_parser.read_data(file_path))
            nmean = np.mean(one_scan_raw)
            one_scan = (one_scan_raw - nmean) / \
                total_scan  # remove DC component

    # compute fft
    spectx, specty = nmr_fft(one_scan, adcFreq, 0)
    specty = abs(specty)
    fft_range = [
        i for i, value in enumerate(spectx)
        if (value >= minfreq and value <= maxfreq)
    ]  # limit fft display

    # compute std
    nstd = np.std(one_scan)

    if en_fig:
        plt.ion()
        fig = plt.figure(fig_num)

        # maximize window
        plot_backend = matplotlib.get_backend()
        mng = plt.get_current_fig_manager()
        if plot_backend == 'TkAgg':
            # mng.resize(*mng.window.maxsize())
            mng.resize(800, 600)
        elif plot_backend == 'wxAgg':
            mng.frame.Maximize(True)
        elif plot_backend == 'Qt4Agg':
            mng.window.showMaximized()

        fig.clf()
        ax = fig.add_subplot(311)

        line1, = ax.plot(spectx[fft_range], specty[fft_range], 'b-')
        # ax.set_ylim(-50, 0)
        ax.set_xlabel('Frequency (MHz)')
        ax.set_ylabel('Amplitude (a.u.)')
        ax.set_title("Spectrum")
        ax.grid()

        ax = fig.add_subplot(312)
        x_time = np.linspace(1, len(one_scan_raw), len(one_scan_raw))
        x_time = np.multiply(x_time, (1 / adcFreq))  # in us
        x_time = np.multiply(x_time, 1e-3)  # in ms
        line1, = ax.plot(x_time, one_scan_raw, 'b-')
        ax.set_xlabel('Time(ms)')
        ax.set_ylabel('Amplitude (a.u.)')
        ax.set_title("Amplitude. std=%0.2f. mean=%0.2f." % (nstd, nmean))
        ax.grid()

        # plot histogram
        n_bins = 200
        ax = fig.add_subplot(313)
        n, bins, patches = ax.hist(one_scan, bins=n_bins)
        ax.set_title("Histogram")

        plt.tight_layout()
        fig.canvas.draw()
        fig.canvas.flush_events()

        # fig = plt.gcf() # obtain handle
        plt.savefig(data_folder + plotname)

    # standard deviation of signal
    print('\t\t: rms= ' + '{0:.4f}'.format(nstd) +
          ' mean= {0:.4f}'.format(nmean))
    return nstd, nmean
def compute_multiple(nmrObj, data_parent_folder, meas_folder, file_name_prefix,
                     Df, Sf, tE, total_scan, en_fig, en_ext_param, thetaref,
                     echoref_avg, direct_read, datain):

    # variables to be input
    # nmrObj            : the hardware definition
    # data_parent_folder : the folder for all datas
    # meas_folder        : the specific folder for one measurement
    # filename_prefix   : the name of data prefix
    # Df                : data frequency
    # Sf                : sample frequency
    # tE                : echo spacing
    # total_scan        : number_of_iteration
    # en_fig            : enable figure
    # en_ext_param      : enable external parameter for data signal processing
    # thetaref          : external parameter : rotation angle
    # echoref_avg        : external parameter : echo average reference
    # datain            : the data captured direct reading. data format: AC,averaged scans, phase-cycled
    # direct_read        : perform direct reading from SDRAM/FIFO

    data_folder = (data_parent_folder + '/' + meas_folder + '/')

    # variables local to this function
    # the setting file for the measurement
    mtch_fltr_sta_idx = 0  # 0 is default or something referenced to SpE, e.g. SpE/4; the start index for match filtering is to neglect ringdown part from calculation
    # perform rotation to the data -> required for reference data for t1
    # measurement
    perform_rotation = 1
    # process individual raw data, otherwise it'll load a sum file generated
    # by C
    proc_indv_data = 0
    # put 1 if the data file uses binary representation, otherwise it is in
    # ascii format
    binary_OR_ascii = 1
    ignore_echoes = 0  # ignore initial echoes #

    # simulate decimation in software (DO NOT use this for normal operation,
    # only for debugging purpose)
    sim_dec = 0
    sim_dec_fact = 32

    # variables from NMR settings
    (param_list, value_list) = data_parser.parse_info(data_folder,
                                                      'acqu.par')  # read file
    SpE = int(data_parser.find_value('nrPnts', param_list, value_list))
    NoE = int(data_parser.find_value('nrEchoes', param_list, value_list))
    en_ph_cycle_proc = data_parser.find_value('usePhaseCycle', param_list,
                                              value_list)
    # tE = data_parser.find_value('echoTimeRun', param_list, value_list)
    # Sf = data_parser.find_value(
    #    'adcFreq', param_list, value_list) * 1e6
    # Df = data_parser.find_value(
    #    'b1Freq', param_list, value_list) * 1e6
    # total_scan = int(data_parser.find_value(
    #    'nrIterations', param_list, value_list))
    fpga_dconv = data_parser.find_value('fpgaDconv', param_list, value_list)
    dconv_fact = data_parser.find_value('dconvFact', param_list, value_list)
    echo_skip = data_parser.find_value('echoSkipHw', param_list, value_list)

    # account for skipped echoes
    NoE = int(NoE / echo_skip)
    tE = tE * echo_skip

    # compensate for dconv_fact if fpga dconv is used
    if fpga_dconv:
        SpE = int(SpE / dconv_fact)
        Sf = Sf / dconv_fact

    # ignore echoes
    if ignore_echoes:
        NoE = NoE - ignore_echoes

    # time domain for plot
    tacq = (1 / Sf) * 1e6 * np.linspace(1, SpE, SpE)  # in uS
    t_echospace = tE / 1e6 * np.linspace(1, NoE, NoE)  # in uS

    if fpga_dconv:  # use fpga dconv
        # load IQ data
        file_path = (data_folder + 'dconv')

        if binary_OR_ascii:
            dconv = data_parser.read_hex_float(
                file_path)  # use binary representation
        else:
            dconv = np.array(
                data_parser.read_data(file_path))  # use ascii representation

        if ignore_echoes:
            dconv = dconv[ignore_echoes * 2 * SpE:len(dconv)]

        # normalize the data
        # normalize with decimation factor (due to sum in the fpga)
        dconv = dconv / dconv_fact
        dconv = dconv / nmrObj.fir_gain  # normalize with the FIR gain in the fpga
        # convert to voltage unit at the probe
        dconv = dconv / nmrObj.totGain * nmrObj.uvoltPerDigit
        # scale all the data to magnitude of sin(45). The FPGA uses unity
        # magnitude (instead of sin45,135,225,315) to simplify downconversion
        # operation
        dconv = dconv * nmrObj.dconv_gain

        # combined IQ
        data_filt = np.zeros((NoE, SpE), dtype=complex)
        for i in range(0, NoE):
            data_filt[i, :] = \
                dconv[i * (2 * SpE):(i + 1) * (2 * SpE):2] + 1j * \
                dconv[i * (2 * SpE) + 1:(i + 1) * (2 * SpE):2]

        if en_fig:  # plot the averaged scan
            echo_space = (1 / Sf) * np.linspace(1, SpE, SpE)  # in s
            plt.figure(1)
            for i in range(0, NoE):
                plt.plot(((i - 1) * tE * 1e-6 + echo_space) * 1e3,
                         np.real(data_filt[i, :]),
                         linewidth=0.4,
                         color='b')
                plt.plot(((i - 1) * tE * 1e-6 + echo_space) * 1e3,
                         np.imag(data_filt[i, :]),
                         linewidth=0.4,
                         color='r')
            plt.title("Averaged raw data (downconverted)")
            plt.xlabel('time(ms)')
            plt.ylabel('probe voltage (uV)')
            plt.savefig(data_folder + 'fig_avg_raw_data.png')

        # raw average data
        echo_rawavg = np.mean(data_filt, axis=0)

        if (en_fig):
            if en_fig:  # plot echo rawavg
                plt.figure(6)
                plt.plot(tacq, np.real(echo_rawavg), label='real')
                plt.plot(tacq, np.imag(echo_rawavg), label='imag')
                plt.plot(tacq, np.abs(echo_rawavg), label='abs')
                plt.xlim(0, max(tacq))
                plt.title("Echo Average before rotation (down-converted)")
                plt.xlabel('time(uS)')
                plt.ylabel('probe voltage (uV)')
                plt.legend()
                plt.savefig(data_folder + 'fig_echo_avg_dconv.png')

        # simulate additional decimation (not needed for normal operqtion). For
        # debugging purpose
        if (sim_dec):
            SpE = int(SpE / sim_dec_fact)
            Sf = Sf / sim_dec_fact
            data_filt_dec = np.zeros((NoE, SpE), dtype=complex)
            for i in range(0, SpE):
                data_filt_dec[:, i] = np.mean(
                    data_filt[:, i * sim_dec_fact:(i + 1) * sim_dec_fact],
                    axis=1)
            data_filt = np.zeros((NoE, SpE), dtype=complex)
            data_filt = data_filt_dec
            tacq = (1 / Sf) * 1e6 * np.linspace(1, SpE, SpE)  # in uS

    else:
        # do down conversion locally
        if (direct_read):
            data = datain
        else:
            if (proc_indv_data):
                # read all datas and average it
                data = np.zeros(NoE * SpE)
                for m in range(1, total_scan + 1):
                    file_path = (data_folder + file_name_prefix +
                                 '{0:03d}'.format(m))
                    # read the data from the file and store it in numpy array
                    # format
                    one_scan = np.array(data_parser.read_data(file_path))
                    one_scan = (one_scan - np.mean(one_scan)) / \
                        total_scan  # remove DC component
                    if (en_ph_cycle_proc):
                        if (m % 2):  # phase cycling every other scan
                            data = data - one_scan
                        else:
                            data = data + one_scan
                    else:
                        data = data + one_scan
            else:
                # read sum data only
                file_path = (data_folder + 'asum')
                data = np.zeros(NoE * SpE)

                if binary_OR_ascii:
                    data = data_parser.read_hex_float(
                        file_path)  # use binary representation
                else:
                    # use ascii representation
                    data = np.array(data_parser.read_data(file_path))

                dataraw = data
                data = (data - np.mean(data))

        # ignore echoes
        if ignore_echoes:
            data = data[ignore_echoes * SpE:len(data)]

        # compute the probe voltage before gain stage
        data = data / nmrObj.totGain * nmrObj.uvoltPerDigit

        if en_fig:  # plot the averaged scan
            echo_space = (1 / Sf) * np.linspace(1, SpE, SpE)  # in s
            plt.figure(1)
            for i in range(1, NoE + 1):
                # plt.plot(((i - 1) * tE * 1e-6 + echo_space) * 1e3, data[(i - 1) * SpE:i * SpE], linewidth=0.4)
                plt.plot(((i - 1) * tE * 1e-6 + echo_space) * 1e3,
                         dataraw[(i - 1) * SpE:i * SpE],
                         linewidth=0.4)
            plt.title("Averaged raw data")
            plt.xlabel('time(ms)')
            plt.ylabel('probe voltage (uV)')
            plt.savefig(data_folder + 'fig_avg_raw_data.png')

        # raw average data
        echo_rawavg = np.zeros(SpE, dtype=float)
        for i in range(0, NoE):
            echo_rawavg += (data[i * SpE:(i + 1) * SpE] / NoE)

        if en_fig:  # plot echo rawavg
            plt.figure(6)
            plt.plot(tacq, echo_rawavg, label='echo rawavg')
            plt.xlim(0, max(tacq))
            plt.title("Echo Average (raw)")
            plt.xlabel('time(uS)')
            plt.ylabel('probe voltage (uV)')
            plt.legend()
            plt.savefig(data_folder + 'fig_echo_avg.png')

        # filter the data
        data_filt = np.zeros((NoE, SpE), dtype=complex)
        for i in range(0, NoE):
            data_filt[i, :] = down_conv(data[i * SpE:(i + 1) * SpE], i, tE, Df,
                                        Sf)

        # simulate additional decimation (not needed for normal operqtion). For
        # debugging purpose
        if (sim_dec):
            SpE = int(SpE / sim_dec_fact)
            Sf = Sf / sim_dec_fact
            data_filt_dec = np.zeros((NoE, SpE), dtype=complex)
            for i in range(0, SpE):
                data_filt_dec[:, i] = np.sum(
                    data_filt[:, i * sim_dec_fact:(i + 1) * sim_dec_fact],
                    axis=1)
            data_filt = np.zeros((NoE, SpE), dtype=complex)
            data_filt = data_filt_dec
            tacq = (1 / Sf) * 1e6 * np.linspace(1, SpE, SpE)  # in uS

    # scan rotation
    if en_ext_param:
        data_filt = data_filt * np.exp(-1j * thetaref)
        theta = math.atan2(np.sum(np.imag(data_filt)),
                           np.sum(np.real(data_filt)))
    else:
        theta = math.atan2(np.sum(np.imag(data_filt)),
                           np.sum(np.real(data_filt)))
        if perform_rotation:
            data_filt = data_filt * np.exp(-1j * theta)

    if en_fig:  # plot filtered data
        echo_space = (1 / Sf) * np.linspace(1, SpE, SpE)  # in s
        plt.figure(2)
        for i in range(0, NoE):
            plt.plot((i * tE * 1e-6 + echo_space) * 1e3,
                     np.real(data_filt[i, :]),
                     'b',
                     linewidth=0.4)
            plt.plot((i * tE * 1e-6 + echo_space) * 1e3,
                     np.imag(data_filt[i, :]),
                     'r',
                     linewidth=0.4)
        plt.legend()
        plt.title('Filtered data')
        plt.xlabel('Time (mS)')
        plt.ylabel('probe voltage (uV)')
        plt.savefig(data_folder + 'fig_filt_data.png')

    # find echo average, echo magnitude
    echo_avg = np.zeros(SpE, dtype=complex)
    for i in range(0, NoE):
        echo_avg += (data_filt[i, :] / NoE)

    if en_fig:  # plot echo shape
        plt.figure(3)
        plt.plot(tacq, np.abs(echo_avg), label='abs')
        plt.plot(tacq, np.real(echo_avg), label='real part')
        plt.plot(tacq, np.imag(echo_avg), label='imag part')
        plt.xlim(0, max(tacq))
        plt.title("Echo Shape")
        plt.xlabel('time(uS)')
        plt.ylabel('probe voltage (uV)')
        plt.legend()
        plt.savefig(data_folder + 'fig_echo_shape.png')

        # plot fft of the echosum
        plt.figure(4)
        zf = 100  # zero filling factor to get smooth curve
        ws = 2 * np.pi / (tacq[1] - tacq[0])  # in MHz
        wvect = np.linspace(-ws / 2, ws / 2, len(tacq) * zf)
        echo_zf = np.zeros(zf * len(echo_avg), dtype=complex)
        echo_zf[int((zf / 2) * len(echo_avg) -
                    len(echo_avg) / 2):int((zf / 2) * len(echo_avg) +
                                           len(echo_avg) / 2)] = echo_avg
        spect = zf * (np.fft.fftshift(np.fft.fft(np.fft.ifftshift(echo_zf))))
        spect = spect / len(spect)  # normalize the spectrum
        plt.plot(wvect / (2 * np.pi), np.real(spect), label='real')
        plt.plot(wvect / (2 * np.pi), np.imag(spect), label='imag')
        plt.xlim(10 / max(tacq) * -1, 10 / max(tacq) * 1)
        plt.title("FFT of the echo-sum. " +
                  "Peak:real@{:0.2f}kHz,abs@{:0.2f}kHz".format(
                      wvect[np.abs(np.real(spect)) == max(
                          np.abs(np.real(spect)))][0] / (2 * np.pi) *
                      1e3, wvect[np.abs(spect) == max(np.abs(spect))][0] /
                      (2 * np.pi) * 1e3))
        plt.xlabel('offset frequency(MHz)')
        plt.ylabel('Echo amplitude (a.u.)')
        plt.legend()
        plt.savefig(data_folder + 'fig_echo_A.png')

    # matched filtering
    a = np.zeros(NoE, dtype=complex)
    for i in range(0, NoE):
        if en_ext_param:
            a[i] = np.mean(
                np.multiply(data_filt[i, mtch_fltr_sta_idx:SpE],
                            np.conj(echoref_avg[mtch_fltr_sta_idx:SpE]))
            )  # find amplitude with reference matched filtering
        else:
            a[i] = np.mean(
                np.multiply(data_filt[i, mtch_fltr_sta_idx:SpE],
                            np.conj(echo_avg[mtch_fltr_sta_idx:SpE]))
            )  # find amplitude with matched filtering

    a_integ = np.sum(np.real(a))

    # def exp_func(x, a, b, c, d):
    #    return a * np.exp(-b * x) + c * np.exp(-d * x)
    def exp_func(x, a, b):
        return a * np.exp(-b * x)

    # average the first 5% of datas
    a_guess = np.mean(np.real(a[0:int(np.round(SpE / 20))]))
    # c_guess = a_guess
    # find min idx value where the value of (a_guess/exp) is larger than
    # real(a)
    # b_guess = np.where(np.real(a) == np.min(
    #    np.real(a[np.real(a) > a_guess / np.exp(1)])))[0][0] * tE / 1e6
    # this is dummy b_guess, use the one I made above this for smarter one
    # (but sometimes it doesn't work)
    b_guess = 0.01
    # d_guess = b_guess
    # guess = np.array([a_guess, b_guess, c_guess, d_guess])
    guess = np.array([a_guess, b_guess])

    try:  # try fitting data
        popt, pocv = curve_fit(exp_func, t_echospace, np.real(a), guess)

        # obtain fitting parameter
        a0 = popt[0]
        T2 = 1 / popt[1]

        # Estimate SNR/echo/scan
        f = exp_func(t_echospace, *popt)  # curve fit
        noise = np.std(np.imag(a))
        res = np.std(np.real(a) - f)
        snr_imag = a0 / (noise * math.sqrt(total_scan))
        snr_res = a0 / (res * math.sqrt(total_scan))
        snr = snr_imag

        # plot fitted line
        plt.figure(5)
        plt.cla()
        plt.plot(t_echospace * 1e3, f, label="fit")  # plot in milisecond
        plt.plot(t_echospace * 1e3, np.real(a) - f, label="residue")

    except:
        print('Problem in fitting. Set a0 and T2 output to 0\n')
        a0 = 0
        T2 = 0
        noise = 0
        res = 0
        snr = 0

    if en_fig:
        # plot data
        plt.figure(5)
        # plot in milisecond
        plt.plot(t_echospace * 1e3, np.real(a), label="real")
        # plot in milisecond
        plt.plot(t_echospace * 1e3, np.imag(a), label="imag")

        # plt.set(gca, 'FontSize', 12)
        plt.legend()
        plt.title(
            'Matched filtered data. SNRim:{:03.2f} SNRres:{:03.2f}.\na:{:03.1f} n_im:{:03.1f} n_res:{:03.1f} T2:{:0.2f}msec'
            .format(snr, snr_res, a0, (noise * math.sqrt(total_scan)),
                    (res * math.sqrt(total_scan)), T2 * 1e3))
        plt.xlabel('Time (mS)')
        plt.ylabel('probe voltage (uV)')
        plt.savefig(data_folder + 'fig_matched_filt_data.png')

        plt.show()

    print('a0 = ' + '{0:.2f}'.format(a0))
    print('SNR/echo/scan = ' +
          'imag:{0:.2f}, res:{1:.2f}'.format(snr, snr_res))
    print('T2 = ' + '{0:.4f}'.format(T2 * 1e3) + ' msec')

    return (a, a_integ, a0, snr, T2, noise, res, theta, data_filt, echo_avg,
            t_echospace)
def compute_wobble(nmrObj, data_parent_folder, meas_folder, s11_min, S11mV_ref,
                   useRef, en_fig, fig_num):

    # S11mV_ref is the reference s11 corresponds to maximum reflection (can be made by totally untuning matching network, e.g. disconnecting all caps in matching network)
    # useRef uses the S11mV_ref as reference, otherwise it will use the max
    # signal available in the data

    # settings
    # put 1 if the data file uses binary representation, otherwise it is in
    # ascii format. Find the setting in the C programming file
    binary_OR_ascii = 1  # manual setting: put the same setting from the C programming

    data_folder = (data_parent_folder + '/' + meas_folder + '/')

    (param_list, value_list) = data_parser.parse_info(data_folder,
                                                      'acqu.par')  # read file
    freqSta = data_parser.find_value('freqSta', param_list, value_list)
    freqSto = data_parser.find_value('freqSto', param_list, value_list)
    freqSpa = data_parser.find_value('freqSpa', param_list, value_list)
    nSamples = data_parser.find_value('nSamples', param_list, value_list)
    freqSamp = data_parser.find_value('freqSamp', param_list, value_list)
    spect_bw = (freqSamp / nSamples) * 4  # determining the RBW

    file_name_prefix = 'tx_acq_'
    # plus freqSpa/2 is to include the endpoint (just like what the C does)
    freqSw = np.arange(freqSta, freqSto + (freqSpa / 2), freqSpa)
    S11mV = np.zeros(len(freqSw))
    S11_ph = np.zeros(len(freqSw))
    for m in range(0, len(freqSw)):
        # for m in freqSw:
        file_path = (data_folder + file_name_prefix +
                     '{:4.3f}'.format(freqSw[m]))

        if binary_OR_ascii:
            # one_scan = data_parser.read_hex_int16(file_path)  # use binary
            # representation
            one_scan = data_parser.read_hex_int32(
                file_path)  # use binary representation for 32-bit file
        else:
            one_scan = np.array(
                data_parser.read_data(file_path))  # use ascii representation

        os.remove(file_path)  # delete the file after use

        # find voltage at the input of ADC in mV
        one_scan = one_scan * nmrObj.uvoltPerDigit / 1e3

        spectx, specty = nmr_fft(one_scan, freqSamp, 0)

        # FIND INDEX WHERE THE MAXIMUM SIGNAL IS PRESENT
        # PRECISE METHOD: find reflection at the desired frequency: creating precision problem where usually the signal shift a little bit from its desired frequency
        # ref_idx = abs(spectx - freqSw[m]) == min(abs(spectx - freqSw[m]))
        # BETTER METHOD: find reflection signal peak around the bandwidth
        ref_idx = (abs(spectx - freqSw[m]) <= (spect_bw / 2))

        # S11mV[m] = max( abs( specty[ref_idx] ) )  # find reflection peak
        # compute the mean of amplitude inside RBW
        S11mV[m] = np.mean(abs(specty[ref_idx]))
        S11_ph[m] = np.mean(np.angle(specty[ref_idx])) * (360 / (2 * np.pi))

    if useRef:  # if reference is present
        S11dB = 20 * np.log10(np.divide(S11mV,
                                        S11mV_ref))  # convert to dB scale
    else:  # if reference is not present
        S11dB = 20 * np.log10(S11mV / max(S11mV))  # convert to dB scale

    S11_min10dB = (S11dB <= s11_min)

    minS11 = min(S11dB)
    minS11_freq = freqSw[np.argmin(S11dB)]

    try:
        S11_fmin = min(freqSw[S11_min10dB])
        S11_fmax = max(freqSw[S11_min10dB])
    except:
        S11_fmin = 0
        S11_fmax = 0
        print('S11 requirement is not satisfied...')

    S11_bw = S11_fmax - S11_fmin

    if en_fig:
        plt.ion()
        fig = plt.figure(fig_num)
        fig.clf()
        ax = fig.add_subplot(211)
        line1, = ax.plot(freqSw, S11dB, 'r-')
        ax.set_ylim(-35, 10)
        ax.set_ylabel('S11 [dB]')
        ax.set_title("Reflection Measurement (S11) Parameter")
        ax.grid()

        bx = fig.add_subplot(212)
        bx.plot(freqSw, S11_ph, 'r-')
        bx.set_xlabel('Frequency [MHz]')
        bx.set_ylabel('Phase (deg)')
        bx.set_title(
            'incorrect phase due to non-correlated transmit and sampling')
        bx.grid()

        fig.canvas.draw()
        fig.canvas.flush_events()

        plt.savefig(data_folder + 'wobble.png')

    # write S11mV to a file
    with open(data_folder + 'S11mV.txt', 'w') as f:
        for (a, b, c) in zip(freqSw, S11dB, S11_ph):
            f.write('{:-8.3f},{:-8.3f},{:-7.1f}\n'.format(a, b, c))

    # print(S11_fmin, S11_fmax, S11_bw)
    if useRef:
        return S11dB, S11_fmin, S11_fmax, S11_bw, minS11, minS11_freq
    else:
        return S11mV, S11_fmin, S11_fmax, S11_bw, minS11, minS11_freq
def compute_gain(nmrObj, data_parent_folder, meas_folder, en_fig, fig_num):
    data_folder = (data_parent_folder + '/' + meas_folder + '/')

    # settings
    # put 1 if the data file uses binary representation, otherwise it is in
    # ascii format. Find the setting in the C programming file
    binary_OR_ascii = 1  # manual setting: put the same setting from the C programming

    (param_list, value_list) = data_parser.parse_info(data_folder,
                                                      'acqu.par')  # read file
    freqSta = data_parser.find_value('freqSta', param_list, value_list)
    freqSto = data_parser.find_value('freqSto', param_list, value_list)
    freqSpa = data_parser.find_value('freqSpa', param_list, value_list)
    nSamples = data_parser.find_value('nSamples', param_list, value_list)
    freqSamp = data_parser.find_value('freqSamp', param_list, value_list)
    spect_bw = (freqSamp / nSamples) * 4  # determining the RBW

    file_name_prefix = 'tx_acq_'
    # plus freqSpa/2 is to include the endpoint (just like what the C does)
    freqSw = np.arange(freqSta, freqSto + (freqSpa / 2), freqSpa)
    S21 = np.zeros(len(freqSw))
    S21_ph = np.zeros(len(freqSw))
    for m in range(0, len(freqSw)):
        # for m in freqSw:
        file_path = (data_folder + file_name_prefix +
                     '{:4.3f}'.format(freqSw[m]))

        if binary_OR_ascii:
            # one_scan = data_parser.read_hex_int16(file_path)  # use binary
            # representation
            one_scan = data_parser.read_hex_int32(
                file_path)  # use binary representation for 32-bit file
        else:
            one_scan = np.array(
                data_parser.read_data(file_path))  # use ascii representation

        os.remove(file_path)  # delete the file after use
        '''
        plt.ion()
        fig = plt.figure( 64 )
        fig.clf()
        ax = fig.add_subplot( 111 )
        ax.plot( one_scan )
        fig.canvas.draw()
        fig.canvas.flush_events()
        '''

        # find voltage at the input of ADC in mV
        one_scan = one_scan * nmrObj.uvoltPerDigit / 1e3

        spectx, specty = nmr_fft(one_scan, freqSamp, 0)

        # FIND INDEX WHERE THE MAXIMUM SIGNAL IS PRESENT
        # PRECISE METHOD: find reflection at the desired frequency: creating precision problem where usually the signal shift a little bit from its desired frequency
        # ref_idx = abs(spectx - freqSw[m]) == min(abs(spectx - freqSw[m]))
        # BETTER METHOD: find reflection signal peak around the bandwidth
        # divide 2 is due to +/- half-BW around the interested frequency
        ref_idx = (abs(spectx - freqSw[m]) <= (spect_bw / 2))

        # compute the mean of amplitude inside RBW
        S21[m] = np.mean(abs(specty[ref_idx]))
        # compute the angle mean. Currently it is not useful because the phase
        # is not preserved in the sequence
        S21_ph[m] = np.mean(np.angle(specty[ref_idx])) * (360 / (2 * np.pi))

    S21dB = 20 * np.log10(S21)  # convert to dBmV scale

    maxS21 = max(S21dB)
    maxS21_freq = freqSw[np.argmax(S21dB)]

    if en_fig:
        plt.ion()
        fig = plt.figure(fig_num)
        fig.clf()
        ax = fig.add_subplot(111)
        line1, = ax.plot(freqSw, S21dB, 'r-')
        ax.set_ylim(-30, 80)
        ax.set_ylabel('S21 [dBmV]')
        ax.set_title("Transmission Measurement (S21) Parameter")
        ax.grid()

        # bx = fig.add_subplot( 212 )
        # bx.plot( freqSw, S21_ph, 'r-' )
        # bx.set_xlabel( 'Frequency [MHz]' )
        # bx.set_ylabel( 'Phase (deg)' )
        # bx.set_title( 'incorrect phase due to non-correlated transmit and sampling' )
        # bx.grid()

        fig.canvas.draw()
        fig.canvas.flush_events()

        plt.savefig(data_folder + 'gain.png')

    # write gain values to a file
    with open(data_folder + 'S21.txt', 'w') as f:
        for (a, b, c) in zip(freqSw, S21dB, S21_ph):
            f.write('{:-8.3f},{:-8.3f},{:-7.1f}\n'.format(a, b, c))

    return maxS21, maxS21_freq, S21
Ejemplo n.º 7
0
'''
Created on Mar 30, 2018

@author: David Ariando
'''

import nmr_std_function.data_parser as nmr_info

data_folder = "D:/NMR_EECS397_PCBv3_2017/DATAS/nmr_2018_03_07_19_32_23/"
file_name = "CPMG_iterate_settings.txt"
(param_list, value_list) = nmr_info.parse_info(data_folder, file_name)

# echo_spacing = value_list[[i for i, elem in enumerate(param_list) if 'echo_spacing_us' in elem][0]]

echo_spacing = nmr_info.find_value('echo_spacing_us', param_list, value_list)

print(echo_spacing)
Ejemplo n.º 8
0
# put the name into the "filename", e.g. dat_001

import matplotlib.pyplot as plt
from nmr_std_function import data_parser
import numpy as np

data_folder = 'Z:/NMR_DATA'
fold1 = '2020_05_18_23_23_49_cpmg'
fold2 = '2020_05_18_23_24_28_cpmg'

filename = 'dat_001'

# variables from NMR settings

# load acquisition setting 1
( param_list, value_list ) = data_parser.parse_info( 
    data_folder + '/' + fold1 + '/', 'acqu.par' )  # read file
dwellTime1 = data_parser.find_value( 
    'dwellTime', param_list, value_list )
dconvFact1 = data_parser.find_value( 
    'dconvFact', param_list, value_list )

# load acquisition setting 2
( param_list, value_list ) = data_parser.parse_info( 
    data_folder + '/' + fold2 + '/', 'acqu.par' )  # read file
dwellTime2 = data_parser.find_value( 
    'dwellTime', param_list, value_list )
dconvFact2 = data_parser.find_value( 
    'dconvFact', param_list, value_list )

# load data 1
file_path = ( data_folder + '/' + fold1 + '/' + filename )
Ejemplo n.º 9
0
                           | nmrObj.RX2_L_msk | nmrObj.RX2_H_msk
                           | nmrObj.RX_SEL1_msk | nmrObj.RX_FL_msk
                           | nmrObj.RX_FH_msk)

while True:
    nmrObj.cpmgSequence(cpmg_freq, pulse1_us, pulse2_us, pulse1_dtcl,
                        pulse2_dtcl, echo_spacing_us, scan_spacing_us,
                        samples_per_echo, echoes_per_scan,
                        init_adc_delay_compensation, number_of_iteration,
                        ph_cycl_en, pulse180_t1_int, delay180_t1_int)

    meas_folder = parse_simple_info(data_folder, 'current_folder.txt')
    data_fold = (data_folder + '/' + meas_folder[0] + '/')
    file_path = (data_fold + 'asum')

    (param_list, value_list) = data_parser.parse_info(data_fold,
                                                      'acqu.par')  # read file

    SpE = int(data_parser.find_value('nrPnts', param_list, value_list))
    NoE = int(data_parser.find_value('nrEchoes', param_list, value_list))
    data = np.zeros(NoE * SpE)
    data = np.array(data_parser.read_data(file_path))

    ppval = np.max(data) - np.min(data)
    print("peak-to-peak : %d" % ppval)
    # print("rms : %f" % ppval / (2 * np.sqrt(2)))

    plt.ion()
    fig = plt.figure(1)
    fig.clf()
    plt.plot(data)
    fig.canvas.draw()
Ejemplo n.º 10
0
def compute_multiple(data_parent_folder, meas_folder, file_name_prefix, Df, Sf,
                     tE, total_scan, en_fig, en_ext_param, thetaref,
                     echoref_avg, direct_read, datain):

    # variables to be input
    # data_parent_folder : the folder for all datas
    # meas_folder        : the specific folder for one measurement
    # filename_prefix   : the name of data prefix
    # Df                : data frequency
    # Sf                : sample frequency
    # tE                : echo spacing
    # total_scan        : number_of_iteration
    # en_fig            : enable figure
    # en_ext_param      : enable external parameter for data signal processing
    # thetaref          : external parameter : rotation angle
    # echoref_avg        : external parameter : echo average reference
    # datain            : the data captured direct reading. data format: AC,averaged scans, phase-cycled
    # direct_read        : perform direct reading from SDRAM/FIFO

    data_folder = (data_parent_folder + '/' + meas_folder + '/')

    # variables local to this function
    # the setting file for the measurement
    mtch_fltr_sta_idx = 0  # 0 is default or something referenced to SpE, e.g. SpE/4; the start index for match filtering is to neglect ringdown part from calculation
    # perform rotation to the data -> required for reference data for t1
    # measurement
    perform_rotation = 1
    # process individual raw data, otherwise it'll load a sum file generated
    # by C
    proc_indv_data = 0

    proc_dconv = 1  # process dconv in python, otherwise use fpga dconv

    dconv_factor = 1  # decimation factor for downconversion

    # receiver gain
    pamp_gain_dB = 54
    rx_gain_dB = 0
    totGain = 10**((pamp_gain_dB + rx_gain_dB) / 20)

    # ADC conversion
    voltPerDigit = 3.2 * (10**6) / 16384  # microvolt

    # variables from NMR settings
    (param_list, value_list) = data_parser.parse_info(data_folder,
                                                      'acqu.par')  # read file
    SpE = int(data_parser.find_value('nrPnts', param_list, value_list))
    NoE = int(data_parser.find_value('nrEchoes', param_list, value_list))
    en_ph_cycle_proc = data_parser.find_value('usePhaseCycle', param_list,
                                              value_list)
    # tE = data_parser.find_value('echoTimeRun', param_list, value_list)
    # Sf = data_parser.find_value(
    #    'adcFreq', param_list, value_list) * 1e6
    # Df = data_parser.find_value(
    #    'b1Freq', param_list, value_list) * 1e6
    # total_scan = int(data_parser.find_value(
    #    'nrIterations', param_list, value_list))

    # compensate for dconv_factor if fpga dconv is used
    if not proc_dconv:
        SpE = int(SpE / dconv_factor)
        Sf = Sf / dconv_factor

    # time domain for plot
    tacq = (1 / Sf) * 1e6 * np.linspace(1, SpE, SpE)  # in uS
    t_echospace = tE / 1e6 * np.linspace(1, NoE, NoE)  # in uS

    if proc_dconv:
        # parse file and remove DC component
        if (direct_read):
            data = datain
        else:
            if (proc_indv_data):
                # read all datas and average it
                data = np.zeros(NoE * SpE)
                for m in range(1, total_scan + 1):
                    file_path = (data_folder + file_name_prefix +
                                 '{0:03d}'.format(m))
                    # read the data from the file and store it in numpy array
                    # format
                    one_scan = np.array(data_parser.read_data(file_path))
                    one_scan = (one_scan - np.mean(one_scan)) / \
                        total_scan  # remove DC component
                    if (en_ph_cycle_proc):
                        if (m % 2):  # phase cycling every other scan
                            data = data - one_scan
                        else:
                            data = data + one_scan
                    else:
                        data = data + one_scan
            else:
                # read sum data only
                file_path = (data_folder + 'asum')
                data = np.zeros(NoE * SpE)
                data = np.array(data_parser.read_data(file_path))
                data = (data - np.mean(data)) / \
                    total_scan  # remove DC component

        # compute raw data before gain stage
        data = data / totGain * voltPerDigit

        if en_fig:  # plot the averaged scan
            echo_space = (1 / Sf) * np.linspace(1, SpE, SpE)  # in s
            plt.figure(1)
            for i in range(1, NoE + 1):
                plt.plot(((i - 1) * tE * 1e-6 + echo_space) * 1e3,
                         data[(i - 1) * SpE:i * SpE],
                         linewidth=0.4)

        # raw average data
        echo_rawavg = np.zeros(SpE, dtype=float)
        for i in range(5, NoE):
            echo_rawavg += (data[i * SpE:(i + 1) * SpE] / NoE)

        if en_fig:  # plot echo rawavg
            plt.figure(6)
            plt.plot(tacq, echo_rawavg, label='echo rawavg')
            plt.xlim(0, max(tacq))
            plt.title("Echo Average")
            plt.xlabel('time(uS)')
            plt.ylabel('amplitude')
            plt.legend()

        # filter the data
        data_filt = np.zeros((NoE, SpE), dtype=complex)
        for i in range(0, NoE):
            data_filt[i, :] = down_conv(data[i * SpE:(i + 1) * SpE], i, tE, Df,
                                        Sf)

    else:  # use fpga dconv
        # in-phase data
        file_path = (data_folder + 'dconvi')
        dconvi = np.array(data_parser.read_data(file_path))
        # quadrature data
        file_path = (data_folder + 'dconvq')
        dconvq = np.array(data_parser.read_data(file_path))
        # combined IQ
        data_filt = np.zeros((NoE, SpE), dtype=complex)
        for i in range(0, NoE):
            data_filt[i, :] = \
                dconvi[i * SpE:(i + 1) * SpE] + 1j * \
                dconvq[i * SpE:(i + 1) * SpE]
        # normalize data
        data_filt = np.divide(data_filt, NoE * 25e3)

    # scan rotation
    if en_ext_param:
        data_filt = data_filt * np.exp(-1j * thetaref)
        theta = math.atan2(np.sum(np.imag(data_filt)),
                           np.sum(np.real(data_filt)))
    else:
        theta = math.atan2(np.sum(np.imag(data_filt)),
                           np.sum(np.real(data_filt)))
        if perform_rotation:
            data_filt = data_filt * np.exp(-1j * theta)

    if en_fig:  # plot filtered data
        echo_space = (1 / Sf) * np.linspace(1, SpE, SpE)  # in s
        plt.figure(2)
        for i in range(0, NoE):
            plt.plot((i * tE * 1e-6 + echo_space) * 1e3,
                     np.real(data_filt[i, :]),
                     'b',
                     linewidth=0.4)
            plt.plot((i * tE * 1e-6 + echo_space) * 1e3,
                     np.imag(data_filt[i, :]),
                     'r',
                     linewidth=0.4)

    # find echo average, echo magnitude
    echo_avg = np.zeros(SpE, dtype=complex)
    for i in range(0, NoE):
        echo_avg += (data_filt[i, :] / NoE)

    if en_fig:  # plot echo shape
        plt.figure(3)
        plt.plot(tacq, np.abs(echo_avg), label='abs')
        plt.plot(tacq, np.real(echo_avg), label='real part')
        plt.plot(tacq, np.imag(echo_avg), label='imag part')
        plt.xlim(0, max(tacq))
        plt.title("Echo Shape")
        plt.xlabel('time(uS)')
        plt.ylabel('amplitude')
        plt.legend()

        # plot fft of the echosum
        plt.figure(4)
        zf = 8  # zero filling factor to get smooth curve
        ws = 2 * np.pi / (tacq[1] - tacq[0])  # in MHz
        wvect = np.linspace(-ws / 2, ws / 2, len(tacq) * zf)
        echo_zf = np.zeros(zf * len(echo_avg), dtype=complex)
        echo_zf[int((zf / 2) * len(echo_avg) -
                    len(echo_avg) / 2):int((zf / 2) * len(echo_avg) +
                                           len(echo_avg) / 2)] = echo_avg
        spect = zf * (np.fft.fftshift(np.fft.fft(np.fft.ifftshift(echo_zf))))
        plt.plot(wvect / (2 * np.pi), np.real(spect), label='real')
        plt.plot(wvect / (2 * np.pi), np.imag(spect), label='imag')
        plt.xlim(4 / max(tacq) * -1, 4 / max(tacq) * 1)
        plt.title("fft of the echo-sum")
        plt.xlabel('offset frequency(MHz)')
        plt.ylabel('Echo amplitude (a.u.)')
        plt.legend()

    # matched filtering
    a = np.zeros(NoE, dtype=complex)
    for i in range(0, NoE):
        if en_ext_param:
            a[i] = np.mean(
                np.multiply(data_filt[i, mtch_fltr_sta_idx:SpE],
                            np.conj(echoref_avg[mtch_fltr_sta_idx:SpE]))
            )  # find amplitude with reference matched filtering
        else:
            a[i] = np.mean(
                np.multiply(data_filt[i, mtch_fltr_sta_idx:SpE],
                            np.conj(echo_avg[mtch_fltr_sta_idx:SpE]))
            )  # find amplitude with matched filtering

    a_integ = np.sum(np.real(a))

    # def exp_func(x, a, b, c, d):
    #    return a * np.exp(-b * x) + c * np.exp(-d * x)
    def exp_func(x, a, b):
        return a * np.exp(-b * x)

    # average the first 5% of datas
    a_guess = np.mean(np.real(a[0:int(np.round(SpE / 20))]))
    #c_guess = a_guess
    # find min idx value where the value of (a_guess/exp) is larger than
    # real(a)
    # b_guess = np.where(np.real(a) == np.min(
    #    np.real(a[np.real(a) > a_guess / np.exp(1)])))[0][0] * tE / 1e6
    # this is dummy b_guess, use the one I made above this for smarter one
    # (but sometimes it doesn't work)
    b_guess = 10
    #d_guess = b_guess
    #guess = np.array([a_guess, b_guess, c_guess, d_guess])
    guess = np.array([a_guess, b_guess])

    try:  # try fitting data
        popt, pocv = curve_fit(exp_func, t_echospace, np.real(a), guess)

        # obtain fitting parameter
        a0 = popt[0]
        T2 = 1 / popt[1]

        # Estimate SNR/echo/scan
        f = exp_func(t_echospace, *popt)  # curve fit
        noise = np.std(np.imag(a))
        res = np.std(np.real(a) - f)
        snr_imag = a0 / (noise * math.sqrt(total_scan))
        snr_res = a0 / (res * math.sqrt(total_scan))
        snr = snr_imag

        # plot fitted line
        plt.figure(5)
        plt.cla()
        plt.plot(t_echospace * 1e3, f, label="fit")  # plot in milisecond
        plt.plot(t_echospace * 1e3, np.real(a) - f, label="residue")

    except:
        print('Problem in fitting. Set a0 and T2 output to 0\n')
        a0 = 0
        T2 = 0
        noise = 0
        res = 0
        snr = 0

    if en_fig:
        # plot data
        plt.figure(5)
        # plot in milisecond
        plt.plot(t_echospace * 1e3, np.real(a), label="real")
        # plot in milisecond
        plt.plot(t_echospace * 1e3, np.imag(a), label="imag")

        #plt.set(gca, 'FontSize', 12)
        plt.legend()
        plt.title('Filtered data')
        plt.xlabel('Time (mS)')
        plt.ylabel('Amplitude')

        plt.show()

    print('a0 = ' + '{0:.2f}'.format(a0))
    print('SNR/echo/scan = ' +
          'imag:{0:.2f}, res:{1:.2f}'.format(snr, snr_res))
    print('T2 = ' + '{0:.4f}'.format(T2 * 1e3) + ' msec')

    return (a, a_integ, a0, snr, T2, noise, res, theta, data_filt, echo_avg,
            t_echospace)
Ejemplo n.º 11
0
def compute_wobble(data_parent_folder, meas_folder, s11_min, en_fig, fig_num):
    data_folder = (data_parent_folder + '/' + meas_folder + '/')

    (param_list, value_list) = data_parser.parse_info(data_folder,
                                                      'acqu.par')  # read file
    freqSta = data_parser.find_value('freqSta', param_list, value_list)
    freqSto = data_parser.find_value('freqSto', param_list, value_list)
    freqSpa = data_parser.find_value('freqSpa', param_list, value_list)
    nSamples = data_parser.find_value('nSamples', param_list, value_list)
    freqSamp = data_parser.find_value('freqSamp', param_list, value_list)
    spect_bw = (freqSamp / nSamples) * 8

    file_name_prefix = 'wobbdata_'
    freqSw = np.arange(freqSta, freqSto, freqSpa)
    S11 = np.zeros(len(freqSw))
    for m in range(0, len(freqSw)):
        # for m in freqSw:
        file_path = (data_folder + file_name_prefix +
                     '{:4.3f}'.format(freqSw[m]))
        one_scan = np.array(data_parser.read_data(file_path))
        spectx, specty = nmr_fft(one_scan, freqSamp, 0)

        # FIND INDEX WHERE THE MAXIMUM SIGNAL IS PRESENT
        # PRECISE METHOD: find reflection at the desired frequency: creating precision problem where usually the signal shift a little bit from its desired frequency
        # ref_idx = abs(spectx - freqSw[m]) == min(abs(spectx - freqSw[m]))
        # BETTER METHOD: find reflection signal peak around the bandwidth
        ref_idx = (abs(spectx - freqSw[m]) <= spect_bw)

        S11[m] = max(specty[ref_idx])  # find reflection peak

    S11 = 20 * np.log10(S11 / max(S11))  # convert to dB scale
    S11_min10dB = (S11 <= s11_min)

    minS11 = min(S11)
    minS11_freq = freqSw[np.argmin(S11)]

    try:
        S11_fmin = min(freqSw[S11_min10dB])
        S11_fmax = max(freqSw[S11_min10dB])
    except:
        S11_fmin = 0
        S11_fmax = 0
        print('S11 requirement is not satisfied...')

    S11_bw = S11_fmax - S11_fmin

    if en_fig:
        plt.ion()
        fig = plt.figure(fig_num)
        fig.clf()
        ax = fig.add_subplot(1, 1, 1)
        line1, = ax.plot(freqSw, S11, 'r-')
        ax.set_ylim(-50, 0)
        ax.set_xlabel('Frequency [MHz]')
        ax.set_ylabel('S11 [dB]')
        ax.set_title("Reflection Measurement (S11) Parameter")
        ax.grid()

        fig.canvas.draw()
        fig.canvas.flush_events()

        # old code, freeze after plotting
        # plt.figure(fig_num)
        # plt.plot(freqSw, S11)
        # plt.title("Reflection Measurement (S11) Parameter")
        # plt.xlabel('Frequency [MHz]')
        # plt.ylabel('S11 [dB]')
        # plt.grid()
        # plt.show()

    # print(S11_fmin, S11_fmax, S11_bw)
    return S11_fmin, S11_fmax, S11_bw, minS11, minS11_freq
Ejemplo n.º 12
0
def compute_wobble(nmrObj, data_parent_folder, meas_folder, s11_min, en_fig,
                   fig_num):
    data_folder = (data_parent_folder + '/' + meas_folder + '/')

    (param_list, value_list) = data_parser.parse_info(data_folder,
                                                      'acqu.par')  # read file
    freqSta = data_parser.find_value('freqSta', param_list, value_list)
    freqSto = data_parser.find_value('freqSto', param_list, value_list)
    freqSpa = data_parser.find_value('freqSpa', param_list, value_list)
    nSamples = data_parser.find_value('nSamples', param_list, value_list)
    freqSamp = data_parser.find_value('freqSamp', param_list, value_list)
    spect_bw = (freqSamp / nSamples) * 4  # determining the RBW

    file_name_prefix = 'wobbdata_'
    freqSw = np.arange(
        freqSta, freqSto + (freqSpa / 2), freqSpa
    )  # plus half is to remove error from floating point number operation
    S11 = np.zeros(len(freqSw))
    S11_ph = np.zeros(len(freqSw))
    for m in range(0, len(freqSw)):
        # for m in freqSw:
        file_path = (data_folder + file_name_prefix +
                     '{:4.3f}'.format(freqSw[m]))
        one_scan = np.array(data_parser.read_data(file_path))

        os.remove(file_path)  # delete the file after use

        # find voltage at the input of ADC in mV
        one_scan = one_scan * nmrObj.uvoltPerDigit / 1e3

        spectx, specty = nmr_fft(one_scan, freqSamp, 0)

        # FIND INDEX WHERE THE MAXIMUM SIGNAL IS PRESENT
        # PRECISE METHOD: find reflection at the desired frequency: creating precision problem where usually the signal shift a little bit from its desired frequency
        # ref_idx = abs(spectx - freqSw[m]) == min(abs(spectx - freqSw[m]))
        # BETTER METHOD: find reflection signal peak around the bandwidth
        ref_idx = (abs(spectx - freqSw[m]) <= (spect_bw / 2))

        # S11[m] = max( abs( specty[ref_idx] ) )  # find reflection peak
        S11[m] = np.mean(abs(
            specty[ref_idx]))  # compute the mean of amplitude inside RBW
        S11_ph[m] = np.mean(np.angle(specty[ref_idx])) * (360 / (2 * np.pi))

    S11dB = 20 * np.log10(S11 / max(S11))  # convert to dB scale
    S11_min10dB = (S11dB <= s11_min)

    minS11 = min(S11dB)
    minS11_freq = freqSw[np.argmin(S11dB)]

    try:
        S11_fmin = min(freqSw[S11_min10dB])
        S11_fmax = max(freqSw[S11_min10dB])
    except:
        S11_fmin = 0
        S11_fmax = 0
        print('S11 requirement is not satisfied...')

    S11_bw = S11_fmax - S11_fmin

    if en_fig:
        plt.ion()
        fig = plt.figure(fig_num)
        fig.clf()
        ax = fig.add_subplot(211)
        line1, = ax.plot(freqSw, S11dB, 'r-')
        ax.set_ylim(-35, 0)
        ax.set_ylabel('S11 [dB]')
        ax.set_title("Reflection Measurement (S11) Parameter")
        ax.grid()

        bx = fig.add_subplot(212)
        bx.plot(freqSw, S11_ph, 'r-')
        bx.set_xlabel('Frequency [MHz]')
        bx.set_ylabel('Phase (deg)')
        bx.set_title(
            'incorrect phase due to non-correlated transmit and sampling')
        bx.grid()

        fig.canvas.draw()
        fig.canvas.flush_events()

        plt.savefig(data_folder + 'wobble.png')

    # write S11 to a file
    with open(data_folder + 'S11.txt', 'w') as f:
        for (a, b, c) in zip(freqSw, S11dB, S11_ph):
            f.write('{:-8.3f},{:-8.3f},{:-7.1f}\n'.format(a, b, c))

    # print(S11_fmin, S11_fmax, S11_bw)
    return S11, S11_fmin, S11_fmax, S11_bw, minS11, minS11_freq