Figures for piezo paper

In [1]:
import math
import numpy as np

#import matplotlib as mlt
#mlt.use('nbagg') # makes plots interactive for zooming. Need to restart notebook to change this setting.

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from mpl_toolkits.mplot3d import Axes3D
# enable plots to be shown in cells - needed when saving plots to files, disables interactivity
%matplotlib inline

Cref = 25.7e-9                           # Farad
Lv = 0.223                               # string vibrating length, m
linear_density = 0.000593172915500839    # string linear density, kg/m
break_angle = 2.6                        # degrees
sin_br_angle = np.sin(np.deg2rad(break_angle))

def compute_Xt(Ra, Rb, omega_array, bridge_op_mag):
    Rasq = Ra * Ra
    Rbsq = Rb * Rb
    RaRb = Ra * Rb
    RasqRbsq = Rasq * Rbsq
    Msq = bridge_op_mag * bridge_op_mag
    one_minus_Msq = 1.0 - Msq
    Xr = -1.0/(omega_array*Cref)
    Xrsq = Xr * Xr
    a_term = (Rbsq * one_minus_Msq) - (Msq * Xrsq)
    b_term = (-2.0 * RaRb) * Xr
    c_term = (Rasq * Xrsq * one_minus_Msq) - (RasqRbsq * Msq)
    sqrt_argument = (b_term*b_term)-(4.0*a_term*c_term)
    sqrt_term = np.sqrt(sqrt_argument)
    Xt = -(b_term + sqrt_term) / (2.0 * a_term)
    return Xt

def read_Z_scan_file1(filename, length, frequency_array, bridge_op_array):
    f = open(filename, "r")
    # discard text at start of file
    for i in range(8):
        f.readline()
    # read in data
    for i in range(length):
        line = f.readline()
        words = line.split(',')
        value = float(words[2])
        if value >= 0.0:
            frequency_array.append(float(words[0]))
            bridge_op_array.append(value)
    # close file
    f.close()

def read_Z_scan_file(filename, length, frequency_array, bridge_op_mag, bridge_op_phase):
    f = open(filename, "r")
    # discard text at start of file
    for i in range(9):
        f.readline()
    # read in data
    for i in range(length):
        line = f.readline()
        words = line.split(',')
        frequency_array.append(float(words[0]))
        bridge_op_mag.append(float(words[7]))
        bridge_op_phase.append(float(words[8]))
    # close file
    f.close()

def read_Z_scan_file_pair(file1, file2, length, frequency_array, bridge_op_mag, bridge_op_phase):
    f1 = open(file1, "r")
    f2 = open(file2, "r")
    # discard text at start of file
    for i in range(9):
        f1.readline()
        f2.readline()
    # read in data
    for i in range(length):
        line1 = f1.readline()
        line2 = f2.readline()
        words1 = line1.split(',')
        words2 = line2.split(',')
        real_part = ((float(words1[7])*np.cos(float(words1[8]))) + (float(words2[7])*np.cos(float(words2[8])))) / 2.0
        imag_part = ((float(words1[7])*np.sin(float(words1[8]))) + (float(words2[7])*np.sin(float(words2[8])))) / 2.0
        frequency_array.append(float(words1[0]))
        bridge_op_mag.append(np.sqrt((real_part*real_part)+(imag_part*imag_part)))
        bridge_op_phase.append(np.arctan2(imag_part,real_part))
    # close files
    f1.close()
    f2.close()
 

Normalised bridge output for the unloaded capsule

In [2]:
ref_frequency=[]
ref_bridge_op_mag=[]
ref_bridge_op_phase=[]
# read in entries from 100 to 6000 Hz in 1 Hz steps
# entries at 4449, 4488, 4743, 4785, 4906, 4908, and 4950 Hz removed due to bad sine wave fits
read_Z_scan_file(".\Z_scan_output_2017.06.26.09.17a.txt", 5894, ref_frequency, ref_bridge_op_mag, ref_bridge_op_phase)
            
plt.figure(figsize=(6,4.5))
plt.plot(ref_frequency, ref_bridge_op_mag, 'k')
#plt.axis([0, 6000, 0, 0.5])
plt.xlabel('Frequency (Hz)')
plt.ylabel('Normalised bridge output')
plt.tight_layout()
plt.show()

Equivalent capacitance of unloaded capsule

In [3]:
Ra = 9380.0
Rb = 10940.0

ref_frequency_array = np.asarray(ref_frequency)
ref_frequency_kHz = ref_frequency_array / 1000.0
ref_bridge_op_mag_array = np.asarray(ref_bridge_op_mag)
ref_bridge_op_phase_array = np.asarray(ref_bridge_op_phase)
omega = (2.0 * math.pi) * ref_frequency_array

X_unloaded = compute_Xt(Ra, Rb, omega, ref_bridge_op_mag_array)
X_absolute = np.abs(X_unloaded)
C_unloaded = -1.0e9/(omega * X_unloaded)  # nF

# low-frequency capacitance - simple average between 200 Hz and 1 kHz (1 Hz steps)
Cfit = np.average(C_unloaded[100:900])/1e9  # Farad
X_fit = 1.0/(omega*Cfit)

print(Cfit*1e9)

plt.figure(figsize=(6,4.5))
plt.plot(ref_frequency, C_unloaded, 'k')
#plt.axis([0, 6000, 0, 35])
plt.xlabel('Frequency (Hz)')
plt.ylabel('Equivalent capacitance (nF)')
plt.tight_layout()
plt.show()
24.3864736934

Equivalent circuit model of unloaded capsule

In [4]:
Ra = 9380.0
Rb = 10940.0

f_s = 4300  # resonant frequency, at |Z| min - set manually based on transducer specification of 4.2 +/- 0.5 kHz 
f_p = ref_frequency_array[3000+np.argmax(ref_bridge_op_mag_array[3000:])] # frequency at bridge_op max (equiv. |Z| max)
print(Cfit, f_s, f_p)

C0 = Cfit * (f_s/f_p)**2
C1 = Cfit - C0
L1 = 1.0 / (C1 * (2.0 * math.pi * f_s)**2)
R1 = 320.0   # set manually
print(C0, C1, L1, R1)

Z0 = 0.0 - 1j/(omega*C0)
Z1 = R1 + 1j * ((omega*L1) - 1.0/(omega*C1))
Z_model = Z0 * Z1 / (Z0 + Z1)

# bridge output
Za = complex(Ra, 0.0)
Zb = complex(Rb, 0.0)
ref_bridge_op_model = (Zb / (Rb - 1j / (omega * Cref))) - (Za / (Za + Z_model))

plt.figure(figsize=(6,4.5))
plt.plot(ref_frequency_kHz, ref_bridge_op_mag, 'k')
plt.plot(ref_frequency_kHz, np.abs(ref_bridge_op_model), 'b--')
#plt.axis([0, 6, 0, 0.5])
plt.xlabel('Frequency (kHz)')
plt.ylabel('Normalised bridge output')
plt.tight_layout()
plt.show()
2.43864736934e-08 4300 4602.0
2.12908346914e-08 3.09563900203e-09 0.442540520598 320.0

Combined plot for unloaded capsule

In [5]:
plt.figure(figsize=(6,6))

ax1=plt.subplot2grid((2,2), (0,0), colspan=2)
ax1.plot(ref_frequency_kHz, ref_bridge_op_mag, 'k', label = 'measured response')
ax1.plot(ref_frequency_kHz, np.abs(ref_bridge_op_model), 'b--', label = 'equivalent circuit model')
ax1.legend(loc='best', prop={'size':9})
ax1.axis([0, 6, 0, 0.5])
ax1.set_xlabel('Frequency (kHz)')
ax1.set_ylabel('Normalised bridge output')

