def cumulative_integration(inputSignal, bypassLundeby, plotLundebyResults, suppressWarnings=True, **kwargs): """Cumulative integration with proper corrections.""" def plot_lundeby(): c0, c1, interIdx, BGL = lundebyParams fig = plt.figure(figsize=(10, 5)) ax = fig.add_axes([0.08, 0.15, 0.75, 0.8], polar=False, projection='rectilinear', xscale='linear') line = c1*timeVector + c0 ax.plot(timeVector, 10*np.log10(timeSignal**2), label='IR') ax.axhline(y=10*np.log10(BGL), color='#1f77b4', label='BG Noise', c='red') ax.plot(timeVector, line,label='Late slope', c='black') ax.axvline(x=interIdx/samplingRate, label='Truncation point', c='green') ax.grid() ax.set_xlabel('Time [s]') ax.set_ylabel('Amplitude [dBFS]') plt.title('{0:.0f} [Hz]'.format(band)) ax.legend(loc='best', shadow=True, fontsize='x-large') timeSignal = inputSignal.timeSignal[:] # Substituted by SignalObj.crop in analyse function # timeSignal, sampleShift = _circular_time_shift(timeSignal) # del sampleShift hSignal = SignalObj(timeSignal, inputSignal.lengthDomain, inputSignal.samplingRate) hSignal = _filter(hSignal, **kwargs) bands = FOF(nthOct=kwargs['nthOct'], freqRange=[kwargs['minFreq'], kwargs['maxFreq']])[:,1] listEDC = [] for ch in range(hSignal.numChannels): signal = hSignal[ch] band = bands[ch] timeSignal = cp.copy(signal.timeSignal[:]) timeVector = signal.timeVector[:] samplingRate = signal.samplingRate numSamples = signal.numSamples numChannels = signal.numChannels timeLength = signal.timeLength energyDecay, energyVector, lundebyParams = \ energy_decay_calculation(band, timeSignal, timeVector, samplingRate, numSamples, numChannels, timeLength, bypassLundeby, suppressWarnings=suppressWarnings) listEDC.append((energyDecay, energyVector)) if plotLundebyResults: # Placed here because Numba can't handle plots. # plot_lundeby(band, timeVector, timeSignal, samplingRate, # lundebyParams) plot_lundeby() return listEDC
def G_Lps(IR, nthOct, minFreq, maxFreq): """G_Lps Calculates the recalibration level, for both in-situ and reverberation chamber. Lps is applied for G calculation. During the recalibration: source height and mic heigth must be >= 1 [m], while the distance between source and mic must be <= 1 [m]. The distances must be the same for in-situ and reverberation chamber measurements. Reference: Christensen, C. L.; Rindel, J. H. APPLYING IN-SITU RECALIBRATION FOR SOUND STRENGTH MEASUREMENTS IN AUDITORIA. :param IR: one channel impulsive response :type IR: ImpulsiveResponse :param nthOct: number of fractions per octave :type nthOct: int :param minFreq: analysis inferior frequency limit :type minFreq: float :param maxFreq: analysis superior frequency limit :type maxFreq: float :return: Analysis object with the calculated parameter :rtype: Analysis """ # Code snippet to guarantee that generated object name is # the declared at global scope # for frame, line in traceback.walk_stack(None): for framenline in traceback.walk_stack(None): # varnames = frame.f_code.co_varnames varnames = framenline[0].f_code.co_varnames if varnames is (): break # creation_file, creation_line, creation_function, \ # creation_text = \ extracted_text = \ traceback.extract_stack(framenline[0], 1)[0] # traceback.extract_stack(frame, 1)[0] # creation_name = creation_text.split("=")[0].strip() creation_name = extracted_text[3].split("=")[0].strip() # firstChNum = IR.systemSignal.channels.mapping[0] # if not IR.systemSignal.channels[firstChNum].calibCheck: # raise ValueError("'IR' must be a calibrated ImpulsiveResponse") if isinstance(IR, SignalObj): SigObj = IR elif isinstance(IR, ImpulsiveResponse): SigObj = IR.systemSignal else: raise TypeError("'IR' must be an ImpulsiveResponse or SignalObj.") # Windowing the IR # dBtoOnSet = 20 # dBIR = 10*np.log10((SigObj.timeSignal[:,0]**2)/((2e-5)**2)) # windowStart = np.where(dBIR > (max(dBIR) - dBtoOnSet))[0][0] broadBandTimeSignal = cp.copy(SigObj.timeSignal[:, 0]) broadBandTimeSignalNoStart, sampleShift = \ _circular_time_shift(broadBandTimeSignal) windowLength = 0.0032 # [s] windowEnd = int(windowLength * SigObj.samplingRate) hSignal = SignalObj( broadBandTimeSignalNoStart[:windowEnd], # hSignal = SignalObj(timeSignal, SigObj.lengthDomain, SigObj.samplingRate) hSignal = _filter(signal=hSignal, nthOct=nthOct, minFreq=minFreq, maxFreq=maxFreq) bands = FOF(nthOct=nthOct, freqRange=[minFreq, maxFreq])[:, 1] Lps = [] for chIndex in range(hSignal.numChannels): timeSignal = cp.copy(hSignal.timeSignal[:, chIndex]) # timeSignalNoStart, sampleShift = _circular_time_shift(timeSignal) # windowLength = 0.0032 # [s] # windowEnd = int(windowLength*SigObj.samplingRate) Lps.append( # 10*np.log10(np.trapz(y=timeSignalNoStart[:windowEnd]**2/(2e-5**2), 10 * np.log10( np.trapz( y=timeSignal**2 / (2e-5**2), # x=hSignal.timeVector[sampleShift:sampleShift+windowEnd]))) x=hSignal.timeVector))) LpsAnal = Analysis(anType='mixed', nthOct=nthOct, minBand=float(bands[0]), maxBand=float(bands[-1]), data=Lps, comment='Source recalibration method IR') LpsAnal.creation_name = creation_name LpsAnal.windowLimits = ((sampleShift) / SigObj.samplingRate, (sampleShift + windowEnd) / SigObj.samplingRate) # Plot IR cutting # fig = plt.figure(figsize=(10, 5)) # ax = fig.add_axes([0.08, 0.15, 0.75, 0.8], polar=False, # projection='rectilinear', xscale='linear') # ax.plot(SigObj.timeVector, 10*np.log10(SigObj.timeSignal**2/2e-5**2)) # ax.axvline(x=(sampleShift)/SigObj.samplingRate, linewidth=4, color='k') # ax.axvline(x=(sampleShift+windowEnd)/SigObj.samplingRate, linewidth=4, color='k') # ax.set_xlim([(sampleShift-100)/SigObj.samplingRate, (sampleShift+windowEnd+100)/SigObj.samplingRate]) return LpsAnal
def G_Lpe(IR, nthOct, minFreq, maxFreq, IREndManualCut=None): """ Calculate the energy level from the room impulsive response. Reference: Christensen, C. L.; Rindel, J. H. APPLYING IN-SITU RECALIBRATION FOR SOUND STRENGTH MEASUREMENTS IN AUDITORIA. :param IR: one channel impulsive response :type IR: ImpulsiveResponse :param nthOct: number of fractions per octave :type nthOct: int :param minFreq: analysis inferior frequency limit :type minFreq: float :param maxFreq: analysis superior frequency limit :type maxFreq: float :return: Analysis object with the calculated parameter :rtype: Analysis """ # Code snippet to guarantee that generated object name is # the declared at global scope # for frame, line in traceback.walk_stack(None): for framenline in traceback.walk_stack(None): # varnames = frame.f_code.co_varnames varnames = framenline[0].f_code.co_varnames if varnames is (): break # creation_file, creation_line, creation_function, \ # creation_text = \ extracted_text = \ traceback.extract_stack(framenline[0], 1)[0] # traceback.extract_stack(frame, 1)[0] # creation_name = creation_text.split("=")[0].strip() creation_name = extracted_text[3].split("=")[0].strip() # firstChNum = IR.systemSignal.channels.mapping[0] # if not IR.systemSignal.channels[firstChNum].calibCheck: # raise ValueError("'IR' must be a calibrated ImpulsiveResponse") if isinstance(IR, SignalObj): SigObj = cp.copy(IR) elif isinstance(IR, ImpulsiveResponse): SigObj = cp.copy(IR.systemSignal) else: raise TypeError("'IR' must be an ImpulsiveResponse or SignalObj.") # Cutting the IR if IREndManualCut is not None: SigObj.crop(0, IREndManualCut) timeSignal, _ = _circular_time_shift(SigObj.timeSignal[:, 0]) # Bands filtering # hSignal = SignalObj(SigObj.timeSignal[:,0], hSignal = SignalObj(timeSignal, SigObj.lengthDomain, SigObj.samplingRate) hSignal = _filter(signal=hSignal, nthOct=nthOct, minFreq=minFreq, maxFreq=maxFreq) bands = FOF(nthOct=nthOct, freqRange=[minFreq, maxFreq])[:, 1] Lpe = [] for chIndex in range(hSignal.numChannels): Lpe.append(10 * np.log10( np.trapz(y=hSignal.timeSignal[:, chIndex]**2 / (2e-5**2), x=hSignal.timeVector))) LpeAnal = Analysis(anType='mixed', nthOct=nthOct, minBand=float(bands[0]), maxBand=float(bands[-1]), data=Lpe, comment='h**2 energy level') LpeAnal.creation_name = creation_name return LpeAnal