def put_audio(self, ch1, ch2): """ Called by putOut() Send to the Output ch1, ch2 interleaved :param ch1: Force data :param ch2: Accel data :return: None """ self.recall_pickled_dict( ) # Load variables from the c.s.v. c.pickle_txt_fullpath SamplingFreq = v.SamplingFreq # Sampling Frequency, Hz samples_acq = v.samples_acq # n° of Samples acquired: c.n_blocks multiple Length = v.Length # Pile length, m Speed = v.Speed # Wave speed # FORMAT = pyaudio.paInt32 # Signed 32 bit (Little Endian byte order) CHANNELS = int(2) byte_width = 4 bytes_size = CHANNELS * byte_width * c.N_FRAMES # Bytes in each period """ Create the data_out alsaaudio object instance In PCM_NORMAL mode, the call will block if the kernel buffer is full, and until enough sound has been played to allow the sound data to be buffered. The call always returns the size of the data provided. Syntax: class alsaaudio.PCM(type=PCM_PLAYBACK, mode=PCM_NORMAL, device='default', cardindex=-1) To find the sound device use: aplay -L: ATTENTION: the device full name must be without spaces ! fuji: "sysdefault" Pisound direct hardware device: "hw:CARD=pisound,DEV=0" AudioBox 22VSL direct hardware device: "hw:CARD=VSL,DEV=0" """ if c.IS_RASPBERRY: print("Machine is Raspberry Pi") else: print("Machine is not a Raspberry Pi") print("Audio device is n°", c.SOUND_DEVICE) # try: # Creation af output audio object data_out = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL, device=c.SOUND_DEVICE) data_out.setchannels(CHANNELS) data_out.setrate(SampRate) data_out.setformat(FORMAT) data_out.setperiodsize(c.N_FRAMES) print(" - Success creating the output audio object.") except: print(" - Failed: an old audio process is running ?") print(" - Search the old process to be terminated.") # cmd = "ps aux | grep put_audio.py | head -n1" # Search it and try to terminate res = subprocess.getoutput(cmd) pid = res[9:15].strip() sts = os.WEXITSTATUS(os.system('kill -TERM ' + pid)) if sts is 0: # Process closed successfully print( " - Found the old process: terminated with success.") try: # Creation of output audio object print( " - Second try to create an output audio object." ) time.sleep(0.1) # Indispensable greather than 0.01 data_out = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL, device=c.SOUND_DEVICE) data_out.setchannels(CHANNELS) data_out.setrate(SampRate) data_out.setformat(FORMAT) data_out.setperiodsize(c.N_FRAMES) print( " - Success creating the output audio object." ) except: # Failed, doBeep(on_list=bip_5_S[0], off_list=bip_5_S[1]) # On Bip of 5s print( " - Failed second attempt to create the output audio object" ) # # else: print(" - Failed attempt to find an old process.") return # # # # END if sts: # END try:...except: # # """ x1x2: vector of interleaved channels, Ch1, Ch2, Output signal """ N = samples_acq # Number of samples # ch1 /= ( self.outGain * t.PI_INP_GAIN ) # Scaling Output from ~3 [t.PI_OUT_GAIN] to ~1, and for Input Gain ch2 /= (self.outGain * t.PI_INP_GAIN) ch1 *= c.full_scale_32 # Scaled to 32 bit ch2 *= c.full_scale_32 x1 = ch1.astype(np.int32) # Convert to int32, ch1 x2 = ch2.astype(np.int32) # Convert to int32, ch2 # pk1_32 = np.max(np.abs(x1)) * (self.outGain) pk2_32 = np.max(np.abs(x2)) * (self.outGain) rms1_32 = np.std(x1) * (self.outGain) rms2_32 = np.std(x2) * (self.outGain) # print( "\n" + "Values are referred to acquired data and not equals to the values putted on Output" ) print("rms1 = {:3.1f}".format((rms1_32 / c.full_scale_32) * 1000), "mV; ", "pk1 = {:3.1f}".format( (pk1_32 / c.full_scale_32) * 1000), "mV") print("rms2 = {:3.1f}".format((rms2_32 / c.full_scale_32) * 1000), "mV; ", "pk2 = {:3.1f}".format( (pk2_32 / c.full_scale_32) * 1000), "mV") # x1x2 = np.zeros(CHANNELS * samples_acq, dtype=np.int32) # Array for interleaved Ch1, Ch2 data x1x2[0::2] = x1 # Scaled Output gain, fill ch1: even indexes x1x2[ 1:: 2] = x2 # Scaled Output gain, fill ch1: odd indexes # Fill x1 at odd indexes: ch2 # out_big_buffer = bytearray(bytes_size * c.N_BLOCKS) out_big_buffer[:] = pack("l" * int(2 * c.N_OF_SAMPLES), *x1x2) # Pack from numpy.int32 to bytes out_short_buffer = bytearray( bytes_size) # Output array written one frame at time # os.system("sync") # To empty write queue, free CPU beg = 0 end = CHANNELS * c.N_FRAMES # for i in range(c.N_BLOCKS): # Write data to stream pyaudio object out_short_buffer[:] = out_big_buffer[beg:end] # As short blocks size = data_out.write(out_short_buffer) #print(size) beg = end end += bytes_size # # data_out.close()
def checkData(self, y1, y2): """ Called by ,fileLoad() :param y1: ch1 data array (Force) :param y2: ch2 data array (Accel) :return: None """ # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ # PUT A PULSE ON Ch1 TO TEST GLITCH DETECTION # # noise_1 = np.random.rand(np.max(y1.shape)) / 500 # noise_2 = np.random.rand(np.max(y1.shape)) / 500 # y1 = noise_1 # y2 = noise_2 # y1[480] = 0.5 # # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ # ========================== SATURATION CHECK BEGIN ==========================\ # y1_Min = np.min(y1) # Getting min / max y1_Max = np.max(y1) y2_Min = np.min(y2) y2_Max = np.max(y2) max_abs1 = max(abs(y1_Min), abs(y1_Max)) # ch1 absolute peak max_abs2 = max(abs(y2_Min), abs(y2_Max)) # ch2 absolute peak max_abs = max(max_abs1, max_abs2) # Greater absolute maximum peak # if max_abs < c.satur_threshold: # Saturation check is_Saturated = False else: is_Saturated = True doBeep(on_list=alarm_3_S[0], off_list=alarm_3_S[1]) print(Saturation_msg, "max_abs =", max_abs) # # # ========================== SATURATION CHECK END ============================/ # ========================== GLITCH TESTING BEGIN ============================\ # global plot_debug # Access to the global plot_debug W = 1 # Lateral range width for each side around the peak sample is_err1 = False is_err2 = False #----------- NEW TEST BASED ON THE DIFFERENTIATION OF THE GLITCH (DIRAC DELTA FUNCTION) y1_d = np.diff(y1) y2_d = np.diff(y2) indx1 = min(min( np.nonzero(np.abs(y1) == max_abs1))) # Index of max(abs(y1)) indx2 = min(min( np.nonzero(np.abs(y2) == max_abs2))) # Index of max(abs(y2)) if indx1 < (np.max(y1.shape) - 2) and (np.max( abs(y1_d[indx1 - 1] - max_abs1)) < max_abs1 / 50): is_err1 = True # # if indx2 < (np.max(y2.shape) - 2) and (np.max( abs(y2_d[indx1 - 1] - max_abs1)) < max_abs2 / 50): is_err2 = True # # #------------------------------------------------------------------------------- if is_err1: print(Glitch_msg, "on Ch1") if is_err2: print(Glitch_msg, "on Ch2") if is_err1 or is_err2: doBeep(on_list=alarm_1_S[0], off_list=alarm_1_S[1]) # # # if is_err1 or is_err2 or plot_debug: # mean1 = int(round(1000*mean_range1)) # max1 = int(round(1000*max_abs1 / 2)) # mean2 = int(round(1000*mean_range2)) # max2 = int(round(1000*max_abs2 / 2)) # print("==============================") # print(" Glitches are absent if:") # print(" mean_range > max_absX/2") # print("==============================") # print(" ch1: {:03d}".format(mean1), " {:03d}".format(max1), " [mV]") # print("------------------------------") # print(" ch2: {:03d}".format(mean2), " {:03d}".format(max2), "[mV]") # print("------------------------------") # print(" ch1 indexes: ", range_indx1) # print(" ch2 indexes: ", range_indx2) # print("------------------------------") # # # # ============================ GLITCH TESTING END ============================/ # ======================= CHANNELS SWAP TESTING BEGIN ========================\ # """ Sometime the first acquisition can swap ch1 with ch2, here we detect that occurrence, and in the case, we recovery by a swap between ch1 and ch2. 1) The autocorrelations of y1 and y2 are calculated. 2) The max absolute peaks of the autocorrelations are calculated. 3) Each one of the autocorrelation amplitudes are normalized using the absolute peaks of point (2). 4) The normalized autocorrelations are rounded to 1 decimal to exclude little oscillations around 0. 5) The differentiation of the sign changements of the normalized and rounded correlations is calculated. 6) The differentiations of point (5) are cumulated to obtain the final scalar count. 7) The ch1 Force signal is a well behaved and then bandlimited pulse with only two zero crossing (excluding noise), by contrast the ch2 Accel signal being a derivative (accelerometer signal) has surely more sign changements, consequently when the total sign changes of ch1 exceeds that of ch2 the channels are swapped. """ upper = int(3 * self.indx_echo_0) # Upper index limit for checking corr_y1 = correlate(y1[0:upper], y1[0:upper], mode='same') corr_y2 = correlate(y2[0:upper], y2[0:upper], mode='same') pk_abs_corr_1 = np.max(np.abs(corr_y1)) pk_abs_corr_2 = np.max(np.abs(corr_y2)) corr_y1 = np.round(corr_y1 / pk_abs_corr_1, 1) corr_y2 = np.round(corr_y2 / pk_abs_corr_2, 1) nz_diff_corr_y1 = (np.diff(np.sign(corr_y1), n=1) != 0).sum() nz_diff_corr_y2 = (np.diff(np.sign(corr_y2), n=1) != 0).sum() # if nz_diff_corr_y1 < nz_diff_corr_y2: is_swapped = False else: is_swapped = True # # if plot_debug: print("Actual loaded file n°", self.fileIndex) print("\n" + "Zero crossing comparison:", nz_diff_corr_y1, " < ", nz_diff_corr_y2, " ", not is_swapped) # # print(" Good if:") print(" nz_diff_corr_y1 < nz_diff_corr_y2") print("-----------------------------------") print(" ", nz_diff_corr_y1, " | ", nz_diff_corr_y2) print("-----------------------------------") if is_swapped: print("\n" + "/" * 35) print(" ERROR: Swapped ch1, ch2") print(" CORRECTION DONE!") print("/" * 35 + "\n") y1, y2 = y2, y1 # Swap Channels self.y1_full = self.y1 self.y2_full = self.y2 # # # ======================== CHANNELS SWAP TESTING END =========================/ self.plotData()
def put_audio(plot_debug=False): """ Getting Constants from Constants.py Variables must be extracted from the Variables_dict who is managed by Disk_IO. """ """ Variables and constants """ n_arg = len(sys.argv) # Number of command-line arguments if n_arg > 3: print("Error: required 2 arguments:" + "\n" + \ "f (free) or v (bounded)" + "\n" + \ "integer from 0 to 100" + "\n" + \ "Example: v 20") return # # arg_list = sys.argv # List of command-line arguments arg_1_str = str(arg_list[1]) # f (free) or v (bounded) arg_2_int = int(str(arg_list[2])) # Integer value expressing echo % # if arg_1_str.upper() == "F": # Positive value: Free Pile foot k_A_echo_0 = 1 is_free = True elif arg_1_str.upper() == "V": # Negative value: bounded Pile foot k_A_echo_0 = -1 is_free = False else: print("Error in the 1.st input parameters: accepted values are :" + "\n" + \ "f (free) or v (bounded)" + "\n" + \ "you have entered: " + arg_1_str) return # # if (0 <= arg_2_int) and (arg_2_int <= 100): k_A_echo_0 *= arg_2_int / 100 else: print("Error in the 2.nd input parameters: accepted value is :" + "\n" + \ "integer value expressing echo as a % (from 0 to 100)" + "\n" + \ "you have entered: " + str(arg_2_int)) return # # save_actual_pid() # Save the PID to allow to kill the process recall_pickled_dict( ) # Load variables from the c.s.v. c.pickle_txt_fullpath # SampRate = v.SamplingFreq # Sampling Frequency, Hz samples_acq = v.samples_acq # N. of Samples acquired: c.n_blocks multiple Length = v.Length # Pile length, m Speed = v.Speed # Wave speed AveragesRequired = v.AveragesRequired + 4 # Averages to do plus a reserve for lost acquisition (glitches) ro_c = v.Density # Actual Density [kg/m^3] Vc = Speed # Actual Speed [m/s] D = v.Diam # Actual Diameter m[] L = Length # Actual Length m[] A = (np.pi * D**2) / 4 # Pile section Area. E = ro_c * Vc**2 # Young module in the RC: Pascal [N / m ^ 2] K = E * A / L # Longitudinal Stiffness [N/m]. M = A * ro_c * L # Pile total Mass [kg]. w0 = np.sqrt(K / M) # Circular peak freq. of not dampen concentrated model # FORMAT = aa.PCM_FORMAT_S32_LE # Int 4 Bytes signed CHANNELS = int(2) byte_width = 4 bytes_size = CHANNELS * byte_width * c.N_FRAMES # Bytes in each period """ Create the data_out alsaaudio object instance In PCM_NORMAL mode, the call will block if the kernel buffer is full, and until enough sound has been played to allow the sound data to be buffered. The call always returns the size of the data provided. Syntax: class alsaaudio.PCM(type=PCM_PLAYBACK, mode=PCM_NORMAL, device='default', cardindex=-1) To find the sound device use: aplay -L: ATTENTION: the device full name must be without spaces ! fuji: "sysdefault" Pisound direct hardware device: "hw:CARD=pisound,DEV=0" AudioBox 22VSL direct hardware device: "hw:CARD=VSL,DEV=0" """ if c.IS_RASPBERRY: print("Machine is Raspberry Pi") else: print("Machine is not a Raspberry Pi") print("Audio device is:", c.SOUND_DEVICE) # try: # Creation af output audio object data_out = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL, device=c.SOUND_DEVICE) data_out.setchannels(CHANNELS) data_out.setrate(SampRate) data_out.setformat(FORMAT) data_out.setperiodsize(c.N_FRAMES) print(" - Success creating the output audio object.") except: # Failed: another audio process is running print(" - Failed to create an input audio object") print(" - Search an active process to be terminated.") # pid = None sts = 1 cmd = "ps aux | grep get_audio.py | head -n1" # Search get_audio.py and try to terminate res = subprocess.getoutput(cmd) pid = res[9:15].strip() if pid: sts = os.WEXITSTATUS(os.system('kill -TERM ' + pid)) if sts is 0: # Process closed successfully print(" - Found an active process: terminated with success.") try: # Creation of input audio object print(" - Second try to create an input audio object.") time.sleep(0.5) # Indispensable greather than 0.01 data_out = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL, device=c.SOUND_DEVICE) data_out.setchannels(CHANNELS) data_out.setrate(SampRate) data_out.setformat(FORMAT) data_out.setperiodsize(c.N_FRAMES) print(" - Success creating the input audio object.") except: # Failed, doBeep(on_list=bip_5_S[0], off_list=bip_5_S[1]) # On Bip of 5s print( " - Failed second attempt to create the input audio object: restart computer" ) sys.exit() # # else: print( " - Old process not found: please restart the computer.") return # # # # END if sts: # END try:...except: # """ x1x2: vector of interleaved channels, Ch1, Ch2, output signal """ # f_bound = v.Speed / (4 * v.Length) # Vp / (4*L) f_free = v.Speed / (2 * v.Length) # Vp / (2*L) if is_free: f_hp = f_free # Pile foot is free else: f_hp = f_bound # Pile foot is bounded # # A_pk_32 = c.full_scale_32 # Peak Amplitude referr. to 32 bit int f.s. dt = 1.0 / SampRate # Sampling period T = 1 # Total signal time length N = samples_acq # Number of samples fnyquist = 0.5 * SampRate T_effective = HAMMER_DT # Pulse equivalent length T_pulse = T_effective * 3 / 2 # Length at the half sine base N_pulse = round(T_pulse / dt) # Number of pulse samples t = np.arange(N_pulse) * dt # Time length of the pulse sample Head_zeros_N = int(0.5 * T_effective / dt) # Number of zeros prepended Head_zeros = np.zeros(Head_zeros_N) # Header of zeros pulse = np.sin(np.pi * t / T_pulse) * c.PUT_AUDIO_PEAK # Pulse as a positive half sine pulse_echo_0 = k_A_echo_0 * pulse # Pulse echo_0: >0 free, <0 bounded T_echo_0 = 2 * Length / Speed # Return time of echo_0 indx_echo_0 = round( T_echo_0 / dt) + Head_zeros_N # Index of the 1.st echo_0 sample # for n in range(int(AveragesRequired)): time.sleep(4) x1f = np.zeros(N) # Array of 0's, ch1 x2f = np.zeros(N) # Array of 0's, ch2 x1f[0:Head_zeros_N] = Head_zeros # Put Head_zeros at index 0 for ch1 x2f[0:Head_zeros_N] = Head_zeros # Put Head_zeros at index 0 for ch2 x1f[Head_zeros_N:Head_zeros_N + N_pulse] = pulse # Put pulse after Head_zeros for ch1 x2f[Head_zeros_N:Head_zeros_N + N_pulse] = pulse # Put pulse after Head_zeros for ch2 x2f[indx_echo_0:indx_echo_0 + N_pulse] = pulse_echo_0 # Put echo_0 delayed pulse on ch2 # # Insertion of a HP 1.stth order filter on the Accel channel pad = 1000 # WAS 300 w_d = f_hp / fnyquist # Digital pulsation, 0..1, where 1 => fnyquist b, a = butter(N=1, Wn=w_d, btype='high') # HP Butterworth filter 1.st order x2f = lfilter(b, a, x2f) # Filtering the Force signal # # Insertion on the Accel channel of the differentiation # x2f = np.diff(x2f, n=1, append=0) / dt # Differentiate x2f with a 0 appended x2f_pk = np.max(np.abs(x2f)) # Absolute peak value of x2f x2f /= x2f_pk # Normalization unity peak, then # # Insertion of a LP 4.th order filter f_lp = 3 / T_pulse # Hammer head response simulation w_d = f_lp / fnyquist # Digital pulsation, 0..1, where 1 => fnyquist b, a = butter( N=2, Wn=w_d, btype='low' ) # WAS butter(N=2 bessel Digital LP Bessel filter 2.nd order x1f = filtfilt(b, a, x1f, padlen=pad) # Filtering the Force signal x2f = filtfilt(b, a, x2f, padlen=pad) # Filtering the Accel signal # # scaling for Pisound Output Gain and then x2f *= c.PUT_AUDIO_PEAK # scaling for require Peak value # x1f *= HAMMER_SIGN # Apply the polarity of the Force cell x2f *= ACCEL_SIGN # Apply the polarity of Accelerometer # x1f *= A_pk_32 # Scale Hammer to 32 bit signed full scale x2f *= A_pk_32 # Scale Accelerometer to 32 bit signed full scale # x1 = x1f.astype(np.int32) # Convert to int32, ch1 x2 = x2f.astype(np.int32) # Convert to int32, ch2 # pk1_32 = np.max(np.abs(x1)) pk2_32 = np.max(np.abs(x2)) rms1_32 = np.std(x1) rms2_32 = np.std(x2) # print("\n" + "Started pulse N°", n + 1) print( "rms1 = {:3.1f}".format( (rms1_32 / c.full_scale_32) * 1000), "mV; ", "pk1 = {:3.1f}".format( (pk1_32 / c.full_scale_32) * 1000), "mV") print( "rms2 = {:3.1f}".format( (rms2_32 / c.full_scale_32) * 1000), "mV; ", "pk2 = {:3.1f}".format( (pk2_32 / c.full_scale_32) * 1000), "mV") # x1x2 = np.zeros(CHANNELS * samples_acq, dtype=np.int32) # Array for interleaved Ch1, Ch2 data x1x2[ 0:: 2] = x1 / PI_OUT_GAIN # Scaled output gain, fill ch1: even indexes x1x2[ 1:: 2] = x2 / PI_OUT_GAIN # Scaled output gain, fill ch1: odd indexes # Fill x1 at odd indexes: ch2 # out_big_buffer = bytearray(bytes_size * c.N_BLOCKS) out_big_buffer[:] = pack("l" * int(2 * c.N_OF_SAMPLES), *x1x2) # Pack from numpy.int32 to bytes out_short_buffer = bytearray( bytes_size) # Output array written one frame at time # beg = 0 end = bytes_size for i in range(c.N_BLOCKS): # Write data to data_out alsaaudio object out_short_buffer[:] = out_big_buffer[beg:end] # As short blocks size = data_out.write(out_short_buffer) #print(size) beg = end end += bytes_size # # #time.sleep(3.0) # if plot_debug: sel = int(15 * indx_echo_0) # Was 1.5 mx1 = max(np.abs(x1f[0:sel] / A_pk_32)) mx2 = max(np.abs(x2f[0:sel] / A_pk_32)) mx = 1.1 * max(mx1, mx2) tp = np.arange(0, N, 1) # * dt fig = plt.figure(1) fig.set_size_inches(w=3, h=4) fig.subplots_adjust(hspace=0.50) plt.subplot(211) plt.plot(tp[0:sel], x1f[0:sel] / A_pk_32) plt.title("put_audio: Hammer") # plt.xlabel("Time [s]") plt.xlabel('Samples [i]') plt.ylabel("Force") plt.grid(True) #[xl, xh, yl, yh] = plt.axis() plt.axis([tp[0], tp[sel], -mx, mx]) # plt.subplot(212) plt.plot(tp[0:sel], x2f[0:sel] / A_pk_32) plt.title("put_audio: Accel") # plt.xlabel("Time [s]") plt.xlabel('Samples [i]') plt.ylabel("Accel") plt.grid(True) #[xl, xh, yl, yh] = plt.axis() plt.axis([tp[0], tp[sel], -mx, mx]) # plt.show() #time.sleep(0.1) # END if plot_debug: # END for n in range(AveragesRequired): # data_out.close() print("put_audio terminated")
def get_audio(plot_debug=False): """ Called by :param: plot_debug, if True do a debug plots :return: None Acquire Data under a trigger mode, then save the Data to the HD. Get Constants from Constants.py Variables will be extracted from the Variables_dict who is managed by Disk_IO. When debug==True plot acquired data as diagnostic. """ recall_pickled_dict( ) # Load variables from the c.s.v. c.pickle_txt_fullpath SampRate = v.SamplingFreq # Sampling Frequency, Hz TrigLevel = v.TrigLevel # Actual Trigger level as a % AveragesRequired = v.AveragesRequired # Required Averages number Length = v.Length # Pile length Speed = v.Speed # P wave speed in concrete SamplingFreq = v.SamplingFreq # Sampling frequency T_echo_0 = 2 * Length / Speed # Return time of echo_0 indx_echo_0 = round(T_echo_0 * SamplingFreq) # Index of the 1.st echo_0 sample # save_actual_pid() # Save the PID to a file, for successive kill # """ Called at each new Acquisition, remove files at c.temp_data_path, if the dir was removed, recreate it. """ create_and_clear(c.temp_data_path, delete_files=True) # TrigLevel_32 = ( TrigLevel / 100 ) * c.full_scale_32 # Trigger level espressed as true amplitude referred to 1 pk_foot = 1 / 100 # Relative factor for the pulse pedestal FORMAT = aa.PCM_FORMAT_S32_LE # Signed 32 bit (Little Endian byte order) CHANNELS = int(2) byte_width = 4 # Bytes in one signed 32 bit sample bytes_size = CHANNELS * byte_width * c.N_FRAMES # bytes in each period (frame) e.g. 2 * 4 * 256 # """ Create the inp alsaaudio object instance PCM_NORMAL: Blocking Mode, not a true blocking as said in the docs, may return a variable number of frames, then is necessary to wait in a loop until the c.n_frames are returned and the buffer is full. To find the sound device use: aplay -L: ATTENTION: the device full name must be without spaces ! fuji: "sysdefault" Pisound direct hardware device: "hw:CARD=pisound,DEV=0" AudioBox 22VSL direct hardware device: "hw:CARD=VSL,DEV=0" """ print() print("=" * 50) print(" Started '" + __file__ + "' as a separate executable") print("=" * 50) if c.IS_RASPBERRY: print("Machine is Raspberry Pi") else: print("Machine is not a Raspberry Pi") print("Audio device is:", c.SOUND_DEVICE) # try: # Creation af input audio object print("- Try to create an input audio object.") inp = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NORMAL, device=c.SOUND_DEVICE) inp.setchannels(CHANNELS) inp.setrate(SampRate) inp.setformat(FORMAT) inp.setperiodsize(c.N_FRAMES) print(" - Success creating the input audio object.") except: # Failed: another audio process is running print(" - Failed to create an input audio object") print(" - Search an active process to be terminated.") # pid = None sts = 1 cmd = "ps aux | grep get_audio.py | head -n1" # Search it and try to terminate res = subprocess.getoutput(cmd) pid = res[9:15].strip() if pid: sts = os.WEXITSTATUS(os.system('kill -TERM ' + pid)) if sts is 0: # Process closed successfully print(" - Found an active process: terminated with success.") try: # Creation of input audio object print(" - Second try to create an input audio object.") time.sleep(0.1) # Indispensable greather than 0.01 inp = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NORMAL, device=c.SOUND_DEVICE) inp.setchannels(CHANNELS) inp.setrate(SampRate) inp.setformat(FORMAT) inp.setperiodsize(c.N_FRAMES) print(" - Success creating the input audio object.") except: # Failed, doBeep(on_list=bip_5_S[0], off_list=bip_5_S[1]) # On Bip of 5s print( " - Failed second attempt to create the input audio object" ) # # else: print( " - Old process not found: please restart the computer.") return # # # # END if sts: # END try:...except: # """ Preacquisition needed to flush bad data in buffers and to inhibit the swap of channels at first run. """ while 1: # Pay attention inp.read() return a tuple (i.e. immutable) l, inp_short_buffer = inp.read( ) # l must be equal to c.n_frames; blocked until filled if l == c.N_FRAMES: print("Preacq: l=", l) break # # # # del inp_short_buffer # Being a tuple i.e. immutable must be destroyed to reuse the name i = 1 while i <= int(AveragesRequired): """ Wait until the Trigger level is reached getting frames continuously, the format is long i.e. int32; the AD has 24 bit, 8 MSBits are zeros. The Trigger Channel is Ch1: Even samples: 0, 2, 4,... => Ch1 The variable inp_short_buffer cannot be predefined as a bytearray (that is mutable) because inp.read() return an array of bytes (that is immutable). """ inp_old_buffer = bytearray(bytes_size) inp_big_buffer = bytearray(bytes_size * c.N_BLOCKS) beg = 0 end = bytes_size os.system("sync") # To empty write queue # # ====================== Trigger loop Begin ======================\ # print("\n" + "=o" * 25 + "=") if do_plot: time.sleep(2) # For testing, wait to close the plot window else: time.sleep(1) print("Waiting for Trigger n.", i, "...") while True: while 1: # Pay attention inp.read() return a tuple (i.e. immutable) l, inp_short_buffer = inp.read( ) # l must be equal to c.n_frames; blocked until filled if l == c.N_FRAMES: break # Check of l can reveal a casual error else: continue # # """ From the array of bytes inp_short_buffer containing int32 ch1, ch2 as blocks of 4 bytes interleaved, get the tuple ch1_32 discarding each successive 4 bytes represented by xxxx (corresponding to ch2, i.e. get only ch1 values), for a total of c.n_frames. """ if len(inp_short_buffer) != bytes_size: continue # 2.nd check because the first may fail !!! ch1_int32 = unpack( 'ixxxx' * c.N_FRAMES, inp_short_buffer ) # Get tuple (int ch1 only) from tuple of bytes ch1_arr_32 = np.array(ch1_int32) # From tuple to np array pk1_32 = np.max( np.abs(ch1_arr_32)) # Get absolute peak value of ch1 as int32 rms1_32 = np.std( ch1_arr_32 ) # Check for single or few noise spike on ch1 as int32 if pk1_32 < TrigLevel_32: # No Trigger inp_old_buffer = inp_short_buffer # No Trigger, save as a possible pre-trigger continue else: # Get Trigger """ The following print is only for testing: must been leaved commented. Represents values of the trigger frame. """ #print("Trig. rms1 = {:3.1f}".format((rms1_32 / c.full_scale_32)*1000), "mV") #print("Trig. pk1 = {:3.1f}".format((pk1_32 / c.full_scale_32)*1000), "mV") inp_big_buffer[ beg:end] = inp_old_buffer # Pre-Trigger block of data beg = end # Prepare 2.nd block address end += bytes_size inp_big_buffer[ beg: end] = inp_short_buffer # Insert 2.nd block of Trigger data break # # # # # END while True: # ======================= Trigger loop End =======================/ # # ====================== Acquisition loop start ==================\ j = 2 # Block index: 0 and 1 blocks inserted inside the Trigger loop while j < c.N_BLOCKS: beg = end end += bytes_size while 1: # Pay attention inp.read() return a tuple (i.e. immutable) l, inp_short_buffer = inp.read( ) # l: n. of frames for each channel: BLOCKING !!! if l == c.N_FRAMES: break # # # # inp_big_buffer[beg:end] = inp_short_buffer # Packing blocks j += 1 # # # ====================== Acquisition loop end ====================/ # """ Unpacking bytes to integers (32 bit) in the tuple tpl_i32. Then separating interleaved Channels, Ch1, Ch2, getting c.N_of_samples points for channel. Finally we have y1, y2 as np.arrays of float64, and scaled to +/-1 and compensated for Pisound Input Gain. To verify the correctness of the formatting string: calcsize('ii') returns 8 """ tpl_i32 = unpack( 'ii' * c.N_OF_SAMPLES, inp_big_buffer) # Packed bytes and interleaved: Ch1, Ch2 y1_tpl_i32 = tpl_i32[0:c.N_OF_SAMPLES * CHANNELS:2] # Even: Ch1 as integer 32 y2_tpl_i32 = tpl_i32[1:c.N_OF_SAMPLES * CHANNELS + 1:2] # Odd: Ch2 as integer 32 y1 = np.array( y1_tpl_i32 ) / c.full_scale_32 # Scaled to +/- 1 full scale # Go to float 8 bytes y2 = np.array(y2_tpl_i32) / c.full_scale_32 y1 /= PI_INP_GAIN # Pisound board Input Gain compensation y2 /= PI_INP_GAIN # # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ # PUT A PULSE ON Ch1 TO TEST GLITCH DETECTION # # noise_1 = np.random.rand(np.max(y1.shape)) / 500 # noise_2 = np.random.rand(np.max(y1.shape)) / 500 # y1 = noise_1 # y2 = noise_2 # y1[480] = 0.5 # # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ # ========================== SATURATION CHECK BEGIN ==========================\ # y1_Min = np.min(y1) # Getting min / max y1_Max = np.max(y1) y2_Min = np.min(y2) y2_Max = np.max(y2) max_abs1 = max(abs(y1_Min), abs(y1_Max)) # ch1 absolute peak max_abs2 = max(abs(y2_Min), abs(y2_Max)) # ch2 absolute peak max_abs = max(max_abs1, max_abs2) # Greater absolute maximum peak # if max_abs < c.satur_threshold: # Saturation check is_Saturated = False else: is_Saturated = True doBeep(on_list=alarm_3_S[0], off_list=alarm_3_S[1]) print(Saturation_msg, "max_abs =", max_abs) # # # ========================== SATURATION CHECK END ============================/ # ========================== GLITCH TESTING BEGIN ============================\ # W = 1 # Range minimum width is_err1 = False is_err2 = False #----------- NEW TEST BASED ON THE DIFFERENTIATION OF THE GLITCH (DIRAC DELTA FUNCTION) y1_d = np.diff(y1) y2_d = np.diff(y2) indx1 = min(min( np.nonzero(np.abs(y1) == max_abs1))) # Index of max(abs(y1)) indx2 = min(min( np.nonzero(np.abs(y2) == max_abs2))) # Index of max(abs(y2)) if indx1 < (np.max(y1.shape) - 2) and (np.max( abs(y1_d[indx1 - 1] - max_abs1)) < max_abs1 / 50): is_err1 = True # # if indx2 < (np.max(y2.shape) - 2) and (np.max( abs(y2_d[indx1 - 1] - max_abs1)) < max_abs2 / 50): is_err2 = True # # #------------------------------------------------------------------------------- print("") if is_err1: print(Glitch_msg, "Ch1") if is_err2: print(Glitch_msg, "Ch2") if is_err1 or is_err2: # Glitches counted as saturation: doBeep(on_list=alarm_1_S[0], off_list=alarm_1_S[1]) is_Saturated = True # the acquisition will be repeated # # # if is_err1 or is_err2 or plot_debug: # mean1 = int(round(1000*mean_range1)) # max1 = int(round(1000*max_abs1 / 2)) # mean2 = int(round(1000*mean_range2)) # max2 = int(round(1000*max_abs2 / 2)) # print("==============================") # print(" Glitches are absent if:") # print(" mean_range > max_absX/2") # print("==============================") # print(" ch1: {:03d}".format(mean1), " {:03d}".format(max1), " [mV]") # print("------------------------------") # print(" ch2: {:03d}".format(mean2), " {:03d}".format(max2), "[mV]") # print("------------------------------") # print(" ch1 indexes: ", range_indx1) # print(" ch2 indexes: ", range_indx2) # print("------------------------------") # # # ============================ GLITCH TESTING END ============================/ # ======================= CHANNELS SWAP TESTING BEGIN ========================\ # """ Sometime the first acquisition can swap ch1 with ch2, here we detect that occurrence, and in the case, we recovery by a swap between ch1 and ch2. 1) The autocorrelations of y1 and y2 are calculated. 2) The max absolute peaks of the autocorrelations are calculated. 3) Each one of the autocorrelation amplitudes are normalized using the absolute peaks of point (2). 4) The normalized autocorrelations are rounded to 1 decimal to exclude little oscillations around 0. 5) The differentiation of the sign changements of the normalized and rounded correlations is calculated. 6) The differentiations of point (5) are cumulated to obtain the final scalar count. 7) The ch1 Force signal is a well behaved and then bandlimited pulse with only two zero crossing (excluding noise), by contrast the ch2 Accel signal being a derivative (accelerometer signal) has surely more sign changements, consequently when the total sign changes of ch1 exceeds that of ch2 the channels are swapped. """ upper = int(3 * indx_echo_0) # Upper index limit for checking corr_y1 = correlate(y1[0:upper], y1[0:upper], mode='same') corr_y2 = correlate(y2[0:upper], y2[0:upper], mode='same') pk_abs_corr_1 = np.max(np.abs(corr_y1)) pk_abs_corr_2 = np.max(np.abs(corr_y2)) corr_y1 = np.round(corr_y1 / pk_abs_corr_1, 1) corr_y2 = np.round(corr_y2 / pk_abs_corr_2, 1) nz_diff_corr_y1 = (np.diff(np.sign(corr_y1), n=1) != 0).sum() nz_diff_corr_y2 = (np.diff(np.sign(corr_y2), n=1) != 0).sum() # if nz_diff_corr_y1 < nz_diff_corr_y2: is_swapped = False else: is_swapped = True # # if plot_debug: print("\n" + "Zero crossing comparison:", nz_diff_corr_y1, " < ", nz_diff_corr_y2, " ", not is_swapped) # # print(" Good if:") print(" nz_diff_corr_y1 < nz_diff_corr_y2") print("-----------------------------------") print(" ", nz_diff_corr_y1, " | ", nz_diff_corr_y2) print("-----------------------------------") if is_swapped: print("\n" + "/" * 35) print(" ERROR: Swapped ch1, ch2") print(" CORRECTION DONE!") print("/" * 35 + "\n") y1, y2 = y2, y1 # Restore from swap error # # # ======================== CHANNELS SWAP TESTING END =========================/ if not is_Saturated: """ If is_Saturated returns to the external acquisition loop: while i <= int(AveragesRequired) Preparing good data for storage: Relocation of the start of the inputrms1_32 signal in y1, y2, to eliminate the pre-trigger part that has a variable length. That part will be shortened as follows: a) Search the first index where the Pulse input Data array (y1) is bigger than pk * pk_foot. The index of that basement point will be named index_Trig. b) The new starting index is obtained decreasing, as possible, index_Trig by a fixed amount, in order to assure the inclusion of sufficient initial part of the signal. The corresponding amount of time, guard_T, will be equal to the hammer pulse width t.HAMMER_DT, the number of samples will be N_guard. """ y1_shape = y1.shape # Array y1 dimensions y2_shape = y2.shape # Array y2 dimensions if not (np.max(y1_shape) == np.max(y2_shape)): # Length comparison print(Length_msg) print("get_audio: y1_shape =", y1_shape) print("get_audio: y2_shape =", y2_shape) doBeep(on_list=bip_1_5_S[0], off_list=bip_1_5_S[1]) return # # # =============================== Relocation Begin =========================\ # pk1 = pk1_32 / c.full_scale_32 # Peak scaled to +/- 1 y1_length = np.max(y1_shape) # Length of y1 array indexes = np.array(np.nonzero( abs(y1) > pk1 * pk_foot))[0, :] # Array of indexes, [0,:] => from 1xN to N len_indexes = len(indexes) print("len_indexes =", len_indexes) if len_indexes != 0: index_Trig = np.amin( indexes) # Lowest index i.e. index pedestal start else: index_Trig = 0 # # dT = 1.0 / SampRate # Sampling time guard_T = HAMMER_DT # Safety margin time, before Pulse pedestal N_guard = int(round(guard_T / dT)) # Corresponding guard margin as samples if index_Trig > N_guard: # Guard must begin after y1[0] new_start = index_Trig - N_guard # Index (>=0) for the new starting point tmp_array = np.zeros( y1_length) # Temporary empty array for new y1 tmp_array[0:-1 - new_start] = copy.copy( y1[new_start:-1] ) # Fill y1 without the guard section of y1 # leaves a tail of zeros y1 = copy.copy(tmp_array) # y1 replaced by relocated data tmp_array = np.zeros(np.max( y2.shape)) # Temporary empty array for new y2 tmp_array[0:-1 - new_start] = copy.copy( y2[new_start:-1] ) # Fill y2 without the guard section of y2 y2 = copy.copy(tmp_array) # y2 replaced tmp_length = np.max(y1.shape) # Actual length of y1 array if tmp_length != y1_length: print( "\n" + "ERROR in get_audio: Length of y1, y2 modified erroneously" ) # # else: # Leave the signal unaltered pass # # #=============================== Relocation End ===========================/ # pk1 = np.max(np.abs(y1)) pk2 = np.max(np.abs(y2)) rms1 = np.std(y1) rms2 = np.std(y2) print("\n" + "Saving data to Disc") print("rms1 = {:3.1f}".format(rms1 * 1000), "mV; pk1 = {:3.1f}".format(pk1 * 1000), "mV") print("rms2 = {:3.1f}".format(rms2 * 1000), "mV; pk2 = {:3.1f}".format(pk2 * 1000), "mV") """ Save data to disc """ name_1 = "/y1_" + str('{:02d}'.format(i)) name_2 = "/y2_" + str('{:02d}'.format(i)) path_1 = c.temp_data_path + name_1 path_2 = c.temp_data_path + name_2 # if c.sav_mode == "npy": """ Saving in Numpy binary file, not compressed, add extension .npy """ np.save(path_1, y1) # Uncompressed format np.save(path_2, y2) elif c.sav_mode == "npz": """ Saving in Numpy binary file, compressed, add extension .npz """ np.savez_compressed(path_1, y1=y1) # Compressed format np.savez_compressed(path_2, y2=y2) # # elif c.sav_mode == "mat": """ Saving in Matlab format V.5, compressed, add automatically the extension .mat loadmat() will return the vector in a dictionary ex: d = scipy.io.loadmat("vector.mat) vect = d["vect"] vect = d1["y1"] and with one more dimension added ("column"): original vect.shape -> (n,), returned vect.shape -> (n, 1); to return to the original dimension: vect_reshaped = vect[:,0] """ scipy.io.savemat(path_1, {"y1": y1}, do_compression=True, oned_as="column") scipy.io.savemat(path_2, {"y2": y2}, do_compression=True, oned_as="column") # # print("New acquired data:", name_1, name_2) print("=o" * 25 + "=") save_blows(c.blows_list_txt, name_1, name_2) os.system("sync") # Delayed write causes a random delay time.sleep(0.2) # Delays for x seconds if plot_debug: sel = int(15 * indx_echo_0) # was 1.5 mx1 = max(np.abs(y1[0:sel])) mx2 = max(np.abs(y2[0:sel])) mx = 1.1 * max(mx1, mx2) tp = np.arange(c.N_OF_SAMPLES ) # * 1.0 / SampRate # Time vector fig = plt.figure(1) fig.set_size_inches(w=3, h=4) fig.subplots_adjust(hspace=0.50) plt.subplot(211) plt.plot(tp[0:sel], y1[0:sel]) plt.title('get_audio Hammer') # plt.xlabel('Time [s]') plt.xlabel('Samples [i]') plt.ylabel('Force') plt.grid(True) plt.axis([tp[0], tp[sel], -mx, mx]) # plt.subplot(212) plt.plot(tp[0:sel], y2[0:sel]) plt.title('get_audio Accel') # plt.xlabel('Time [s]') plt.xlabel('Samples [i]') plt.ylabel('Accel') plt.grid(True) plt.axis([tp[0], tp[sel], -mx, mx]) # plt.show() time.sleep(0.1) # END if plot_debug: i += 1 # END if not is_Saturated): # END while i <= int(AveragesRequired): # inp.close() # Close inp alsaaudio object # print("get_audio: closed input device, terminated")
def get_audio(plot_debug=False): """ Called by :param: plot_debug, if True do a debug plots :return: None Acquire Data under a trigger mode, then save the Data to the HD. Get Constants from Constants.py Variables will be extracted from the Variables_dict who is managed by Disk_IO. When debug==True plot acquired data as diagnostic. """ recall_pickled_dict( ) # Load variables from the c.s.v. c.pickle_txt_fullpath SamplingFreq = v.SamplingFreq # Sampling Frequency, Hz TrigLevel = v.TrigLevel # Actual Trigger level as a % AveragesRequired = v.AveragesRequired # Required Averages number Length = v.Length # Pile length Speed = v.Speed # P wave speed in concrete T_echo_0 = 2 * Length / Speed # Return time of echo_0 indx_echo_0 = round(T_echo_0 * SamplingFreq) # Index of the 1.st echo_0 sample # save_actual_pid() # Save the PID to a file, for successive kill # """ Called at each new Acquisition, remove files at c.temp_data_path, if the dir was removed, recreate it. """ create_and_clear(c.temp_data_path, delete_files=True) # TrigLevel_32 = ( TrigLevel / 100 ) * c.full_scale_32 # From % to fraction of unity, to fract. of c.full_scale_32 pk_foot = 1 / 100 # Relative factor for the pulse pedestal FORMAT = pyaudio.paInt32 # Signed 32 bit (Little Endian byte order) CHANNELS = int(2) byte_width = 4 # Bytes in one signed 32 bit sample # print() print("=" * 50) print(" Started '" + __file__ + "' as a separate executable") print("=" * 50) if c.IS_RASPBERRY: print("Machine is Raspberry Pi") else: print("Machine is not a Raspberry Pi") print("Audio device is:", c.SOUND_DEVICE) """ New error handler: suppress all libasound.so error messages, by substitution of the error handler. """ ERROR_HANDLER_FUNC = CFUNCTYPE(None, c_char_p, c_int, c_char_p, c_int, c_char_p) def py_error_handler(filename, line, function, err, fmt): print('.', end="") # Print a single '.' point instead of the error message # # # c_error_handler = ERROR_HANDLER_FUNC(py_error_handler) asound = cdll.LoadLibrary('libasound.so') # # Set error handler asound.snd_lib_error_set_handler(c_error_handler) # # Initialize PyAudio p = pyaudio.PyAudio() p.terminate() print() # #--------------------------- Error handler end ---------------------------------- """ Creates an instance of the PyAudio class , and open a stream to write output frames """ paudio = pyaudio.PyAudio() stream = paudio.open(rate=SamplingFreq, channels=CHANNELS, format=FORMAT, input=True, input_device_index=c.SOUND_DEVICE, frames_per_buffer=c.N_FRAMES) # i = 1 while i <= int(AveragesRequired): """ Wait until the Trigger level is reached getting frames continuously, the format is long i.e. int32; the AD has 24 bit, 8 MSBits are zeros. The Trigger Channel is Ch1: Odd samples: 1, 3, 5,... => Ch1 The variable inp_short_buffer cannot be predefined as a bytearray (that is mutable) because stream.read() return an array of bytes (that is immutable). """ inp_old_buffer = np.zeros(CHANNELS * c.N_FRAMES, dtype=np.int32) inp_big_buffer = np.zeros(CHANNELS * c.N_FRAMES * c.N_BLOCKS, dtype=np.int32) beg = 0 end = CHANNELS * c.N_FRAMES os.system("sync") # To empty write queue # # ====================== Trigger loop Begin ======================\ # print("\n" + "=o" * 25 + "=") if do_plot: time.sleep(2) # For testing, wait to close the plot window else: time.sleep(1) print("Waiting for Trigger n.", i, "...") while True: inp_short_buffer = np.frombuffer(stream.read(c.N_FRAMES), np.int32) # # """ From the array of bytes inp_short_buffer of int32: ch1, ch2, c1, ch2,... blocks of interleaved 4 bytes get the tuple ch1_int32 discarding each successive 4 bytes represented by xxxx (corresponding to ch2, i.e. get only ch1 values), for a total of c.n_frames. """ if len(inp_short_buffer) != c.N_FRAMES * CHANNELS: continue ch1_int32 = unpack( 'ixxxx' * c.N_FRAMES, inp_short_buffer ) # Get tuple (i32 ch1 only) from tuple of bytes ch1_arr_32 = np.array(ch1_int32) # From tuple to np array pk1_32 = np.max( np.abs(ch1_arr_32)) # Get absolute peak value of ch1 as int32 if pk1_32 < TrigLevel_32: # No Trigger inp_old_buffer = inp_short_buffer # No Trigger, save as a possible pre-trigger continue else: # Get Trigger """ The following print is only for testing: must been leaved commented. Represents values of the trigger frame. """ # rms1_32 = np.std(ch1_arr_32) # Check for single or few noise spike on ch1 # print("Trig. rms1 = {:3.1f}".format((rms1_32 / c.full_scale_32)*1000), "mV") # print("Trig. pk1 = {:3.1f}".format((pk1_32 / c.full_scale_32)*1000), "mV") inp_big_buffer[ beg:end] = inp_old_buffer # Pre-Trigger block of data beg = end # Prepare 2.nd block address end += CHANNELS * c.N_FRAMES # Next step into inp_big_buffer inp_big_buffer[ beg: end] = inp_short_buffer # Insert 2.nd block of Trigger data break # # # # # END while True: # ======================= Trigger loop End =======================/ # # ====================== Acquisition loop start ==================\ j = 2 # Block index: 0 and 1 blocks inserted inside the Trigger loop while j < c.N_BLOCKS: beg = end end += CHANNELS * c.N_FRAMES inp_short_buffer = np.frombuffer(stream.read(c.N_FRAMES), np.int32) inp_big_buffer[beg:end] = inp_short_buffer # Packing blocks j += 1 # # # ====================== Acquisition loop end ====================/ # """ Unpacking bytes to integers (32 bit) in the tuple tpl_i32. Then separating interleaved Channels, Ch1, Ch2, getting c.N_of_samples points for channel. Finally we have y1, y2 as np.arrays of float64, and scaled to +/-1 and compensated for Pisound Input Gain. To verify the correctness of the formatting string: calcsize('ii') returns 8 """ tpl_i32 = unpack( 'ii' * c.N_OF_SAMPLES, inp_big_buffer) # Packed bytes and interleaved: Ch1, Ch2 y1_tpl_i32 = tpl_i32[0:c.N_OF_SAMPLES * CHANNELS:2] # Odd: Ch1 as integer 32 y2_tpl_i32 = tpl_i32[1:c.N_OF_SAMPLES * CHANNELS + 1:2] # Even: Ch2 as integer 32 y1 = np.array( y1_tpl_i32 ) / c.full_scale_32 # Scaled to +/- 1 full scale # Go to float 8 bytes y2 = np.array(y2_tpl_i32) / c.full_scale_32 y1 /= PI_INP_GAIN # Pisound board Input Gain compensation y2 /= PI_INP_GAIN # # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ # PUT A PULSE ON Ch1 TO TEST GLITCH DETECTION # # noise_1 = np.random.rand(np.max(y1.shape)) / 500 # noise_2 = np.random.rand(np.max(y1.shape)) / 500 # y1 = noise_1 # y2 = noise_2 # y1[480] = 0.5 # # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ # ========================== SATURATION CHECK BEGIN ==========================\ # y1_Min = np.min(y1) # Getting min / max y1_Max = np.max(y1) y2_Min = np.min(y2) y2_Max = np.max(y2) max_abs1 = max(abs(y1_Min), abs(y1_Max)) # ch1 absolute peak max_abs2 = max(abs(y2_Min), abs(y2_Max)) # ch2 absolute peak max_abs = max(max_abs1, max_abs2) # Greater absolute maximum peak # if max_abs < c.satur_threshold: # Saturation check is_Saturated = False else: is_Saturated = True doBeep(on_list=alarm_3_S[0], off_list=alarm_3_S[1]) print(Saturation_msg, "max_abs =", max_abs) # # # ========================== SATURATION CHECK END ============================/ # ========================== GLITCH TESTING BEGIN ============================\ # W = 1 # Range minimum width is_err1 = False is_err2 = False #----------- NEW TEST BASED ON THE DIFFERENTIATION OF THE GLITCH (DIRAC DELTA FUNCTION) y1_d = np.diff(y1) y2_d = np.diff(y2) indx1 = min(min( np.nonzero(np.abs(y1) == max_abs1))) # Index of max(abs(y1)) indx2 = min(min( np.nonzero(np.abs(y2) == max_abs2))) # Index of max(abs(y2)) if indx1 < (np.max(y1.shape) - 2) and (np.max( abs(y1_d[indx1 - 1] - max_abs1)) < max_abs1 / 50): is_err1 = True # # if indx2 < (np.max(y2.shape) - 2) and (np.max( abs(y2_d[indx1 - 1] - max_abs1)) < max_abs2 / 50): is_err2 = True # # #------------------------------------------------------------------------------- print("") if is_err1: print(Glitch_msg, "Ch1") if is_err2: print(Glitch_msg, "Ch2") if is_err1 or is_err2: # Glitches counted as saturation: doBeep(on_list=alarm_1_S[0], off_list=alarm_1_S[1]) is_Saturated = True # the acquisition will be repeated # # # ============================ GLITCH TESTING END ============================/ # ======================= CHANNELS SWAP TESTING BEGIN ========================\ # """ Sometime the first acquisition can swap ch1 with ch2, here we detect that occurrence, and in the case, we recovery by a swap between ch1 and ch2. 1) The autocorrelations of y1 and y2 are calculated. 2) The max absolute peaks of the autocorrelations are calculated. 3) Each one of the autocorrelation amplitudes are normalized using the absolute peaks of point (2). 4) The normalized autocorrelations are rounded to 1 decimal to exclude little oscillations around 0. 5) The differentiation of the sign changements of the normalized and rounded correlations is calculated. 6) The differentiations of point (5) are cumulated to obtain the final scalar count. 7) The ch1 Force signal is a well behaved and then bandlimited pulse with only two zero crossing (excluding noise), by contrast the ch2 Accel signal being a derivative (accelerometer signal) has surely more sign changements, consequently when the total sign changes of ch1 exceeds that of ch2 the channels are swapped. """ upper = int(3 * indx_echo_0) # Upper index limit for checking corr_y1 = correlate(y1[0:upper], y1[0:upper], mode='same') corr_y2 = correlate(y2[0:upper], y2[0:upper], mode='same') pk_abs_corr_1 = np.max(np.abs(corr_y1)) pk_abs_corr_2 = np.max(np.abs(corr_y2)) corr_y1 = np.round(corr_y1 / pk_abs_corr_1, 1) corr_y2 = np.round(corr_y2 / pk_abs_corr_2, 1) nz_diff_corr_y1 = (np.diff(np.sign(corr_y1), n=1) != 0).sum() nz_diff_corr_y2 = (np.diff(np.sign(corr_y2), n=1) != 0).sum() # if nz_diff_corr_y1 < nz_diff_corr_y2: is_swapped = False else: is_swapped = True # # if plot_debug: print("\n" + "Zero crossing comparison:", nz_diff_corr_y1, " < ", nz_diff_corr_y2, " ", not is_swapped) # # print(" Good if:") print(" nz_diff_corr_y1 < nz_diff_corr_y2") print("-----------------------------------") print(" ", nz_diff_corr_y1, " | ", nz_diff_corr_y2) print("-----------------------------------") if is_swapped: print("\n" + "/" * 35) print(" ERROR: Swapped ch1, ch2") print(" CORRECTION DONE!") print("/" * 35 + "\n") y1, y2 = y2, y1 # Restore from swap error # # # ======================== CHANNELS SWAP TESTING END =========================/ if not is_Saturated: """ If is_Saturated returns to the external acquisition loop: while i <= int(AveragesRequired) Preparing good data for storage: Relocation of the start of the inputrms1_32 signal in y1, y2, to eliminate the pre-trigger part that has a variable length. That part will be shortened as follows: a) Search the first index where the Pulse input Data array (y1) is bigger than pk * pk_foot. The index of that basement point will be named index_Trig. b) The new starting index is obtained decreasing, as possible, index_Trig by a fixed amount, in order to assure the inclusion of sufficient initial part of the signal. The corresponding amount of time, guard_T, will be equal to the hammer pulse width t.HAMMER_DT, the number of samples will be N_guard. """ y1_shape = y1.shape # Array y1 dimensions y2_shape = y2.shape # Array y2 dimensions if not (np.max(y1_shape) == np.max(y2_shape)): # Length comparison print(Length_msg) print("get_audio: y1_shape =", y1_shape) print("get_audio: y2_shape =", y2_shape) doBeep(on_list=bip_1_5_S[0], off_list=bip_1_5_S[1]) return # # # =============================== Relocation Begin =========================\ # pk1 = pk1_32 / c.full_scale_32 # Peak scaled to +/- 1 y1_length = np.max(y1_shape) # Length of y1 array indexes = np.array(np.nonzero( abs(y1) > pk1 * pk_foot))[0, :] # Array of indexes, [0,:] => from 1xN to N print("len(indexes) =", len(indexes)) index_Trig = np.amin( indexes) # Lowest index i.e. index pedestal start dT = 1.0 / SamplingFreq # Sampling time guard_T = HAMMER_DT # Safety margin time, before Pulse pedestal N_guard = int(round(guard_T / dT)) # Corresponding guard margin as samples if index_Trig > N_guard: # Guard must begin after y1[0] new_start = index_Trig - N_guard # Index (>=0) for the new starting point tmp_array = np.zeros( y1_length) # Temporary empty array for new y1 tmp_array[0:-1 - new_start] = copy.copy( y1[new_start:-1] ) # Fill y1 without the guard section of y1 # leaves a tail of zeros y1 = copy.copy(tmp_array) # y1 replaced by relocated data tmp_array = np.zeros(np.max( y2.shape)) # Temporary empty array for new y2 tmp_array[0:-1 - new_start] = copy.copy( y2[new_start:-1] ) # Fill y2 without the guard section of y2 y2 = copy.copy(tmp_array) # y2 replaced tmp_length = np.max(y1.shape) # Actual length of y1 array if tmp_length != y1_length: print( "\n" + "ERROR in get_audio: Length of y1, y2 modified erroneously" ) # # else: # Leave the signal unaltered pass # # #=============================== Relocation End ===========================/ # pk1 = np.max(np.abs(y1)) pk2 = np.max(np.abs(y2)) rms1 = np.std(y1) rms2 = np.std(y2) print("\n" + "Saving data to Disc") print("rms1 = {:3.1f}".format(rms1 * 1000), "mV; pk1 = {:3.1f}".format(pk1 * 1000), "mV") print("rms2 = {:3.1f}".format(rms2 * 1000), "mV; pk2 = {:3.1f}".format(pk2 * 1000), "mV") """ Save data to disc """ name_1 = "/y1_" + str('{:02d}'.format(i)) name_2 = "/y2_" + str('{:02d}'.format(i)) path_1 = c.temp_data_path + name_1 path_2 = c.temp_data_path + name_2 # if c.sav_mode == "npy": """ Saving in Numpy binary file, not compressed, add extension .npy """ np.save(path_1, y1) # Uncompressed format np.save(path_2, y2) elif c.sav_mode == "npz": """ Saving in Numpy binary file, compressed, add extension .npz """ np.savez_compressed(path_1, y1=y1) # Compressed format np.savez_compressed(path_2, y2=y2) # # elif c.sav_mode == "mat": """ Saving in Matlab format V.5, compressed, add automatically the extension .mat loadmat() will return the vector in a dictionary ex: d = scipy.io.loadmat("vector.mat) vect = d["vect"] vect = d1["y1"] and with one more dimension added ("column"): original vect.shape -> (n,), returned vect.shape -> (n, 1); to return to the original dimension: vect_reshaped = vect[:,0] """ scipy.io.savemat(path_1, {"y1": y1}, do_compression=True, oned_as="column") scipy.io.savemat(path_2, {"y2": y2}, do_compression=True, oned_as="column") # # print("New acquired data:", name_1, name_2) print("=o" * 25 + "=") save_blows(c.blows_list_txt, name_1, name_2) os.system("sync") # Delayed write causes a random delay time.sleep(0.2) # Delays for x seconds if plot_debug: sel = int(15 * indx_echo_0) # was 1.5 mx1 = max(np.abs(y1[0:sel])) mx2 = max(np.abs(y2[0:sel])) mx = 1.1 * max(mx1, mx2) tp = np.arange( c.N_OF_SAMPLES) * 1.0 / SamplingFreq # Time vector fig = plt.figure(1) fig.set_size_inches(w=3, h=4) fig.subplots_adjust(hspace=0.50) plt.subplot(211) plt.plot(tp[0:sel], y1[0:sel]) plt.title('get_audio Hammer') plt.xlabel('Time [s]') plt.ylabel('Force') plt.grid(True) plt.axis([tp[0], tp[sel], -mx, mx]) # plt.subplot(212) plt.plot(tp[0:sel], y2[0:sel]) plt.title('get_audio Accel') plt.xlabel('Time [s]') plt.ylabel('Accel') plt.grid(True) plt.axis([tp[0], tp[sel], -mx, mx]) # plt.show() time.sleep(0.1) # END if plot_debug: i += 1 # END if not is_Saturated): # END while i <= int(AveragesRequired): # stream.stop_stream() stream.close() # Close inp pyaudio object paudio.terminate() # print("get_audio: closed input device, terminated")