ax2=plt.subplot2grid((2,2), (1,0))
ax2.loglog(ref_frequency[0:1900], X_absolute[0:1900], 'k')
ax2.loglog(ref_frequency[1900:], X_absolute[1900:], 'k:')
ax2.loglog(ref_frequency, X_fit, 'b--')
ax2.set_xlabel('Frequency (Hz)')
ax2.set_ylabel('Derived reactance ($\Omega$)')

ax3=plt.subplot2grid((2,2), (1,1))
ax3.plot(ref_frequency_kHz[0:1900], C_unloaded[0:1900], 'k')
ax3.plot(ref_frequency_kHz[1900:], C_unloaded[1900:], 'k:')
ax3.axis([0, 6, 0, 35])
ax3.set_xlabel('Frequency (kHz)')
ax3.set_ylabel('Equivalent capacitance (nF)')

plt.tight_layout()

def get_axis_limits(ax, scale_x=0.9, scale_y=0.9):
    return ax.get_xlim()[1]*scale_x, ax.get_ylim()[1]*scale_y

ax1.annotate('(a)', xy=get_axis_limits(ax1, 0.95, 0.89))
ax2.annotate('(b)', xy=get_axis_limits(ax2, 0.55, 0.55))
ax3.annotate('(c)', xy=get_axis_limits(ax3, 0.88, 0.89))

plt.savefig('./piezo_unloaded.eps', format='eps', dpi=1000)
#plt.show()

Capacitance vs load

In [6]:
def comp_cap_load1(Ra, Rb, filename, freq1, freq2, sense, length, start, stop, load_array, cap_array):
    frequency=[]
    bridge_op=[]
    string_freq = (freq1+freq2)/2.0
    wave_velocity = 2.0 * Lv * string_freq
    tension_est = 0.9 * wave_velocity * wave_velocity * linear_density
    load_est = sense * tension_est * sin_br_angle
    # read in impedance response
    read_Z_scan_file1(filename, length, frequency, bridge_op)
    frequency_array = np.asarray(frequency)
    bridge_op_array = np.asarray(bridge_op)
    omega = (2.0 * math.pi) * frequency_array
    Xt = compute_Xt(Ra, Rb, omega, bridge_op_array)
    Ct = -1.0e9/(omega * Xt)  # nF
    Ct_avge = np.average(Ct[start:stop])
    #print(load_est,Ct_avge)
    load_array.append(load_est)
    cap_array.append(Ct_avge)

def comp_cap_load(Ra, Rb, filename, freq1, freq2, sense, length, start, stop, load_array, cap_array):
    frequency=[]
    bridge_op_mag=[]
    bridge_op_phase=[]
    string_freq = (freq1+freq2)/2.0
    wave_velocity = 2.0 * Lv * string_freq
    tension_est = 0.9 * wave_velocity * wave_velocity * linear_density
    load_est = sense * tension_est * sin_br_angle
    # read in impedance response
    read_Z_scan_file(filename, length, frequency, bridge_op_mag, bridge_op_phase)
    frequency_array = np.asarray(frequency)
    bridge_op_array = np.asarray(bridge_op_mag)
    omega = (2.0 * math.pi) * frequency_array
    Xt = compute_Xt(Ra, Rb, omega, bridge_op_array)
    Ct = -1.0e9/(omega * Xt)  # nF
    Ct_avge = np.average(Ct[start:stop])
    #print(load_est,Ct_avge)
    load_array.append(load_est)
    cap_array.append(Ct_avge)

# calculate capacitance vs load response for tests using normal support
# data files have entries from 100 to 200 Hz in 1 Hz steps
# use all of them to compute average capacitance
Ra = 9000.0
Rb = 11110.0
#
load1=[]
cap1=[]
comp_cap_load1(Ra, Rb, ".\Z_scan_output_2017.05.27.16.17.txt", 374.031, 402.164, 1.0, 101, 0, 101, load1, cap1)   #  30 MPa
comp_cap_load1(Ra, Rb, ".\Z_scan_output_2017.05.27.16.24.txt", 527.954, 516.987, 1.0, 101, 0, 101, load1, cap1)   #  60 MPa
comp_cap_load1(Ra, Rb, ".\Z_scan_output_2017.05.27.16.30.txt", 657.463, 637.150, 1.0, 101, 0, 101, load1, cap1)   #  90 MPa
comp_cap_load1(Ra, Rb, ".\Z_scan_output_2017.05.27.16.33.txt", 770.950, 746.536, 1.0, 101, 0, 101, load1, cap1)   # 120 MPa
comp_cap_load1(Ra, Rb, ".\Z_scan_output_2017.05.27.16.37.txt", 866.032, 842.381, 1.0, 101, 0, 101, load1, cap1)   # 150 MPa
comp_cap_load1(Ra, Rb, ".\Z_scan_output_2017.05.27.16.40.txt", 944.233, 920.773, 1.0, 101, 0, 101, load1, cap1)   # 180 MPa
comp_cap_load1(Ra, Rb, ".\Z_scan_output_2017.05.27.16.46.txt", 1010.609, 977.421, 1.0, 101, 0, 101, load1, cap1)  # 210 MPa
# fitted line for tests using normal support
fit = np.polyfit(load1, cap1, 1)
fit_fn1 = np.poly1d(fit)
            
# calculate capacitance vs load response for tests using wire loop support
# data files have entries from 200 to 400 Hz in 1 Hz steps
# use all of them to compute average capacitance
Ra = 9380.0
Rb = 10940.0
#
load2=[]
cap2=[]
comp_cap_load(Ra, Rb, ".\Z_scan_output_2017.06.26.08.53.txt", 841.045, 840.759, -1.0, 201, 0, 201, load2, cap2)   #  other way up
comp_cap_load(Ra, Rb, ".\Z_scan_output_2017.06.26.09.01.txt", 843.048, 843.143, 1.0, 201, 0, 201, load2, cap2)   #  normal way up
# fitted line for tests using wire loop support, plus result for unloaded transducer - measured on same day (above)
load2.append(0.0)
cap2.append(Cfit*1e9)
fit = np.polyfit(load2, cap2, 1)
fit_fn2 = np.poly1d(fit)

# calculate capacitance vs load response for second set of tests using normal support
# data files have entries from 200 to 400 Hz in 1 Hz steps
# use all of them to compute average capacitance
Ra = 9440.0
Rb = 11020.0
#
load3=[]
cap3=[]
comp_cap_load(Ra, Rb, ".\Z_scan_output_2017.07.02.11.36.txt", 387.001, 391.293, 1.0, 201, 0, 201, load3, cap3)
comp_cap_load(Ra, Rb, ".\Z_scan_output_2017.07.02.11.53.txt", 519.657, 516.605, 1.0, 201, 0, 201, load3, cap3)
comp_cap_load(Ra, Rb, ".\Z_scan_output_2017.07.02.12.10.txt", 631.046, 627.136, 1.0, 201, 0, 201, load3, cap3)
comp_cap_load(Ra, Rb, ".\Z_scan_output_2017.07.02.12.27.txt", 750.351, 745.773, 1.0, 201, 0, 201, load3, cap3)
comp_cap_load(Ra, Rb, ".\Z_scan_output_2017.07.02.12.46.txt", 827.694, 824.070, 1.0, 201, 0, 201, load3, cap3)
comp_cap_load(Ra, Rb, ".\Z_scan_output_2017.07.02.13.08.txt", 908.279, 904.369, 1.0, 201, 0, 201, load3, cap3)
comp_cap_load(Ra, Rb, ".\Z_scan_output_2017.07.02.13.25.txt", 979.710, 974.369, 1.0, 201, 0, 201, load3, cap3)
fit = np.polyfit(load3, cap3, 1)
fit_fn3 = np.poly1d(fit)


