def csv_check(n_clicks): if n_clicks > 0: global SAVING global SAVE_LOCATION try: f = open(SAVE_LOCATION + '/NO_SAMPLE.csv') # Do something with the file f.close() except IOError: print("File not accessible") logger.add_text("NO_SAMPLE does not exist.") return hz_table table_values = [['<b>RT</b>', '<b>Pass/Fail</b>']] hz_out = auto_report.full_values(SAVE_LOCATION + '/NO_SAMPLE.csv') colour_map = ["white"] for entry in hz_out: table_values.append(('{0:.2f}'.format(entry[0]), entry[1])) if entry[1] == 1: colour_map.append(("white", "lightgreen")) else: colour_map.append(("white", "darksalmon")) new_table = go.Figure( data=[go.Table(header=dict(values=hz_list), cells=dict(values=table_values, fill_color=colour_map))]) new_table.update_layout(height=120, margin=dict(r=50, l=50, t=10, b=5)) return new_table else: return hz_table
def triggerMeasurements(n_runs, decay_time, noise_color, source_volume): decay_results = performMeasurement(n_runs, decay_time=decay_time, noise_color=noise_color, source_volume=source_volume) print('Measurement completed with {} runs'.format(n_runs)) logger.add_text('Measurement completed with {} runs'.format(n_runs)) print('Decay of {} seconds'.format(decay_time)) print('Noise color: {}'.format(noise_color)) return decay_results
def save_data(n_clicks, save_data, rh, room_temperature, pressure): global SAVING if SAVING: return True else: if n_clicks > 0: SAVING = True print("Choosing Save Location") global DATA global SAVE_LOCATION save_file_name = new_functions.new_save_data(save_data, rh, room_temperature, pressure, DATA) SAVING = False SAVE_LOCATION = save_file_name print("Save location changed to " + save_file_name) logger.add_text("Save location changed to " + save_file_name) return False, False return True, True
def trigger_measurements(n_clicks, sample_bool, number_of_runs, decay_time, noise_type, db_decay, room_volume, room_temp, room_humidity, room_pressure, bracket_type, job_no, client, specimen_name, specimen_desc, specimen_size, specimen_mass, specimen_area): if n_clicks > 0: print("Submitting Parameters for Measurements") print(number_of_runs, decay_time, noise_type, room_volume) global DATA global SAVE_LOCATION # Run Measurements DATA = new_functions.new_meas1(number_of_runs, decay_time, noise_type, db_decay, room_temp, room_humidity, room_pressure, 100) # Save Sample csv and generate report if sample_bool: new_functions.save_csv(SAVE_LOCATION + '/SAMPLE.csv', room_humidity, room_temp, room_pressure, DATA) global CONSOLE unique_values = [job_no, client, specimen_name, specimen_desc, specimen_size, specimen_mass, specimen_area, room_temp, room_humidity, room_pressure] try: auto_report.full_values(SAVE_LOCATION + '/SAMPLE.csv') auto_report.changeValues(SAVE_LOCATION, bracket_type, unique_values) logger.add_text("Report Created for " + bracket_type) except Exception as e: print(traceback.print_exc()) logger.add_text("Error in Reporting Process, check Python console.") return '' else: # Save No_Sample csv and generate table new_functions.save_csv(SAVE_LOCATION + '/NO_SAMPLE.csv', room_humidity, room_temp, room_pressure, DATA) logger.add_text("NO_SAMPLE.csv has been saved, use RT Check to see values.") return '' else: return ''
def performMeasurement(n_runs, decay_time, noise_color, source_volume): # Load configuration file - this needs to be absolute path for exe file config_data = helpers.parseConfigFile(path=r'config.cfg') # Nick PC ## The system names for the NI devices can be found using the NI-MAX package which is installed as part of the DAQ-mx installation ## These names can be changed in NI-MAX, but need to be updated in the software configuration file ao_device_name = config_data[ 'aodevicename'] # Name of NI analog output device - 2 channel voltage output ai_device_name = config_data[ 'aidevicename'] # Name of NI analog input device - 8 channel voltage (microphone) input dio_device_name = config_data[ 'diodevicename'] # Name of NI digital input/output device # Measurement parameters in config file fs = int(config_data['samplingfrequency']) # Sampling frequency rise_time = float( config_data['risetime'] ) # Desired rise time for reverberation time measurements. This prevents sharp clips on sound sources excitation_time = float( config_data['excitationtime'] ) # Desired excitation time for reverberation time measurements mics = config_data['micid'] # IDs for microphones as listed in NI-MAX channel_name = config_data['micnames'] # Microphone names analog_output_id = config_data[ 'outputid'] # IDs for outputs as listed in NI-MAX storedSignal = config_data[ 'usestoredexcitation'] # Defines how to generate signal. If true a prebuilt psuedo-random signal is used, otherwise generates a new signal in code (slower) # Generate excitation array - use prebuilt excitation if this is set in config. # Typically building a new array is significantly slower and will yeild slightly less repeatable results # Both signals are non-correlated for each sound source if storedSignal == 'true': excitation_array1, t_array = helpers.usePreExcitation( rise_time=rise_time, excitation_time=excitation_time, decay_time=decay_time, fs=fs, noise_color="pink", signal_num=1) excitation_array2, t_array = helpers.usePreExcitation( rise_time=rise_time, excitation_time=excitation_time, decay_time=decay_time, fs=fs, noise_color="pink", signal_num=2) else: excitation_array1, t_array = helpers.createExcitation( rise_time=rise_time, excitation_time=excitation_time, decay_time=decay_time, fs=fs, noise_color=noise_color) excitation_array2, t_array = helpers.createExcitation( rise_time=rise_time, excitation_time=excitation_time, decay_time=decay_time, fs=fs, noise_color=noise_color) # Build 2d array to excite both sources using 2x analog outputs twoChanEx = (source_volume / 100) * np.vstack( [excitation_array1, excitation_array2]) # Calculate total measurement duration and corresponding number of samples t_tot = rise_time + excitation_time + decay_time N_samp = int(t_tot) * fs # Setup NI task and begin measurement with nidaqmx.task.Task("OutputTask") as ao_task, nidaqmx.task.Task( 'InputTask') as ai_task: print("Setting up generator") ################## Setup analogue output ################ for ao_channel in analog_output_id: ao_chan_name = ao_device_name + '/' + ao_channel # Build channel name based on config details print('AO channel name: {}'.format(ao_chan_name)) # Add an analog output channel ao_task.ao_channels.add_ao_voltage_chan( ao_chan_name, name_to_assign_to_channel=ao_channel, min_val=-3.0, max_val=3.0) # Setup tast timing - use fixed sample length ao_task.timing.cfg_samp_clk_timing(fs, sample_mode=AcquisitionType.FINITE, samps_per_chan=N_samp) # Store outgoing data on chassis but do not start measurement ao_task.write(twoChanEx, auto_start=False) ############ Setup microphone inputs ############# print("Setting up inputs") for micID, channel in zip(mics, channel_name): ai_chan_name = ai_device_name + '/' + micID # Build channel name based on config details print('AI channel name: {}'.format(ai_chan_name)) # Add an analog input channel ai_task.ai_channels.add_ai_microphone_chan( ai_chan_name, name_to_assign_to_channel=channel, mic_sensitivity=22.4, max_snd_press_level=140, units=nidaqmx.constants.SoundPressureUnits.PA) # Setup tast timing - use fixed sample length ai_task.timing.cfg_samp_clk_timing(fs, sample_mode=AcquisitionType.FINITE, samps_per_chan=N_samp) results = [] print("Starting measurements") for nxd in range(n_runs): print("Run: {}".format(nxd)) logger.add_text("Run: {}/{}".format(nxd + 1, n_runs)) # Start output signal and measurement ao_task.start() ai_task.start() # Wait until both tasks are complete ao_task.wait_until_done(timeout=t_tot + 5) ai_task.wait_until_done(timeout=t_tot + 5) # Record data from mic data = ai_task.read(number_of_samples_per_channel=N_samp) print('Shape of data: {}'.format(np.shape(data))) # Stop both tasks ao_task.stop() ai_task.stop() results.append(data) print("Measurement completed") print('Shape of results: {}'.format(np.shape(results))) # Store results in no array and return this data results_np = np.array(results) print(results_np) return results_np
def save_csv(save_filename, rh, room_temperature, pressure, data): env_df = buildRH_TempDF(rh, room_temperature, pressure) save_data(data, env_df=env_df, filename=save_filename) logger.add_text(save_filename + ' has been created and saved.') print(save_filename) return
def performRTcalculation(data, volume, temp, relativeHumidity, pressure, db_decay='t20', decay_time=5): print("Shape of data into RT calc: {}".format(np.shape(data))) # Load configuration file - path needs to be absolute for executable to find it config_data = helpers.parseConfigFile(path=r'config.cfg') print(config_data) # Read all configuration data print('Reading config data from .config file') fs = int(config_data['samplingfrequency']) # Sampling frequency rise_time = float(config_data['risetime']) # Rise time for excitation excitation_time = float(config_data['excitationtime'] ) # Duration that excitation is played for mics = config_data['micid'] # IDs for microphones as listed in NI-MAX channel_name = config_data['micnames'] # Microphone names analog_output_id = config_data[ 'outputid'] # IDs for outputs as listed in NI-MAX estRT = float( config_data['estimatedrt'] ) # Estimated reverberation time for room - allows selection of filtering time pRef = float(config_data['referencepressure'] ) # Reference pressure value for conversion to dB p_ref = pRef n_mics = int( config_data['nummics']) # Number of microphones used in measurements window_length = float( config_data['windowlength']) # Desired length of window fLow = int( config_data['flow']) # Low frequency limit for 1/3rd octave bands fHigh = int( config_data['fhigh']) # High frequency limit for 1/3rd octave bands averaging_type = config_data[ 'avgtype'] # Select exponential or linear averaging signal_type = config_data[ 'usestoredexcitation'] # If a pre-calculated excitation signal was used in measurements # General parameters t_tot = rise_time + excitation_time + decay_time # Total duration of measurement N_samp = int(t_tot) * fs # Number of samples in measurement dt = 1.0 / fs # Sample spacing t_array = np.arange(start=0, stop=t_tot, step=dt) # Array of time points (seconds) windowN = int(window_length * fs) # Number of samples in window print("Length of window: {}s : {} samples".format(window_length, windowN)) # Setup loop parameters total_reverb = {} mic_location = [1, 2, 3, 4, 5, 6] # Perform ensemble averaging of individual mics for number of runs # averaged_data = helpers.ensemble_average(raw_data=data, n_mics=n_mics) print('Performing ensemble averaging at each microphone') print("Shape of data before ensemble average: {}".format(np.shape(data))) mean_data = np.mean(a=data, axis=0) print("Shape of data after ensemble average: {}".format( np.shape(mean_data))) # Build pandas df for data print('Inserting data into pandas dataFrame') df = pd.DataFrame() rt_df = pd.DataFrame(columns=['frequency_Hz'] + mics) for row, mic in zip(mean_data, mics): print('Inserted mic: {}'.format(mic)) df['mean_{}'.format(mic)] = row print(df) # Step though mics and calculate RT for mic in mics: print('Calculating RT for mic: {}'.format(mic)) logger.add_text('Calculating RT for mic: {}'.format(mic)) # Perform 3rd octave filters print( 'Applying 1/3rd Octave filters to data between {}Hz - {}Hz'.format( fLow, fHigh)) filtered_data = filterAndBands.thirdOctFilters( data=df['mean_{}'.format(mic)], fs=fs, f_low=fLow, f_high=fHigh) rt_df['frequency_Hz'] = filtered_data.keys() for key in filtered_data: df['{}_{}Hz'.format(mic, key)] = 10 * np.log10( (abs(filtered_data[key])**2) / (p_ref**2)) # df['{}_log_data'.format(mic)] = 10*np.log10(abs(df['mean_{}'.format(mic)])/p_ref) df['{}_samples'.format(mic)] = np.arange(len( df['mean_{}'.format(mic)])) df['{}_seconds'.format(mic)] = df['{}_samples'.format(mic)] / fs dummy_RT = [] for key in filtered_data: print("Averaging {}Hz band for {}".format(key, mic)) df['{}_{}Hz_exp'.format(mic, key)] = df['{}_{}Hz'.format( mic, key)].ewm(span=windowN, adjust=False).mean() print(df['{}_{}Hz_exp'.format(mic, key)]) # exit() # df['{}_{}Hz_lin'.format(mic, key)] = df['{}_{}Hz'.format(mic,key)].rolling(window=windowN).mean() windowed_data = df['{}_{}Hz_{}'.format(mic, key, averaging_type)].values print(windowed_data) # exit() t_array = df['{}_seconds'.format(mic)].values print('Seperating signal into sections for evaluation') excitation_range = [ int((rise_time) * fs), int((rise_time + excitation_time) * fs) ] print('Excitation range:') print(excitation_range) excitation_level = filterAndBands.dBavg( windowed_data[excitation_range[0] + int(0.5 * fs):excitation_range[1] - int(0.5 * fs)]) print('Excitation level: {}'.format(excitation_level)) trigger_level = excitation_level - 5 print('Decay trigger level: {}'.format(trigger_level)) min_decay_level = min(windowed_data[excitation_range[1]:]) print('Minimum decay level: {}'.format(min_decay_level)) bg_start = len(windowed_data) - 2 * fs print('Start of bg evaluation - last 2 sec: {}'.format(bg_start)) bg_level = filterAndBands.dBavg(windowed_data[bg_start:]) print('Background noise level: {}'.format(bg_level)) if db_decay == 't20': bg_trigger_level = max([trigger_level - 20, bg_level + 5]) print('Trigger level - 20dB: {}'.format(bg_trigger_level)) print('Headroom: {}'.format(bg_trigger_level - bg_level)) elif db_decay == 't30': bg_trigger_level = max([trigger_level - 30, bg_level + 5]) print('Trigger level - 30dB: {}'.format(bg_trigger_level)) print('Headroom: {}'.format(bg_trigger_level - bg_level)) elif db_decay == 'all': bg_trigger_level = bg_level + 10 print('Background + 10dB: {}'.format(bg_trigger_level)) print('Headroom: {}'.format(bg_trigger_level - bg_level)) ndx_start = np.where( windowed_data[excitation_range[1]:] <= trigger_level) decay_start = ndx_start[0][0] print('Start of evaluation range {} seconds'.format( decay_start, decay_start / fs)) ndx_end = np.where( windowed_data[excitation_range[1]:] <= bg_trigger_level) decay_end = ndx_end[0][0] print('End of evaluation range {} samples / {} seconds'.format( decay_end, decay_end / fs)) # exit() print("Calculating RT of {}Hz band for {}".format(key, mic)) fitting_level = windowed_data[excitation_range[1] + decay_start:excitation_range[1] + decay_end] print("Fitting level:") print(fitting_level) fitting_times = t_array[excitation_range[1] + decay_start:excitation_range[1] + decay_end] print("Fitting times:") print(fitting_times) slope, intercept, r_value, p_value, std_err = stats.linregress( x=fitting_times, y=fitting_level) print('Slope: {}, intercept: {}, R-squared: {}'.format( slope, intercept, r_value)) t_plot = np.arange(start=0, stop=rise_time + excitation_time + decay_time, step=0.1) y_fitted = slope * t_plot + intercept print("R-squared: {}".format(r_value)) RT = -60 / slope dummy_RT.append(RT) print("Reverberation Time at {} Hz: {}s".format(key, RT)) # exit() rt_df[mic] = dummy_RT print('Reverb time prior to averaging') print(rt_df) rt_df = rt_df.set_index('frequency_Hz') rt_df['avg'] = rt_df.mean(axis=1) print('Averaged RT') print(rt_df) print('Calculating Absorption Area') a = [] for freq in rt_df.index.array: print('Calculating absorption area for {} Hz'.format(freq)) rt = rt_df.loc[freq] print(rt) rt = rt['avg'] print('Average RT: {}'.format(rt)) a.append( iso354.soundAbsorptionArea(V=volume, RT=rt, T=temp, f=int(freq), hr=relativeHumidity, Pa=pressure * 1000)) rt_df['abs_area'] = a return rt_df