plt.figure(figsize=(6,4.5))
x1 = -5.5
x2 = 5.5
plt.plot(0.0, Cfit*1e9, 'ko', markersize = 6, label='unloaded')
plt.plot(load2[0:2], cap2[0:2], 'go', markeredgecolor = 'g', markersize = 6, label='wire loop support')
plt.plot([x1, x2], [fit_fn2(x1),fit_fn2(x2)], 'g--')
plt.plot(load1, cap1, 'bo', markeredgecolor = 'b', markersize = 6, label='normal support')
plt.plot([x1, x2], [fit_fn1(x1),fit_fn1(x2)], 'b--')
plt.plot(load3, cap3, 'cyan', ls='none', marker='o', markeredgecolor = 'cyan', markersize = 6, label='normal support 2')
plt.plot([x1, x2], [fit_fn3(x1),fit_fn3(x2)], 'cyan', ls='--')
plt.axis([x1, x2, 23, 27])
plt.xticks([-5,-4,-3,-2,-1,0,1,2,3,4,5])
plt.legend(loc='best', prop={'size':9})
plt.xlabel('Load (N)')
plt.ylabel('Capacitance (nF)')
plt.tight_layout()
plt.savefig('./piezo_capacitance_vs_load.eps', format='eps', dpi=1000)
#plt.show()

Bridge output response with string

In [7]:
# read in impedance response for capsule with string: 100 to 6000 Hz in 1 Hz steps
# entries at 2132 and 2315 Hz removed due to bad sine wave fits
frequency=[]
bridge_op_mag=[]
bridge_op_phase=[]
read_Z_scan_file(".\Z_scan_output_2017.06.27.09.10a.txt", 5899, frequency, bridge_op_mag, bridge_op_phase)

# refit equivalent circuit model
Ra = 9380.0
Rb = 10940.0
frequency_array = np.asarray(frequency)
bridge_op_mag_array = np.asarray(bridge_op_mag)
omega = (2.0 * math.pi) * frequency_array
X_loaded = compute_Xt(Ra, Rb, omega, bridge_op_mag_array)
C_loaded = -1.0e9/(omega * X_loaded)  # nF
# low-frequency capacitance - simple average between 200 and 400 Hz (1 Hz steps)
Cfit2 = np.average(C_loaded[100:301])/1e9  # Farad
#Cfit2=Cfit-1.5e-9
f_s = 2128.0  # resonant frequency, at |Z| min - set manually 
f_p = frequency[1900+np.argmax(bridge_op_mag_array[1900:])] # frequency at |Z| max
print(Cfit2, f_s, f_p, Cfit2-Cfit)
C0 = Cfit2 * (f_s/f_p)**2
C1 = Cfit2 - C0
L1 = 1.0 / (C1 * (2.0 * math.pi * f_s)**2)
R1 = 1300.0   # set manually
print(C0, C1, L1, R1)
Z0 = 0.0 - 1j/(omega*C0)
Z1 = R1 + 1j * ((omega*L1) - 1.0/(omega*C1))
Z_model = Z0 * Z1 / (Z0 + Z1)
# bridge output
Za = complex(Ra, 0.0)
Zb = complex(Rb, 0.0)
bridge_op_model = (Zb / (Rb - 1j / (omega * Cref))) - (Za / (Za + Z_model))

#read in FFT of manual pluck response
FFT_response=[]
with open(".\\frequency_response_after_connected_2017.06.27_2.txt", "r") as f:
    # discard first two lines (sampling rate and number of points)
    line = f.readline()
    words = line.split()
    sampling_rate=float(words[0])
    f.readline()
    # read in 240,001 entries
    for i in range(240001):
        line = f.readline()
        words = line.split()
        FFT_response.append(float(words[0]))

#corresponding frequencies
FFT_frequency = np.linspace(0, sampling_rate/2.0, 240001)
#scaled FFT response        
FFT_array=np.asarray(FFT_response)
FFT_max = np.max(FFT_array)
FFT_scaled = 0.45 * FFT_array / FFT_max

plt.figure(figsize=(6,4.5))
plt.plot(ref_frequency, ref_bridge_op_mag, 'k:', label='unloaded transducer')
plt.plot(FFT_frequency, FFT_scaled, 'r', label='FFT of manual pluck')
plt.plot(frequency, bridge_op_mag, 'k', lw=1.5, label='loaded transducer')
#plt.plot(frequency, np.abs(bridge_op_model), 'b--', label = 'equivalent circuit model')
#plt.axis([0, 6000, 0, 0.65])
plt.axis([0, 6000, 0, 0.6])
plt.legend(loc=2, prop={'size':9})
plt.xlabel('Frequency (Hz)')
plt.ylabel('Bridge output and scaled FFT')
plt.tight_layout()
plt.savefig('./piezo_loaded.eps', format='eps', dpi=1000)
#plt.show()
2.32673523463e-08 2128.0 2201.0 -1.11912134714e-09
2.17495426343e-08 1.51780971196e-09 3.68535805527 1300.0

Changes to bridge balance

In [8]:
# read in impedance response for Ra = 8010, Rb = 12120: 820 to 850 Hz in 0.1 Hz steps; 7 Vpp, 0.3 V/div
frequency_1=[]
bridge_op_mag_1=[]
bridge_op_phase_1=[]
read_Z_scan_file(".\Z_scan_output_2017.06.29.15.10a.txt", 300, frequency_1, bridge_op_mag_1, bridge_op_phase_1)  
real_part1 = (bridge_op_mag_1 * np.cos(bridge_op_phase_1))
imag_part1 = (bridge_op_mag_1 * np.sin(bridge_op_phase_1))
real_offset1 = (np.average(real_part1[:10])+np.average(real_part1[-10:]))/2.0
imag_offset1 = (np.average(imag_part1[:10])+np.average(imag_part1[-10:]))/2.0
magnitude1 = np.sqrt((real_part1-real_offset1)**2+(imag_part1-imag_offset1)**2)
str_freq_1 = frequency_1[np.argmax(magnitude1)] # frequency at maximum
print(real_offset1, imag_offset1, str_freq_1)

# read in impedance response for Ra = 9440, Rb = 11020: 820 to 850 Hz in 0.1 Hz steps; 7 Vpp, 0.3 V/div
frequency_2=[]
bridge_op_mag_2=[]
bridge_op_phase_2=[]
read_Z_scan_file(".\Z_scan_output_2017.06.29.15.19.txt", 301, frequency_2, bridge_op_mag_2, bridge_op_phase_2)  
real_part2 = (bridge_op_mag_2 * np.cos(bridge_op_phase_2))
imag_part2 = (bridge_op_mag_2 * np.sin(bridge_op_phase_2))
real_offset2 = (np.average(real_part2[:10])+np.average(real_part2[-10:]))/2.0
imag_offset2 = (np.average(imag_part2[:10])+np.average(imag_part2[-10:]))/2.0
magnitude2 = np.sqrt((real_part2-real_offset2)**2+(imag_part2-imag_offset2)**2)
str_freq_2 = frequency_2[np.argmax(magnitude2)] # frequency at maximum
print(real_offset2, imag_offset2, str_freq_2)

# read in impedance response for Ra = 10600, Rb = 9640: 820 to 850 Hz in 0.1 Hz steps; 7 Vpp, 0.3 V/div
frequency_3=[]
bridge_op_mag_3=[]
bridge_op_phase_3=[]
read_Z_scan_file(".\Z_scan_output_2017.06.29.14.37.txt", 301, frequency_3, bridge_op_mag_3, bridge_op_phase_3)  
real_part3 = (bridge_op_mag_3 * np.cos(bridge_op_phase_3))
imag_part3 = (bridge_op_mag_3 * np.sin(bridge_op_phase_3))
real_offset3 = (np.average(real_part3[:10])+np.average(real_part3[-10:]))/2.0
imag_offset3 = (np.average(imag_part3[:10])+np.average(imag_part3[-10:]))/2.0
magnitude3 = np.sqrt((real_part3-real_offset3)**2+(imag_part3-imag_offset3)**2)
str_freq_3 = frequency_3[np.argmax(magnitude3)] # frequency at maximum
print(real_offset3, imag_offset3, str_freq_3)

# read in impedance response for Ra = 11880, Rb = 8420: 820 to 850 Hz in 0.1 Hz steps; 7 Vpp, 0.3 V/div
frequency_4=[]
bridge_op_mag_4=[]
bridge_op_phase_4=[]
read_Z_scan_file(".\Z_scan_output_2017.06.29.14.48.txt", 301, frequency_4, bridge_op_mag_4, bridge_op_phase_4)  
real_part4 = (bridge_op_mag_4 * np.cos(bridge_op_phase_4))
imag_part4 = (bridge_op_mag_4 * np.sin(bridge_op_phase_4))
real_offset4 = (np.average(real_part4[:10])+np.average(real_part4[-10:]))/2.0
imag_offset4 = (np.average(imag_part4[:10])+np.average(imag_part4[-10:]))/2.0
magnitude4 = np.sqrt((real_part4-real_offset4)**2+(imag_part4-imag_offset4)**2)
str_freq_4 = frequency_4[np.argmax(magnitude4)] # frequency at maximum
print(real_offset4, imag_offset4, str_freq_4)

# read in impedance response for Ra = 12900, Rb = 7170: 820 to 850 Hz in 0.1 Hz steps; 7 Vpp, 0.3 V/div
frequency_5=[]
bridge_op_mag_5=[]
bridge_op_phase_5=[]
read_Z_scan_file(".\Z_scan_output_2017.06.29.15.00.txt", 301, frequency_5, bridge_op_mag_5, bridge_op_phase_5)  
real_part5 = (bridge_op_mag_5 * np.cos(bridge_op_phase_5))
imag_part5 = (bridge_op_mag_5 * np.sin(bridge_op_phase_5))
real_offset5 = (np.average(real_part5[:10])+np.average(real_part5[-10:]))/2.0
imag_offset5 = (np.average(imag_part5[:10])+np.average(imag_part5[-10:]))/2.0
magnitude5 = np.sqrt((real_part5-real_offset5)**2+(imag_part5-imag_offset5)**2)
str_freq_5 = frequency_5[np.argmax(magnitude5)] # frequency at maximum
print(real_offset5, imag_offset5, str_freq_5)

# apply frequency offset corrections - using the near-balanced response as reference
freq_offset1 = str_freq_1 - str_freq_3
frequency_1[:] = [x - freq_offset1 for x in frequency_1]
freq_offset2 = str_freq_2 - str_freq_3
frequency_2[:] = [x - freq_offset2 for x in frequency_2]
freq_offset4 = str_freq_4 - str_freq_3
frequency_4[:] = [x - freq_offset4 for x in frequency_4]
freq_offset5 = str_freq_5 - str_freq_3
frequency_5[:] = [x - freq_offset5 for x in frequency_5]
print(freq_offset1, freq_offset2, 0.0, freq_offset4, freq_offset5)

# plot
fig=plt.figure(figsize=(6,6))
#
# uncorrected magitude responses
ax1=fig.add_subplot(221)
ax1.plot(frequency_1, bridge_op_mag_1, 'r', label='$R_A$ = 8010, $R_B$ = 12120')
ax1.plot(frequency_2, bridge_op_mag_2, 'magenta', label='$R_A$ = 9440, $R_B$ = 11020')
ax1.plot(frequency_3, bridge_op_mag_3, 'g', label='$R_A$ = 10600, $R_B$ = 9640')
ax1.plot(frequency_4, bridge_op_mag_4, 'cyan', label='$R_A$ = 11880, $R_B$ = 8420')
ax1.plot(frequency_5, bridge_op_mag_5, 'b', label='$R_A$ = 12900, $R_B$ = 7170')
ax1.axis([820, 850, 0, 0.4])
ax1.set_xlabel('Frequency (Hz)')
ax1.set_ylabel('Measured bridge output')

# real parts
ax2=fig.add_subplot(222)
ax2.plot(frequency_1, real_part1, 'r', label='$R_A$ = 8010, $R_B$ = 12120')
ax2.plot(frequency_2, real_part2, 'magenta', label='$R_A$ = 9440, $R_B$ = 11020')
ax2.plot(frequency_3, real_part3, 'g', label='$R_A$ = 10600, $R_B$ = 9640')
ax2.plot(frequency_4, real_part4, 'cyan', label='$R_A$ = 11880, $R_B$ = 8420')
ax2.plot(frequency_5, real_part5, 'b', label='$R_A$ = 12900, $R_B$ = 7170')
ax2.axis([820, 850, -0.35, 0.4])
ax2.set_xlabel('Frequency (Hz)')
ax2.set_ylabel('Real part of bridge output')

# imaginary parts
ax3=fig.add_subplot(224)
ax3.plot(frequency_1, imag_part1, 'r', label='$R_A$ = 8010, $R_B$ = 12120')
ax3.plot(frequency_2, imag_part2, 'magenta', label='$R_A$ = 9440, $R_B$ = 11020')
ax3.plot(frequency_3, imag_part3, 'g', label='$R_A$ = 10600, $R_B$ = 9640')
ax3.plot(frequency_4, imag_part4, 'cyan', label='$R_A$ = 11880, $R_B$ = 8420')
ax3.plot(frequency_5, imag_part5, 'b', label='$R_A$ = 12900, $R_B$ = 7170')
ax3.axis([820, 850, -0.05, 0.25])
ax3.set_xlabel('Frequency (Hz)')
ax3.set_ylabel('Imaginary part of bridge output')

# corrected magitude responses
ax4=fig.add_subplot(223)
ax4.plot(frequency_1, magnitude1, 'r', label='$R_A$ = 8010, $R_B$ = 12120')
ax4.plot(frequency_2, magnitude2, 'magenta', label='$R_A$ = 9440, $R_B$ = 11020')
ax4.plot(frequency_3, magnitude3, 'g', label='$R_A$ = 10600, $R_B$ = 9640')
ax4.plot(frequency_4, magnitude4, 'cyan', label='$R_A$ = 11880, $R_B$ = 8420')
ax4.plot(frequency_5, magnitude5, 'b', label='$R_A$ = 12900, $R_B$ = 7170')
ax4.axis([820, 850, 0, 0.22])
ax4.set_xlabel('Frequency (Hz)')
ax4.set_ylabel('Balanced bridge output')

plt.tight_layout()  # need to do this before adjusting box heights

# Shrink subplot box heights by 10% on the bottom
box = ax1.get_position()
ax1.set_position([box.x0, box.y0 + box.height * 0.1, box.width, box.height * 0.9])
box = ax2.get_position()
ax2.set_position([box.x0, box.y0 + box.height * 0.1, box.width, box.height * 0.9])
box = ax3.get_position()
ax3.set_position([box.x0, box.y0 + box.height * 0.2, box.width, box.height * 0.9])
box = ax4.get_position()
ax4.set_position([box.x0, box.y0 + box.height * 0.2, box.width, box.height * 0.9])

ax1.text(0.1, 0.85,'(a)', transform=ax1.transAxes)
ax2.text(0.1, 0.85,'(b)', transform=ax2.transAxes)
ax3.text(0.1, 0.85,'(d)', transform=ax3.transAxes)
ax4.text(0.1, 0.85,'(c)', transform=ax4.transAxes)

ax4.legend(bbox_to_anchor=(1.13, -0.25), loc=9, ncol = 3, prop={'size':8.5}, borderaxespad=0.)

#plt.savefig('./piezo_bridge_balance.eps', format='eps', dpi=1000)
plt.show()
0.238927485342 -0.0421562939904 834.1
0.117603878729 -0.0231994423226 834.5
-0.00280964821549 0.00498298818875 834.6
-0.118724682899 0.029378234393 834.6
-0.230567523075 0.0440298156327 834.5
-0.5 -0.10000000000002274 0.0 0.0 -0.10000000000002274

3D plot

In [9]:
plt.figure(figsize=(6,6))

# uncorrected magitude responses
ax1=plt.subplot2grid((2,2), (0,0))
ax1.plot(frequency_1, bridge_op_mag_1, 'r', label='$R_A$ = 8010, $R_B$ = 12120')
ax1.plot(frequency_2, bridge_op_mag_2, 'magenta', label='$R_A$ = 9440, $R_B$ = 11020')
ax1.plot(frequency_3, bridge_op_mag_3, 'g', label='$R_A$ = 10600, $R_B$ = 9640')
ax1.plot(frequency_4, bridge_op_mag_4, 'cyan', label='$R_A$ = 11880, $R_B$ = 8420')
ax1.plot(frequency_5, bridge_op_mag_5, 'b', label='$R_A$ = 12900, $R_B$ = 7170')
ax1.axis([820, 850, 0, 0.4])
ax1.set_xlabel('Frequency (Hz)')
ax1.set_ylabel('Bridge output')

# corrected magitude responses
ax2=plt.subplot2grid((2,2), (0,1))
ax2.plot(frequency_1, magnitude1, 'r', label='$R_A$ = 8010, $R_B$ = 12120')
ax2.plot(frequency_2, magnitude2, 'magenta', label='$R_A$ = 9440, $R_B$ = 11020')
ax2.plot(frequency_3, magnitude3, 'g', label='$R_A$ = 10600, $R_B$ = 9640')
ax2.plot(frequency_4, magnitude4, 'cyan', label='$R_A$ = 11880, $R_B$ = 8420')
ax2.plot(frequency_5, magnitude5, 'b', label='$R_A$ = 12900, $R_B$ = 7170')
ax2.axis([820, 850, 0, 0.22])
ax2.set_xlabel('Frequency (Hz)')
ax2.set_ylabel('Balanced bridge output')

# uncorrected complex responses
ax3=plt.subplot2grid((2,2), (1,0), colspan=2, projection='3d')
#ax3.grid(False)
#ax3.xaxis.pane.set_edgecolor('black')
#ax3.yaxis.pane.set_edgecolor('black')
#ax3.zaxis.pane.set_edgecolor('black')
ax3.xaxis.pane.fill = False
ax3.yaxis.pane.fill = False
ax3.zaxis.pane.fill = False
#ax3.plot([820,850], [0,0], [0,0], 'k--')
ax3.plot([820,850], [imag_offset1, imag_offset1], [real_offset1, real_offset1], 'r--')
ax3.plot([820,850], [imag_offset2, imag_offset2], [real_offset2, real_offset2], 'magenta', ls='--')
ax3.plot([820,850], [imag_offset3, imag_offset3], [real_offset3, real_offset3], 'g--')
ax3.plot([820,850], [imag_offset4, imag_offset4], [real_offset4, real_offset4], 'cyan', ls='--')
ax3.plot([820,850], [imag_offset5, imag_offset5], [real_offset5, real_offset5], 'b--')
ax3.plot(frequency_1, imag_part1, real_part1, 'r', label='$R_A$ = 8010, $R_B$ = 12120')
ax3.plot(frequency_2, imag_part2, real_part2, 'magenta', label='$R_A$ = 9440, $R_B$ = 11020')
ax3.plot(frequency_3, imag_part3, real_part3, 'g', label='$R_A$ = 10600, $R_B$ = 9640')
ax3.plot(frequency_4, imag_part4, real_part4, 'cyan', label='$R_A$ = 11880, $R_B$ = 8420')
ax3.plot(frequency_5, imag_part5, real_part5, 'b', label='$R_A$ = 12900, $R_B$ = 7170')
ax3.set_xlim3d(820, 850)
ax3.set_xlabel('\n' + 'Frequency (Hz)', linespacing=2)
#ax3.set_ylim3d(-0.05, 0.25)
ax3.set_ylabel('Imaginary part')
ax3.yaxis.set_major_locator(ticker.MultipleLocator(0.1))
#ax3.set_zlim3d(-0.35, 0.4)
ax3.set_zlabel('Real part')
ax3.view_init(20,280)
#ax3.view_init(0,0)

plt.tight_layout()  # need to do this before adjusting box heights

# Adjust subplot box sizes
box = ax1.get_position()
ax1.set_position([box.x0, box.y0 + box.height * 0.2, box.width, box.height * 0.8])
box = ax2.get_position()
ax2.set_position([box.x0, box.y0 + box.height * 0.2, box.width, box.height * 0.8])
box = ax3.get_position()
ax3.set_position([box.x0 - box.width * 0.3, box.y0 + box.height * 0.0, box.width * 1.4, box.height * 1.2])

ax1.text(0.1, 0.85, '(a)', transform=ax1.transAxes)
ax2.text(0.1, 0.85, '(c)', transform=ax2.transAxes)
ax3.text3D(822, 0.2, 0.25, '(b)')

ax3.legend(bbox_to_anchor=(0.54, -0.2), loc=9, ncol = 3, prop={'size':8.5}, borderaxespad=0.)

plt.savefig('./piezo_bridge_balance_3D.eps', format='eps', dpi=1000)
#plt.show()

Comparison with model

In [10]:
# model interval is 820 to 850 Hz in 0.1 Hz steps
model_frequency = np.linspace(820, 850, 301)
model_omega = (2.0 * math.pi) * model_frequency

string_freq = 834.6          # string fundamental, Hz
#wave_velocity = 2.0 * Lv * string_freq
#tension_est = 0.9 * wave_velocity * wave_velocity * linear_density

# mechanical receptance ratio
Q1 = 655.0
#K = 90000 #45000
p1 = 1.0 #-0.0018 

complex_omega = model_omega + (1j * 0.0)
omega1 = complex(2.0 * math.pi * string_freq, 0.0) * complex(1.0, 1/(2.0 * Q1))
#R0 = complex(1.0/(K + (tension_est / Lv)), 0.0)
Rpole = complex(p1, 0.0) / (complex_omega - omega1)
#Romega = R0 + Rpole
Romega = Rpole

#scaler = complex(0.00002, 0.0)
scaler = complex(-3.6e-8, 0.0)
C_offset = -0.2e-9
C_model = Cfit2 - C_offset
print(Cfit, Cfit2, C_model, C_model-Cfit)

def compute_model(Ra, Rb, C0, Cref, scaler):
    Za = complex(Ra, 0.0)
    Zb = complex(Rb, 0.0)
    den0 = 1.0 + 1j * model_omega * Ra * C0
    den1 = 1.0 + 1j * model_omega * Rb * Cref
    num = 0.0 + 1j * (model_omega * Ra * scaler) * Romega
    bridge_op = (complex(1.0,0.0)/den0) - (complex(1.0,0.0)/den1) - (num / den0**2)
    return bridge_op

model_op_1 = compute_model(8010.0, 12120.0, C_model, Cref, scaler)
model_op_2 = compute_model(9440.0, 11020.0, C_model, Cref, scaler)
model_op_3 = compute_model(10600.0, 9640.0, C_model, Cref, scaler)
model_op_4 = compute_model(11880.0, 8420.0, C_model, Cref, scaler)
model_op_5 = compute_model(12900.0, 7170.0, C_model, Cref, scaler)

plt.figure(figsize=(6,4.5))
plt.plot(frequency_1, bridge_op_mag_1, 'r', label='$R_A$ = 8010, $R_B$ = 12120')
plt.plot(frequency_2, bridge_op_mag_2, 'magenta', label='$R_A$ = 9440, $R_B$ = 11020')
plt.plot(frequency_3, bridge_op_mag_3, 'g', label='$R_A$ = 10600, $R_B$ = 9640')
plt.plot(frequency_4, bridge_op_mag_4, 'cyan', label='$R_A$ = 11880, $R_B$ = 8420')
plt.plot(frequency_5, bridge_op_mag_5, 'b', label='$R_A$ = 12900, $R_B$ = 7170')
plt.plot(model_frequency, np.abs(model_op_1), 'r--')
plt.plot(model_frequency, np.abs(model_op_2), 'magenta', ls='--')
plt.plot(model_frequency, np.abs(model_op_3), 'g--')
plt.plot(model_frequency, np.abs(model_op_4), 'cyan', ls='--')
plt.plot(model_frequency, np.abs(model_op_5), 'b--')
plt.axis([820, 850, 0, 0.4])
plt.legend(loc='best', prop={'size':8})
plt.xlabel('Frequency (Hz)')
plt.ylabel('Bridge output')
plt.tight_layout()
plt.savefig('./piezo_comparison.eps', format='eps', dpi=1000)
#plt.show()
2.43864736934e-08 2.32673523463e-08 2.34673523463e-08 -9.19121347135e-10

Reduced drive levels

In [11]:
# all scans taken from 825 to 845 Hz in 0.1 Hz steps

# calculate calibration offsets from tests measuring a fixed capacitance (22 nF nominal)
# reference test at 8 Vpp, 0.3 V/div
frequency_1=[]
bridge_op_mag_1=[]
bridge_op_phase_1=[]
read_Z_scan_file_pair(".\Z_scan_output_2017.06.28.15.21.txt", ".\Z_scan_output_2017.06.28.15.26.txt",
                      201, frequency_1, bridge_op_mag_1, bridge_op_phase_1)  
ref_level = np.average(bridge_op_mag_1)
# test at 2.7 Vpp, 0.1 V/div, 34%
frequency_2=[]
bridge_op_mag_2=[]
bridge_op_phase_2=[]
read_Z_scan_file_pair(".\Z_scan_output_2017.06.28.15.32.txt", ".\Z_scan_output_2017.06.28.15.38.txt",
                      201, frequency_2, bridge_op_mag_2, bridge_op_phase_2)  
vert_offset2 = np.average(bridge_op_mag_2) - ref_level
# test at 0.8 Vpp, 0.03 V/div, 10%
frequency_3=[]
bridge_op_mag_3=[]
bridge_op_phase_3=[]
read_Z_scan_file_pair(".\Z_scan_output_2017.06.28.15.43.txt", ".\Z_scan_output_2017.06.28.15.51.txt",
                      201, frequency_3, bridge_op_mag_3, bridge_op_phase_3)  
vert_offset3 = np.average(bridge_op_mag_3) - ref_level
# test at 0.27 Vpp, 0.01 V/div, 3%
frequency_4=[]
bridge_op_mag_4=[]
bridge_op_phase_4=[]
read_Z_scan_file_pair(".\Z_scan_output_2017.06.28.15.56.txt", ".\Z_scan_output_2017.06.28.16.00.txt",
                      201, frequency_4, bridge_op_mag_4, bridge_op_phase_4)  
vert_offset4 = np.average(bridge_op_mag_4) - ref_level

print(ref_level, vert_offset2, vert_offset3, vert_offset4)

# read in impedance responses
# reference test at 8 Vpp, 0.3 V/div
frequency_1=[]
bridge_op_mag_1=[]
bridge_op_phase_1=[]
read_Z_scan_file(".\Z_scan_output_2017.06.28.17.33a.txt", 195, frequency_1, bridge_op_mag_1, bridge_op_phase_1)  
# test at 2.7 Vpp, 0.1 V/div, 34%
frequency_2=[]
bridge_op_mag_2=[]
bridge_op_phase_2=[]
read_Z_scan_file(".\Z_scan_output_2017.06.28.17.43.txt", 201, frequency_2, bridge_op_mag_2, bridge_op_phase_2)  
# test at 0.8 Vpp, 0.03 V/div, 10%
frequency_3=[]
bridge_op_mag_3=[]
bridge_op_phase_3=[]
read_Z_scan_file(".\Z_scan_output_2017.06.28.17.53a.txt", 200, frequency_3, bridge_op_mag_3, bridge_op_phase_3)  
# test at 0.27 Vpp, 0.01 V/div, 3%
frequency_4=[]
bridge_op_mag_4=[]
bridge_op_phase_4=[]
read_Z_scan_file(".\Z_scan_output_2017.06.28.18.04a.txt", 199, frequency_4, bridge_op_mag_4, bridge_op_phase_4)  

# add complex offset to responses and locate maxima
# reference test at 8 Vpp, 0.3 V/div
real_part1 = (bridge_op_mag_1 * np.cos(bridge_op_phase_1))
imag_part1 = (bridge_op_mag_1 * np.sin(bridge_op_phase_1))
real_offset1 = (np.average(real_part1[:10])+np.average(real_part1[-10:]))/2.0
imag_offset1 = (np.average(imag_part1[:10])+np.average(imag_part1[-10:]))/2.0
magnitude1 = np.sqrt((real_part1-real_offset1)**2+(imag_part1-imag_offset1)**2)
str_freq_1 = frequency_1[np.argmax(magnitude1)] # frequency at maximum
print(real_offset1, imag_offset1, str_freq_1)

# test at 2.7 Vpp, 0.1 V/div, 34%
real_part2 = (bridge_op_mag_2 * np.cos(bridge_op_phase_2))
imag_part2 = (bridge_op_mag_2 * np.sin(bridge_op_phase_2))
real_offset2 = (np.average(real_part2[:10])+np.average(real_part2[-10:]))/2.0
imag_offset2 = (np.average(imag_part2[:10])+np.average(imag_part2[-10:]))/2.0
magnitude2 = np.sqrt((real_part2-real_offset2)**2+(imag_part2-imag_offset2)**2)
str_freq_2 = frequency_2[np.argmax(magnitude2)] # frequency at maximum
print(real_offset2, imag_offset2, str_freq_2)
freq_offset2 = str_freq_2 - str_freq_1
frequency_2[:] = [x - freq_offset2 for x in frequency_2]

# test at 0.8 Vpp, 0.03 V/div, 10%
real_part3 = (bridge_op_mag_3 * np.cos(bridge_op_phase_3))
imag_part3 = (bridge_op_mag_3 * np.sin(bridge_op_phase_3))
real_offset3 = (np.average(real_part3[:10])+np.average(real_part3[-10:]))/2.0
imag_offset3 = (np.average(imag_part3[:10])+np.average(imag_part3[-10:]))/2.0
magnitude3 = np.sqrt((real_part3-real_offset3)**2+(imag_part3-imag_offset3)**2)
str_freq_3 = frequency_3[np.argmax(magnitude3)] # frequency at maximum
print(real_offset3, imag_offset3, str_freq_3)
freq_offset3 = str_freq_3 - str_freq_1
frequency_3[:] = [x - freq_offset3 for x in frequency_3]

# test at 0.27 Vpp, 0.01 V/div, 3%
real_part4 = (bridge_op_mag_4 * np.cos(bridge_op_phase_4))
imag_part4 = (bridge_op_mag_4 * np.sin(bridge_op_phase_4))
real_offset4 = (np.average(real_part4[:10])+np.average(real_part4[-10:]))/2.0
imag_offset4 = (np.average(imag_part4[:10])+np.average(imag_part4[-10:]))/2.0
magnitude4 = np.sqrt((real_part4-real_offset4)**2+(imag_part4-imag_offset4)**2)
str_freq_4 = frequency_4[np.argmax(magnitude4)] # frequency at maximum
print(real_offset4, imag_offset4, str_freq_4)
freq_offset4 = str_freq_4 - str_freq_1
frequency_4[:] = [x - freq_offset4 for x in frequency_4]

print(str_freq_1, freq_offset2, freq_offset3, freq_offset4)

# plot bridge output real part, with offsets applied
plt.figure(figsize=(6,4.5))
#
plt.plot(frequency_1, real_part1-real_offset1, 'g', label='8.0 Vpp')
plt.plot(frequency_2, real_part2-real_offset2, 'cyan', label='2.7 Vpp')
plt.plot(frequency_3, real_part3-real_offset3, 'b', label='0.80 Vpp')
plt.plot(frequency_4, real_part4-real_offset4, 'magenta', label='0.27 Vpp')
#plt.axis([825, 845, 0.03, 0.3])
plt.legend(loc=2, prop={'size':9})
plt.xlabel('Frequency (Hz)')
plt.ylabel('Balanced Real part')
plt.tight_layout()
plt.show()

# plot bridge output imag part, with offsets applied
plt.figure(figsize=(6,4.5))
#
plt.plot(frequency_1, imag_part1-imag_offset1, 'g', label='8.0 Vpp')
plt.plot(frequency_2, imag_part2-imag_offset2, 'cyan', label='2.7 Vpp')
plt.plot(frequency_3, imag_part3-imag_offset3, 'b', label='0.80 Vpp')
plt.plot(frequency_4, imag_part4-imag_offset4, 'magenta', label='0.27 Vpp')
#plt.axis([825, 845, 0.03, 0.3])
plt.legend(loc=2, prop={'size':9})
plt.xlabel('Frequency (Hz)')
plt.ylabel('Balanced Imaginary part')
plt.tight_layout()
plt.show()

# plot balanced magnitude responses
plt.figure(figsize=(6,4.5))
#
plt.plot(frequency_1, magnitude1, 'g', label='8.0 Vpp')
plt.plot(frequency_2, magnitude2, 'cyan', label='2.7 Vpp')
plt.plot(frequency_3, magnitude3, 'b', label='0.80 Vpp')
plt.plot(frequency_4, magnitude4, 'magenta', label='0.27 Vpp')
#plt.axis([825, 845, 0.03, 0.3])
plt.legend(loc=2, prop={'size':9})
plt.xlabel('Frequency (Hz)')
plt.ylabel('Balanced bridge output')
plt.tight_layout()
#plt.savefig('../tex/Figs/piezo_drive_levels_bal.eps', format='eps', dpi=1000)
plt.show()

# plot brige output
plt.figure(figsize=(6,4.5))
#
plt.plot(frequency_1, bridge_op_mag_1, 'g', label='8.0 Vpp input')
plt.plot(frequency_2, bridge_op_mag_2-vert_offset2, 'cyan', label='2.7 Vpp')
plt.plot(frequency_3, bridge_op_mag_3-vert_offset3, 'b', label='0.80 Vpp')
plt.plot(frequency_4, bridge_op_mag_4-vert_offset4, 'magenta', label='0.27 Vpp')
plt.axis([825, 845, 0.03, 0.3])
plt.legend(loc=2, prop={'size':9})
plt.xlabel('Frequency (Hz)')
plt.ylabel('Bridge output')
plt.tight_layout()
plt.savefig('./piezo_drive_levels.eps', format='eps', dpi=1000)
#plt.show()
0.0991773539586 -0.0102953538165 -0.014723930413 -0.0169275582613
0.120770745086 -0.0243926682207 835.5
0.112544567003 -0.019196276152 835.7
0.108687479257 -0.0166946739973 835.8
0.105801450067 -0.0165108595647 835.9
835.5 0.20000000000004547 0.2999999999999545 0.39999999999997726

Variations in string stress

In [12]:
# read in impedance response for stress of about 30 MPa; 365 to 390 Hz in 0.1 Hz steps
frequency_1=[]
bridge_op_mag_1=[]
bridge_op_phase_1=[]
read_Z_scan_file(".\Z_scan_output_2017.07.02.11.28.txt", 251, frequency_1, bridge_op_mag_1, bridge_op_phase_1)
# identify and add complex offset
real_part = (bridge_op_mag_1 * np.cos(bridge_op_phase_1))
imag_part = (bridge_op_mag_1 * np.sin(bridge_op_phase_1))
real_offset = (np.average(real_part[32:42])+np.average(real_part[-10:]))/2.0
imag_offset = (np.average(imag_part[32:42])+np.average(imag_part[-10:]))/2.0
magnitude1 = np.sqrt((real_part-real_offset)**2+(imag_part-imag_offset)**2)

# read in impedance response for stress of about 60 MPa; 520 to 545 Hz in 0.1 Hz steps
frequency_2=[]
bridge_op_mag_2=[]
bridge_op_phase_2=[]
read_Z_scan_file(".\Z_scan_output_2017.07.02.11.45.txt", 251, frequency_2, bridge_op_mag_2, bridge_op_phase_2)
# identify and add complex offset
real_part = (bridge_op_mag_2 * np.cos(bridge_op_phase_2))
imag_part = (bridge_op_mag_2 * np.sin(bridge_op_phase_2))
real_offset = (np.average(real_part[:10])+np.average(real_part[162:172]))/2.0
imag_offset = (np.average(imag_part[:10])+np.average(imag_part[162:172]))/2.0
magnitude2 = np.sqrt((real_part-real_offset)**2+(imag_part-imag_offset)**2)

# read in impedance response for stress of about 90 MPa; 640 to 665 Hz in 0.1 Hz steps
frequency_3=[]
bridge_op_mag_3=[]
bridge_op_phase_3=[]
read_Z_scan_file(".\Z_scan_output_2017.07.02.12.02.txt", 251, frequency_3, bridge_op_mag_3, bridge_op_phase_3)
# identify and add complex offset
real_part = (bridge_op_mag_3 * np.cos(bridge_op_phase_3))
imag_part = (bridge_op_mag_3 * np.sin(bridge_op_phase_3))
real_offset = (np.average(real_part[:10])+np.average(real_part[104:114]))/2.0
imag_offset = (np.average(imag_part[:10])+np.average(imag_part[104:114]))/2.0
magnitude3 = np.sqrt((real_part-real_offset)**2+(imag_part-imag_offset)**2)

# read in impedance response for stress of about 120 MPa; 750 to 775 Hz in 0.1 Hz steps
frequency_4=[]
bridge_op_mag_4=[]
bridge_op_phase_4=[]
read_Z_scan_file(".\Z_scan_output_2017.07.02.12.19.txt", 251, frequency_4, bridge_op_mag_4, bridge_op_phase_4)
# identify and add complex offset
real_part = (bridge_op_mag_4 * np.cos(bridge_op_phase_4))
imag_part = (bridge_op_mag_4 * np.sin(bridge_op_phase_4))
real_offset = (np.average(real_part[:10])+np.average(real_part[203:213]))/2.0
imag_offset = (np.average(imag_part[:10])+np.average(imag_part[203:213]))/2.0
magnitude4 = np.sqrt((real_part-real_offset)**2+(imag_part-imag_offset)**2)

# read in impedance response for stress of about 150 MPa; 830 to 855 Hz in 0.1 Hz steps
frequency_5=[]
bridge_op_mag_5=[]
bridge_op_phase_5=[]
read_Z_scan_file(".\Z_scan_output_2017.07.02.12.36a.txt", 250, frequency_5, bridge_op_mag_5, bridge_op_phase_5)
# identify and add complex offset
real_part = (bridge_op_mag_5 * np.cos(bridge_op_phase_5))
imag_part = (bridge_op_mag_5 * np.sin(bridge_op_phase_5))
real_offset = (np.average(real_part[:10])+np.average(real_part[214:224]))/2.0
imag_offset = (np.average(imag_part[:10])+np.average(imag_part[214:224]))/2.0
magnitude5 = np.sqrt((real_part-real_offset)**2+(imag_part-imag_offset)**2)

# read in impedance response for stress of about 180 MPa; 910 to 935 Hz in 0.1 Hz steps
frequency_6=[]
bridge_op_mag_6=[]
bridge_op_phase_6=[]
read_Z_scan_file(".\Z_scan_output_2017.07.02.12.58a.txt", 250, frequency_6, bridge_op_mag_6, bridge_op_phase_6)
# identify and add complex offset
real_part = (bridge_op_mag_6 * np.cos(bridge_op_phase_6))
imag_part = (bridge_op_mag_6 * np.sin(bridge_op_phase_6))
real_offset = (np.average(real_part[:10])+np.average(real_part[213:223]))/2.0
imag_offset = (np.average(imag_part[:10])+np.average(imag_part[213:223]))/2.0
magnitude6 = np.sqrt((real_part-real_offset)**2+(imag_part-imag_offset)**2)

# read in impedance response for stress of about 210 MPa; 984 to 1009 Hz in 0.1 Hz steps
frequency_7=[]
bridge_op_mag_7=[]
bridge_op_phase_7=[]
read_Z_scan_file(".\Z_scan_output_2017.07.02.13.17.txt", 251, frequency_7, bridge_op_mag_7, bridge_op_phase_7)
# identify and add complex offset
real_part = (bridge_op_mag_7 * np.cos(bridge_op_phase_7))
imag_part = (bridge_op_mag_7 * np.sin(bridge_op_phase_7))
real_offset = (np.average(real_part[:10])+np.average(real_part[186:196]))/2.0
imag_offset = (np.average(imag_part[:10])+np.average(imag_part[186:196]))/2.0
magnitude7 = np.sqrt((real_part-real_offset)**2+(imag_part-imag_offset)**2)

plt.figure(figsize=(6,4.5))
plt.plot(frequency_1[32:], bridge_op_mag_1[32:], 'b', label='30 MPa')
plt.plot(frequency_2[:172], bridge_op_mag_2[:172], 'g', label='60 MPa')
plt.plot(frequency_3[:114], bridge_op_mag_3[:114], 'magenta', label='90 MPa')
plt.plot(frequency_4[:213], bridge_op_mag_4[:213], 'cyan', label='120 MPa')
plt.plot(frequency_5[:224], bridge_op_mag_5[:224], 'b--', label='150 MPa')
plt.plot(frequency_6[:223], bridge_op_mag_6[:223], 'g--', label='180 MPa')
plt.plot(frequency_7[:196], bridge_op_mag_7[:196], 'magenta', linestyle='--', label='210 MPa')
plt.axis([300, 1100, 0.0, 0.3])
plt.legend(loc=2, prop={'size':9})
plt.xlabel('Frequency (Hz)')
plt.ylabel('Bridge output')
plt.tight_layout()
#plt.savefig('../tex/Figs/piezo_stress.eps', format='eps', dpi=1000)
plt.show()

plt.figure(figsize=(6,4.5))
plt.plot(frequency_1[32:], magnitude1[32:], 'b', label='30 MPa')
plt.plot(frequency_2[:172], magnitude2[:172], 'g', label='60 MPa')
plt.plot(frequency_3[:114], magnitude3[:114], 'magenta', label='90 MPa')
plt.plot(frequency_4[:213], magnitude4[:213], 'cyan', label='120 MPa')
plt.plot(frequency_5[:224], magnitude5[:224], 'b--', label='150 MPa')
plt.plot(frequency_6[:223], magnitude6[:223], 'g--', label='180 MPa')
plt.plot(frequency_7[:196], magnitude7[:196], 'magenta', linestyle='--', label='210 MPa')
plt.axis([300, 1100, 0.0, 0.23])
plt.legend(loc=2, prop={'size':9})
plt.xlabel('Frequency (Hz)')
plt.ylabel('Balanced bridge output')
plt.tight_layout()
plt.savefig('./piezo_stress_bal.eps', format='eps', dpi=1000)
#plt.show()

Tuning performance

In [13]:
from datetime import datetime
FMT = ' %H:%M:%S'

# read in data file without tuning correction
minutes_1=[]
tuning_error_1=[]
with open(".\Z_tuning_tracking_2017.07.05.07.57.txt", "r") as f:
    # discard text at start of file
    for i in range(3):
        f.readline()
    # read in first line of data
    line = f.readline()
    words = line.split(',')
    start_time = datetime.strptime(words[1], FMT)
    minutes_1.append(0.0)
    tuning_error_1.append(float(words[3]))
    # read in remaining data
    data = f.readlines()
    for line in data:
        words = line.split(',')
        tdelta = datetime.strptime(words[1], FMT) - start_time
        minutes = tdelta.seconds / 60.0
        minutes_1.append(minutes)
        tuning_error_1.append(float(words[3]))

# read in data file with tuning correction
minutes_2=[]
tuning_error_2=[]
with open(".\Z_tuning_performance_2017.07.05.09.27.txt", "r") as f:
    # discard text at start of file
    for i in range(3):
        f.readline()
    # read in first line of data
    line = f.readline()
    words = line.split(',')
    start_time = datetime.strptime(words[1], FMT)
    minutes_2.append(0.0)
    tuning_error_2.append(float(words[3]))
    # read in remaining data
    data = f.readlines()
    for line in data:
        words = line.split(',')
        tdelta = datetime.strptime(words[1], FMT) - start_time
        minutes = tdelta.seconds / 60.0
        minutes_2.append(minutes)
        tuning_error_2.append(float(words[3]))

plt.figure(figsize=(6,4.5))
plt.plot(minutes_1, tuning_error_1, 'magenta', label='uncompensated')
plt.plot(minutes_2, tuning_error_2, 'b', label='compensated')
# highlight heating periods
plt.plot(minutes_1[24:41], tuning_error_1[24:41], 'r', lw=1.5, label='heating applied')
plt.plot(minutes_2[26:43], tuning_error_2[26:43], 'r', lw=1.5)
plt.plot([0,80],[-3,-3],'k--')
plt.plot([0,80],[3,3],'k--')
#plt.axis([0, 80, -12, 2])
plt.legend(loc='best', prop={'size':9})
plt.xlabel('Time (minutes)')
plt.ylabel('Tuning error (cents)')
plt.tight_layout()
plt.savefig('./piezo_tuning.eps', format='eps', dpi=1000)
#plt.show()