#pragma rtGlobals=1 // Use modern global access method. menu "eFish" "Process file", efProcessOneFile() "Batch process files/2", efGetFilesToProcess() "-" "Load .eod .dmf .rms files/1", efLoadSingleFloatFiles() "Save .eod .dmf .rms waves", efSaveWaves() "-" "Remove playback", efGetFilesForSubtraction() "-" "Settings/3", eFishSettings() end //------------------------------------------------------------ Function CheckeFishGlobals() String dfSav= GetDataFolder(1) // Save data folder String theDF= "root:eFish" NewDataFolder/O/S $theDF if( (exists("GraphWidth") == 0) || (exists("GraphHeight") == 0) || (exists("LoadEOD") == 0)) Variable/G GraphWidth = 7,GraphHeight=6, LoadEOD=1 endif if( (exists("LowHz") == 0) || (exists("HighHz") == 0) || (exists("WindowDur") == 0) || (exists("OutputInterval") == 0) ) Variable/G LowHz = 500,HighHz=5000,WindowDur=0.006, OutputInterval=0.002 endif if( (exists("OverlapPercent") == 0) || (exists("dBthreshold") == 0) ) Variable/G OverlapPercent, dBthreshold=-94 endif NVAR WindowDur=root:eFish:WindowDur, OutputInterval=root:eFish:OutputInterval NVAR OverlapPercent=root:eFish:OverlapPercent OverlapPercent=(1-(OutputInterval/WindowDur))*100 if( (exists("LogToNotebook") == 0) || (exists("CountChirps") == 0) || (exists("ListBoxSelection") == 0) ) Variable/G LogToNotebook = 0, CountChirps=0, ListBoxSelection=1 endif if( (exists("LoadWAVFiles") == 0) || (exists("CurrChirpNum") == 0) ) Variable/G LoadWAVFiles = 0, CurrChirpNum=0 endif if( (exists("ChirpHzThresh") == 0) || (exists("ChirpHzLOWThresh") == 0) || (exists("minChirpDur") == 0) || (exists("minIntervalDur") == 0)|| (exists("BaselineDur") == 0)) Variable/G ChirpHzThresh = 3, ChirpHzLOWThresh=1, minChirpDur=10, minIntervalDur=100, BaselineDur=5 endif if( (exists("NewStatsThresh") == 0) || (exists("AdjustHzThresh") == 0) || (exists("TempPeakWidth") == 0) ) Variable/G NewStatsThresh = 3, AdjustHzThresh=1, TempPeakWidth endif if( (exists("MaxChirpDur") == 0) || (exists("MaxDurSkip") == 0)) Variable/G MaxChirpDur = 30, MaxDurSkip=10 endif if( (exists("UseFFTPhase") == 0) ) Variable/G UseFFTPhase = 0 endif if( (waveexists(ChirpOnsetsStr) == 0) || (waveexists(ChirpOffsetsStr) == 0) || (waveexists(ChirpBaselineHzStr) == 0) ) String/G ChirpOnsetsStr, ChirpOffsetsStr, ChirpBaselineHzStr endif if( (waveexists(PosStartTimes) == 0) || (waveexists(PosEndTimes) == 0) || (waveexists(BaseCrossPoints) == 0) ) Make/O/N=9999 PosStartTimes, PosEndTimes, BaseCrossPoints endif if( (waveexists(NegStartTimes) == 0) || (waveexists(NegEndTimes) == 0) ) Make/O/N=9999 NegStartTimes, NegEndTimes endif if( (waveexists(ChirpBaselineHz) == 0) || (waveexists(ChirpBaselineHzPlus) == 0) || (waveexists(ChirpBaselineHzNeg) == 0) || (waveexists(BaselineHzPeakWidth) == 0) ) Make/O/N=9999 ChirpBaselineHz, ChirpBaselineHzPlus, ChirpBaselineHzNeg, BaselineHzPeakWidth endif if( (exists("NumChirpsFoundStr") == 0) || (exists("CurrFileName") == 0)) String/G NumChirpsFoundStr = "", CurrFileName="" endif SetDataFolder dfSav // Return to data folder End //------------------------------------------------------------ Function efGetFilesToProcess() CheckeFishGlobals() String ListOfFiles, FilePathName, FileName, FilePath Variable refNum NVAR LoadWAVFiles=root:eFish:LoadWAVFiles if(LoadWAVFiles) Open/D/R/M="Select one file to get list of files"/T=".wav" refNum if(strlen(S_fileName)<=0) return 0 endif FilePathName=S_fileName FileName = StringFromList(ItemsInList(FilePathName,":")-1, FilePathName, ":") FilePath=RemoveFromList(FileName, FilePathName, ":") NewPath/C/O/Q LoadPath, FilePath NewPath/C/O/Q SavePath, FilePath+"OutputFiles" ListOfFiles = IndexedFile(LoadPath,-1,".wav") else Open/D/R/M="Select ??? one file to get list of files"/T=".ibw" refNum if(strlen(S_fileName)<=0) return 0 endif FilePathName=S_fileName FileName = StringFromList(ItemsInList(FilePathName,":")-1, FilePathName, ":") FilePath=RemoveFromList(FileName, FilePathName, ":") NewPath/C/O/Q LoadPath, FilePath NewPath/C/O/Q SavePath, FilePath+"OutputFiles" ListOfFiles = IndexedFile(LoadPath,-1,".ibw") endif eFShowFileList(ListOfFiles) End //------------------------------------------------------------ Function efShowFileList(ListOfFiles) String ListOfFiles DoWindow PrintList // Remember window position if the window exists if(V_flag) GetWindow PrintList wsize DoWindow/K PrintList NewPanel /K=1 /W=(V_left,V_top,V_right,V_bottom) as"Print List" else DoWindow/K PrintList NewPanel/K=1 /W=(550,45,800,455) as "Print List" endif DoWindow/C PrintList Variable rows = ItemsInList(ListOfFiles) Make/O/T/N=(rows,1) root:PrintListWave Make/O/N=(rows,1) root:WaveOfFilesSel Wave/T PrintListWave=root:PrintListWave Wave WaveOfFilesSel=root:WaveOfFilesSel; WaveOfFilesSel=1 variable i, numlist for(i=0;istrlen(BaseName)-6;i-=1) if(stringmatch(BaseName[i,i], ".")==1) BaseName[i,strlen(BaseName)]="" break endif endfor Variable SRate=1/deltax(InputSound) // Prompt for Pitch estimation variables or get variables from the Spectrogram settings dialog NVAR LowHz=root:eFish:LowHz, HighHz=root:eFish:HighHz NVAR WindowDur=root:eFish:WindowDur, OutputInterval=root:eFish:OutputInterval NVAR OverlapPercent=root:eFish:OverlapPercent, dBthreshold=root:eFish:dBthreshold //if(PromptSettings) // prompt WindowDur, "Window duration (sec.):" // prompt OverlapPercent, "Overlap (%; default for "+num2str(OutputInterval)+" interval)" // prompt dBthreshold, "Threshold (dB):" // prompt LowHz, "Lowest frequency (Hz):" // prompt HighHz, "Highest frequency (Hz):" // doprompt "Autocorrelation", WindowDur, OverlapPercent, dBthreshold, LowHz, HighHz // if(V_Flag) // return 0 // endif //endif WaveStats/Q InputSound Variable thresholdRMS=V_max*(10^(dBthreshold/20)) Variable Overlap=OverlapPercent/100 Variable WindowPnts=WindowDur*Srate, MinAccurateHz=(1/WindowDur)*3, SeriousErrors=0, PotentialErrors=0 variable SegStep = max(ceil((WindowPnts)*(1-Overlap)),1) variable NumSegs = max(floor(numpnts(InputSound)/SegStep)-1,1) NVAR UseFFTPhase=root:eFish:UseFFTPhase if(UseFFTPhase) // check to see if FFT size is a prime number and warn if so. Variable k, Prime=1 for(k=2;k0 && AutoOut[f+1]-AutoOut[f]<0 && AutoOut[f]-AutoOut[f-2]>0 && AutoOut[f]-AutoOut[f+2]>0) firstPnt=f-2 lastPnt=f+2 break endif endfor // interpolate and get location (time) of first peak duplicate/O/R=[firstPnt,lastPnt] AutoOut, Int if(lastPnt-firstPnt >=3) execute "Interpolate/T=3/N=1000/F=1/Y=int_SS int" wave int_SS=root:int_SS WaveStats/Q int_SS peak=V_maxloc if(peak<(1/LowHz) && peak>(1/HighHz) ) if(peak<(1/MinAccurateHz)) HzOut[i]=1/peak else HzOut[i]=1/peak PotentialErrors+=0 endif else HzOut[i]=NaN endif RMSOut[i]=RMS else HzOut[i]=NaN RMSOut[i]=RMS SeriousErrors+=1 endif endif // for FFT analysis endif //threshold if(mod(i, 500)==0) doupdate endif endfor KillWaves/Z Seg,Win,Int,int_SS,AutoOut, AOT //,InputSound if(PotentialErrors>=1 || SeriousErrors>=1) String AlertStr if(PotentialErrors>=1 && SeriousErrors<=1) SPrintf AlertStr, "%g potential error(s) were reported. Window length should be longer than %g to find peaks below %g Hz", PotentialErrors, WindowDur, (1/WindowDur)*3 else SPrintf AlertStr, "%g serious undersampling(s) were reported. Window length should be longer than %g to find peaks below %g Hz", SeriousErrors, WindowDur, (1/WindowDur)*3 endif if(PotentialErrors>=1 && SeriousErrors>=1) SPrintf AlertStr, "%g serious undersampling(s) and %g potential error(s) were reported. Window length should be longer than %g to find peaks below %g Hz," SeriousErrors, PotentialErrors, WindowDur, (1/WindowDur)*3 endif //DoAlert 0, AlertStr Print AlertStr endif // Write files efSaveSingleFloat32bit(InputSound, HzOut, RMSOut) SetDataFolder dfSav return 0 End //-------------------------------------------------------------------------- Function MakeEFGraph(EOD, DF, RMS, CurrentFile, analysis) Wave EOD, DF, RMS String CurrentFile Variable analysis SVAR ChirpOnsetsStr=root:eFish:ChirpOnsetsStr, ChirpOffsetsStr=root:eFish:ChirpOffsetsStr SVAR ChirpBaselineHzStr=root:eFish:ChirpBaselineHzStr NVAR GraphHeight=root:eFish:GraphHeight, GraphWidth=root:eFish:GraphWidth ChirpOnsetsStr="all;"; ChirpOffsetsStr=""; ChirpBaselineHzStr="" SVAR NumChirpsFoundStr=root:eFish:NumChirpsFoundStr NumChirpsFoundStr="0" String DFstr=PossiblyQuoteName(NameOfWave(DF)), RMSstr=PossiblyQuoteName(NameOfWave(RMS)) DoWindow/K eFish Display/K=1/I/W=(0.2,0.5,GraphWidth+0.2,GraphHeight+0.5)/L=LeftHz DF as CurrentFile;delayupdate DoWindow/C eFish;delayupdate SetWindow eFish note=CurrentFile ;delayupdate SetWindow eFish, hook=eFWindowEvent2;DelayUpdate NVAR LoadEOD=root:eFish:LoadEOD if(LoadEOD) String EODstr=PossiblyQuoteName(NameOfWave(EOD)) AppendToGraph/L=LeftRMS EOD;delayupdate ModifyGraph rgb($EODstr)=(48059,48059,48059);delayupdate endif AppendToGraph/L=LeftRMS RMS;delayupdate ModifyGraph margin(left)=50;delayupdate ModifyGraph lSize=0.5;delayupdate ModifyGraph rgb($DFstr)=(1,3,39321),rgb($RMSstr)=(1,3,39321);delayupdate ModifyGraph tick=2;delayupdate ModifyGraph fSize=12;delayupdate ModifyGraph highTrip(LeftHz)=999,highTrip(LeftRMS)=999;delayupdate ModifyGraph standoff(LeftHz)=0,standoff(LeftRMS)=0;delayupdate ModifyGraph lblPos(LeftHz)=50,lblPos(LeftRMS)=50;delayupdate ModifyGraph freePos(LeftHz)={0,bottom};delayupdate ModifyGraph freePos(LeftRMS)={0,bottom};delayupdate ModifyGraph axisEnab(LeftHz)={0.53,1};delayupdate ModifyGraph axisEnab(LeftRMS)={0,0.47};delayupdate SetAxis LeftRMS 0,7.1 ;delayupdate Label LeftHz "frequency (\\U)";delayupdate Label LeftRMS "amplitude (\\U)";delayupdate Label bottom "time (\\U)";delayupdate analysis=1 if(analysis) ControlBar 28;delayupdate Button CountChirps pos={10,3},title="Count", proc=CountChirpsProc;delayupdate SetVariable NumChirps pos={65,7},size={35,15},frame=0,title=" ",value= root:eFish:NumChirpsFoundStr;delayupdate PopupMenu ChirpListPopUp pos={100,3}, title="go",mode=0,proc=ChirpNumProc,value=#root:eFish:ChirpOnsetsStr;delayupdate Button LastFile pos={147,3},size={20,20},proc=efNextFileProc,title="-" Button NextFile pos={170,3},size={20,20},proc=efNextFileProc,title="+" ValDisplay ChirpNum pos={194,6},size={33,20},value=#root:eFish:CurrChirpNum Button Play pos={235,3},size={50,20},proc=efPlayProc,title="Play" Button Stop pos={290,3},size={50,20},proc=efPlayProc,title="Stop" Button GetStats pos={355,3},size={80,20},proc=efStatsProc,title="Get Stats" endif doupdate NVAR CurrChirpNum=root:eFish:CurrChirpNum; CurrChirpNum=1 NVAR CountChirps=root:eFish:CountChirps, LogToNotebook=root:eFish:LogToNotebook if(CountChirps) CountChirpsProc("CountChirps") if(LogToNotebook) efStatsProc("GetStats") endif endif End Function efNextFileProc(ctrlName) : ButtonControl String ctrlName SVAR ChirpOnsetsStr=root:eFish:ChirpOnsetsStr if(ItemsInlist(ChirpOnsetsStr)<=1) Print "No chirps counted" return 0 endif GetWindow $WinName(0,1) note String Name=S_Value SetDataFolder root: Wave DMF=$Name+".dmf" NVAR CurrChirpNum=root:eFish:CurrChirpNum if(stringmatch(ctrlName, "NextFile")) CurrChirpNum+=1 else CurrChirpNum-=1 endif SVAR ChirpOnsetsStr=root:eFish:ChirpOnsetsStr, ChirpOffsetsStr=root:eFish:ChirpOffsetsStr Variable NumChirps=ItemsInList(ChirpOnsetsStr)-2 if(CurrChirpNum>NumChirps+1) CurrChirpNum=1 // skip "all;..." endif if(CurrChirpNum<1) // skip "all;..." CurrChirpNum=NumChirps+1 endif Variable Onset=str2num(stringfromlist(CurrChirpNum, ChirpOnsetsStr)) Variable Offset=str2num(stringfromlist(CurrChirpNum-1, ChirpOffsetsStr)) Onset=Onset-(max(Offset-Onset,0.1)) Offset=Offset+(max(Offset-Onset,0.1)) SetAxis bottom max(Onset, leftx(DMF)), min(Offset, pnt2x(DMF,numpnts(DMF)-1) ) WaveStats/Q/R=(Onset, Offset) DMF SetAxis LeftHz V_min,V_max End Function eFWindowEvent2(infoStr) String infoStr String event= StringByKey("EVENT",infoStr) String graphName String TraceList = TraceNameList("",";",1) if(stringmatch(event,"kill")) DoWindow/K $StringByKey("WINDOW",infoStr) NVAR AutoKill=root:eFish:AutoKill if(1) eFKillCurrentWaves2(TraceList) endif endif return 0 End Function eFKillCurrentWaves2(TraceList) String TraceList variable i, loc String currWave for(i=0; i RightSel) // Make sure Left is Left... temp = RightSel RightSel =LeftSel LeftSel = temp endif Duplicate/O/R=(LeftSel,RightSel) $NameOfSound, tempWave else // Duplicate whole wave for playing GetAxis/Q bottom Duplicate/O/R=(V_min,V_max) $NameOfSound, tempWave endif endif tempWave *= 32767/10 Redimension/W tempWave Playsound/A=1 tempWave Killwaves tempWave else // "Stop" Make/O/w/N=2 Dummy SetScale/P x 0,1/44100,"", Dummy PlaySound/A=2 Dummy KillWaves Dummy endif End //-------------------------------------------------------------------------- Function ChirpNumProc(ctrlName,popNum,popStr) : PopupMenuControl String ctrlName Variable popNum String popStr GetWindow $WinName(0,1) note String Name=S_Value SetDataFolder root: Wave DMF=$Name+".dmf" if(stringmatch(popStr, "all")) SetAxis/A bottom SetAxis/A LeftHz return 0 endif SVAR ChirpOnsetsStr=root:eFish:ChirpOnsetsStr, ChirpOffsetsStr=root:eFish:ChirpOffsetsStr NVAR CurrChirpNum=root:eFish:CurrChirpNum CurrChirpNum=popNum-1//; Print CurrChirpNum Variable Onset=str2num(stringfromlist(popNum-1, ChirpOnsetsStr)) Variable Offset=str2num(stringfromlist(popNum-2, ChirpOffsetsStr)) Onset=Onset-(max(Offset-Onset,0.1)) Offset=Offset+(max(Offset-Onset,0.1)) SetAxis bottom max(Onset, leftx(DMF)), min(Offset, pnt2x(DMF,numpnts(DMF)-1) ) WaveStats/Q/R=(Onset, Offset) DMF SetAxis LeftHz V_min,V_max End //-------------------------------------------------------------------------- Function efSaveSingleFloat32bit(W, DF, RMS) Wave W, DF, RMS Variable refNum, dx, lx // Get base name for output files String BaseName=NameOfWave(W), str Variable i for(i=strlen(BaseName)-1;i>strlen(BaseName)-6;i-=1) if(stringmatch(BaseName[i,i], ".")==1) BaseName[i,strlen(BaseName)]="" break endif endfor // Write EOD wave Open/P=SavePath refNum as BaseName+".eod" if(refNum==0) return 0 endif str=""; str=PadString(str, 1024, 0) FBinWrite/B=3 refNum, str FSetPos refNum, 0 str="Fish" FBinWrite/B=3 refNum, str dx=leftx(W) FBinWrite/B=3/F=4 refNum, lx dx=deltax(W) FBinWrite/B=3/F=4 refNum, dx FSetPos refNum, 1024 FBinWrite/B=3/F=4 refNum, W Close refNum // Write dominant frequency wave Open/P=SavePath refNum as BaseName+".dmf" if(refNum==0) return 0 endif str=""; str=PadString(str, 1024, 0) FBinWrite/B=3 refNum, str FSetPos refNum, 0 str="Fish" FBinWrite/B=3 refNum, str dx=leftx(DF) FBinWrite/B=3/F=4 refNum, lx dx=deltax(DF) FBinWrite/B=3/F=4 refNum, dx FSetPos refNum, 1024 FBinWrite/B=3/F=4 refNum, DF Close refNum // Write dominant frequency wave Open/P=SavePath refNum as BaseName+".rms" if(refNum==0) return 0 endif str=""; str=PadString(str, 1024, 0) FBinWrite/B=3 refNum, str FSetPos refNum, 0 str="Fish" FBinWrite/B=3 refNum, str dx=leftx(RMS) FBinWrite/B=3/F=4 refNum, lx dx=deltax(RMS) FBinWrite/B=3/F=4 refNum, dx FSetPos refNum, 1024 FBinWrite/B=3/F=4 refNum, RMS Close refNum End //-------------------------------------------------------------------- Function efLoadSingleFloatFiles() CheckeFishGlobals() Variable refNum Open/R/Z=0/M="Open an .eod, .dmf or .rms file"/T="????" refNum String FilePathName=S_Filename if(refNum==0) return 0 endif Close refNum String FileName = StringFromList(ItemsInList(FilePathName,":")-1, FilePathName, ":") String FilePath=RemoveFromList(FileName, FilePathName, ":") NewPath/O/Q LoadPath, FilePath PathInfo/S LoadPath String BaseName=FileName, SignalFilePath Variable i for(i=strlen(BaseName)-1;i>strlen(BaseName)-6;i-=1) if(stringmatch(BaseName[i,i], ".")==1) BaseName[i,strlen(BaseName)]="" break endif endfor String FileType=" " Variable dx=0, lx=0 String/G gfileName="" NVAR LoadEOD=root:eFish:LoadEOD if(LoadEOD) String/G gfileName=BaseName+".eod" Open/R/Z=0/P=LoadPath/M="Open .eod file"/T="????" refNum as gfileName SignalFilePath=S_Filename if(refNum==0) return 0 endif FileType=" " FBinRead/B=3 refNum, FileType FBinRead/B=3/F=4 refNum, lx FBinRead/B=3/F=4 refNum, dx Close refNum Execute "GBLoadWave/Q/B/T={2,2}/S=1024/W=1/P=LoadPath gfileName" SetScale/P x lx,dx,"sec", wave0 if(WaveExists($gfileName)) killWaves/Z $gfileName endif rename wave0, $gfileName SetScale d 0,0,"V", $BaseName+".eod" endif gfileName=BaseName+".dmf" Open/R/Z=0/P=LoadPath/M="Open .dmf file"/T="????" refNum as gfileName SignalFilePath=S_Filename if(refNum==0) return 0 endif FileType=" " FBinRead/B=3 refNum, FileType FBinRead/B=3/F=4 refNum, lx FBinRead/B=3/F=4 refNum, dx Close refNum Execute "GBLoadWave/Q/B/T={2,2}/S=1024/W=1/P=LoadPath gfileName" SetScale/P x lx,dx,"sec", wave0 if(WaveExists($gfileName)) DoWindow/K eFish killWaves/Z $gfileName endif rename wave0, $gfileName SetScale d 0,0,"Hz", $BaseName+".dmf" NVAR WindowDur=root:eFish:WindowDur, OutputInterval=root:eFish:OutputInterval NVAR OverlapPercent=root:eFish:OverlapPercent //OutputInterval=dx //OverlapPercent=(1-(OutputInterval/WindowDur))*100 gfileName=BaseName+".rms" Open/R/Z=0/P=LoadPath/M="Open .rms file"/T="????" refNum as gfileName SignalFilePath=S_Filename if(refNum==0) return 0 endif FileType=" " FBinRead/B=3 refNum, FileType FBinRead/B=3/F=4 refNum, lx FBinRead/B=3/F=4 refNum, dx Close refNum Execute "GBLoadWave/Q/B/T={2,2}/S=1024/W=1/P=LoadPath gfileName" SetScale/P x lx,dx,"sec", wave0 if(WaveExists($gfileName)) DoWindow/K eFish killWaves/Z $gfileName endif rename wave0, $gfileName SetScale d 0,0,"V", $BaseName+".rms" KillStrings/Z gfileName MakeEFGraph($BaseName+".eod", $BaseName+".dmf", $BaseName+".rms", BaseName, 1) End //------------------------------------------------------------ Window eFishSettings() : Panel CheckeFishGlobals() PauseUpdate; Silent 1 // building window... DoWindow eFishSettings // Remember window position if the window exists if(V_flag) GetWindow eFishSettings wsize DoWindow/K eFishSettings NewPanel /K=1 /W=(V_left,V_top,V_right,V_bottom) as "Settings" else DoWindow/K eFishSettings NewPanel/K=1/W=(462,80,690,670) as "Settings" endif DoWindow/C eFishSettings CheckBox loadWAVs pos={50,5},title="load WAV files (vs. ibw)", variable=root:eFish:LoadWAVFiles SetDrawEnv fsize= 10, fstyle= 4 DrawText 80,43,"Remove playback" DrawLine 12,25,210,25 CheckBox SubtractPlayback pos={27,50},size={100,14},title="List box selection", variable=root:eFish:ListBoxSelection DrawLine 12,70,210,70 CheckBox UseFFTPhase pos={27,80},size={120,14},title="Use FFT phase (vs. autocorrelation)", variable=root:eFish:UseFFTPhase DrawLine 12,100,210,100 SetVariable WindowDur,pos={22,108},size={180,15},bodyWidth=60, title="Window duration (sec.)" SetVariable WindowDur,limits={1e-05,1,0.001},proc=SetOverlap,value= root:eFish:WindowDur SetVariable OutputInterval,pos={28,127},size={175,15},bodyWidth=60, title="Output interval (sec.)" SetVariable OutputInterval,limits={1e-05,1,0.001},proc=SetOverlap,value= root:eFish:OutputInterval SetVariable OverLap,pos={61,148},size={140,15},bodyWidth=60, title="Overlap (%)" SetVariable OverLap,limits={0,99,5},proc=SetOutputInterval,value= root:eFish:OverlapPercent SetVariable Threshold,pos={50,169},size={150,15},bodyWidth=60, title="Threshold (dB)" SetVariable Threshold,limits={-99,99,3},value= root:eFish:dBthreshold SetDrawEnv fsize= 10 DrawText 55,207,"Autocorrelation Hz range" DrawLine 12,190,210,190 SetVariable HighHz,pos={36,215},size={168,15}, bodyWidth=60, title="High frequency (Hz)" SetVariable HighHz,limits={1,130000,500},value= root:eFish:HighHz SetVariable LowHz,pos={39,234},size={165,15},bodyWidth=60, title="Low frequency (Hz)" SetVariable LowHz,limits={1,130000,500},value= root:eFish:LowHz SetDrawEnv fsize= 10, fstyle= 4 DrawText 80,273,"Graph Settings" DrawLine 12,258,210,258 CheckBox LoadEOD pos={25,280},size={100,14},title="Load EOD", variable=root:eFish:LoadEOD SetVariable GraphWidth,pos={110,280},size={90,15},bodyWidth=60, title="Width" SetVariable GraphWidth,limits={1,50,1},value= root:eFish:GraphWidth SetVariable GraphHeight,pos={108,300},size={92,15},bodyWidth=60, title="Height" SetVariable GraphHeight,limits={1,50,1},value= root:eFish:GraphHeight SetDrawEnv fsize= 10, fstyle= 4 DrawText 80,345,"Chirp Counting" DrawLine 12,331,210,331 CheckBox Count pos={25,352},size={100,14},title="Count chirps automatically", variable=root:eFish:CountChirps SetVariable ChirpHzThresh,pos={40,375},size={162,15},bodyWidth=60,title="Chirp High thresh. (Hz)" SetVariable ChirpHzThresh,limits={0.5,50,0.5},value= root:eFish:ChirpHzThresh SetVariable ChirpHzLOWThresh,pos={40,394},size={162,15},bodyWidth=60,title="Chirp Low thresh. (Hz)" SetVariable ChirpHzLOWThresh,limits={0.5,50,0.5},value= root:eFish:ChirpHzLOWThresh SetVariable minChirpDur,pos={42,414},size={160,15},bodyWidth=60,title="Min. chirp dur. (ms)" SetVariable minChirpDur,limits={1,1000,1},value= root:eFish:minChirpDur SetVariable maxIntervalDur,pos={27,433},size={175,15},bodyWidth=60,title="Min. interval dur. (ms)" SetVariable maxIntervalDur,limits={1,10000,1},value= root:eFish:minIntervalDur SetVariable MaxChirpDur,pos={45,452},size={157,15},bodyWidth=60,title="Max. chirp dur. (S)" SetVariable MaxChirpDur,limits={1,1000,1},value= root:eFish:MaxChirpDur SetVariable MaxDurSkip,pos={62,471},size={140,15},bodyWidth=60,title="Advance time (S)" SetVariable MaxDurSkip,limits={0.01,10000,1},value= root:eFish:MaxDurSkip SetVariable BaselineDur,pos={47,490},size={155,15},bodyWidth=60,title="Baseline dur. (sec.)" SetVariable BaselineDur,limits={0.01,60,1},value= root:eFish:BaselineDur SetDrawEnv fsize= 10, fstyle= 4 DrawText 80,530,"Chirp Statistics" DrawLine 12,516,210, 516 CheckBox LogToNotebook pos={25,538},size={100,14},title="write stats automatically" , variable=root:eFish:LogToNotebook CheckBox SubtractHz pos={25,560},size={100,14},title="Stats thresh.", variable=root:eFish:AdjustHzThresh SetVariable NewStatsThresh,pos={35,560},size={157,15},bodyWidth=50,title="(Hz)" SetVariable NewStatsThresh,limits={0.1,1000,0.5},value= root:eFish:NewStatsThresh Button TestStatsThresh pos={200,557}, size={15,20},title="a", proc=TestStatsThreshProc EndMacro //------------------------------------------------------------ Function TestStatsThreshProc(ctrlName) : ButtonControl String ctrlName AdjustHzThreshForStats() End Function SetOverlap(ctrlName,varNum,varStr,varName) : SetVariableControl String ctrlName Variable varNum String varStr String varName NVAR WindowDur=root:eFish:WindowDur, OutputInterval=root:eFish:OutputInterval NVAR OverlapPercent=root:eFish:OverlapPercent OverlapPercent=(1-(OutputInterval/WindowDur))*100 End Function SetOutputInterval(ctrlName,varNum,varStr,varName) : SetVariableControl String ctrlName Variable varNum String varStr String varName NVAR WindowDur=root:eFish:WindowDur, OutputInterval=root:eFish:OutputInterval NVAR OverlapPercent=root:eFish:OverlapPercent OutputInterval=(1-(OverlapPercent/100))*WindowDur End //------------------------------------------------------------------------------- Function CountChirpsProc(ctrlName) : ButtonControl String ctrlName GetWindow $WinName(0,1) note String Name=S_Value SetDataFolder root: Wave DMF=$Name+".dmf" Wave PosStartTimes=root:eFish:PosStartTimes, PosEndTimes=root:eFish:PosEndTimes Wave ChirpBaselineHz=root:eFish:ChirpBaselineHz, ChirpBaselineHzNeg=root:eFish:ChirpBaselineHzNeg Wave BaseCrossPoints=root:eFish:BaseCrossPoints, ChirpBaselineHzPlus=root:eFish:ChirpBaselineHzPlus Wave NegStartTimes=root:eFish:NegStartTimes, NegEndTimes=root:eFish:NegEndTimes Wave BaselineHzPeakWidth=root:eFish:BaselineHzPeakWidth Redimension/N=9999 PosStartTimes, PosEndTimes, BaseCrossPoints, NegStartTimes, NegEndTimes, ChirpBaselineHz, ChirpBaselineHzPlus, ChirpBaselineHzNeg, BaselineHzPeakWidth PosStartTimes=0; PosEndTimes=0; BaseCrossPoints=0; ChirpBaselineHz=0; ChirpBaselineHzPlus=0; NegStartTimes=0; NegEndTimes=0 NVAR HzThresh=root:eFish:ChirpHzThresh, HzLOWThresh=root:eFish:ChirpHzLOWThresh, BaselineDur=root:eFish:BaselineDur NVAR minChirpDur=root:eFish:minChirpDur, minIntervalDur=root:eFish:minIntervalDur NVAR MaxChirpDur=root:eFish:MaxChirpDur, MaxDurSkip=root:eFish:MaxDurSkip Variable dx=deltax(DMF) Variable BaselineCalcPnts=round(BaselineDur/dx) Variable minChirpDurPnts=round((minChirpDur/1000)/dx) Variable minIntervalPnts=round((minIntervalDur/1000)/dx) Variable i, j, k, c=0, BaselineHz, GetBaseLine=0 Variable PosStartTime=0, PosEndTime=0, BaseCross=0, NegStartTime=0, NegEndTime=0 Variable TypeOne=0, TypeTwo=0, dxHz1, dxHz2, pnt Variable StartPnt=0, StopPnt=numpnts(DMF) GetMarquee bottom if(V_flag) StartPnt=x2pnt(DMF, V_left); StopPnt=x2pnt(DMF, V_right) endif BaselineHz=efMode(DMF, StartPnt-(BaselineCalcPnts/2), StartPnt+(BaselineCalcPnts/2)) for(i=StartPnt; i<=StopPnt; i+=1) // get running BaselineHz using mode as we go if(mod(i, BaselineCalcPnts)==0 || GetBaseLine) BaselineHz=efMode(DMF, i-(BaselineCalcPnts/2), i+(BaselineCalcPnts/2)) GetBaseLine=0 endif if(DMF[i]>=BaselineHz+HzThresh) //PosStartTime=(i-1)*dx // set chirp onset time without interpolation dxHz1=DMF[i-1]-(BaselineHz+HzThresh); dxHz2=(BaselineHz+HzThresh)-DMF[i] PosStartTime=((i-1)*dx)+(dx*(dxHz1/(dxHz2+dxHz1))) //interpolate for(j=i;j<=i+MaxChirpDur*(1/dx); j+=1) if(DMF[j]<=BaselineHz+HzThresh) // first drop towards baseline for(k=j;k<=j+minIntervalPnts; k+=1) if(DMF[k]>=BaselineHz+HzThresh ) // make sure the Hz drop is not due to noise j=k //break // don't break! else if(k==j+minIntervalPnts) TypeTwo=1 endif endif if(DMF[k]<=BaselineHz-HzLOWThresh ) // if the chip is Type Two TypeOne=1 TypeTwo=0 BaseCross=ZeroHzCross(DMF, k, BaselineHz) // fine X (time) of TypeOne Chirp midpoint PosEndTime=FindPosEnd(DMF, x2pnt(DMF, BaseCross)+1, BaselineHz+HzThresh) // fine X (time) of TypeOne Chirp end time NegStartTime=FindNegStart(DMF, x2pnt(DMF, BaseCross)-1, BaselineHz-HzLOWThresh) // fine X (time) of TypeOne Chirp end time j=TypeOneChipTermination(DMF, k, BaselineHz, HzLOWThresh) // fine termination of TypeOne Chirp break endif endfor if(TypeTwo) // if no additional increases/decreases above/below threshold found; j is the last point BaseCross=NaN TypeOne=0 break endif if(TypeOne) break endif endif endfor if((j-i) > minChirpDurPnts) pnt=x2pnt(DMF, j*dx) if(TypeTwo) //PosEndTime=(j)*dx // if type II without interpolation dxHz1=DMF[pnt-1]-(BaselineHz+HzThresh); dxHz2=(BaselineHz+HzThresh)-DMF[pnt] PosEndTime=((pnt)*dx)+(dx*(dxHz1/(dxHz2+dxHz1))) //interpolate else //NegEndTime=(j)*dx // if type I, PosEndTime already set without interpolation dxHz1=DMF[pnt-1]-(BaselineHz-HzLOWThresh); dxHz2=(BaselineHz-HzLOWThresh)-DMF[pnt] NegEndTime=((pnt-1)*dx)+(dx*(dxHz1/(dxHz2+dxHz1))) //interpolate endif //check to see if chirp exceeds maximum length - shouldn't occur due to checks above if(PosEndTime-PosStartTime > MaxChirpDur) i=x2pnt(DMF, PosStartTime+MaxDurSkip) GetBaseLine=1 else PosStartTimes[c]=PosStartTime PosEndTimes[c]=PosEndTime ChirpBaselineHz[c]=BaselineHz ChirpBaselineHzPlus[c]=BaselineHz+HzThresh ChirpBaselineHzNeg[c]=BaselineHz-HzLOWThresh NVAR TempPeakWidth=root:eFish:TempPeakWidth BaselineHzPeakWidth[c]=TempPeakWidth if(TypeOne) BaseCrossPoints[c]=BaseCross NegStartTimes[c]=NegStartTime NegEndTimes[c]=NegEndTime else BaseCrossPoints[c]=NaN NegStartTimes[c]=NaN NegEndTimes[c]=NaN endif c+=1 i=j+1 GetBaseLine=1 endif endif endif // positive threshold if statement endfor deletepoints c, 9999-c, PosStartTimes, PosEndTimes, BaseCrossPoints, NegStartTimes, NegEndTimes, ChirpBaselineHz, ChirpBaselineHzPlus, ChirpBaselineHzNeg, BaselineHzPeakWidth String TraceList=TraceNameList("",";",1) if(strsearch(TraceList, "ChirpBaselineHz", 0)>0) RemoveFromGraph ChirpBaselineHz endif if(strsearch(TraceList, "ChirpBaselineHzPlus", 0)>0) RemoveFromGraph ChirpBaselineHzPlus ,ChirpBaselineHzPlus#1 endif if(strsearch(TraceList, "ChirpBaselineHzNeg", 0)>0) RemoveFromGraph ChirpBaselineHzNeg ,ChirpBaselineHzNeg#1 endif AppendToGraph/L=LeftHz ChirpBaselineHz vs BaseCrossPoints;DelayUpdate ModifyGraph mode(ChirpBaselineHz)=3,marker(ChirpBaselineHz)=0;DelayUpdate ModifyGraph msize(ChirpBaselineHz)=5, rgb(ChirpBaselineHz)=(3,52428,1) AppendToGraph/L=LeftHz ChirpBaselineHzPlus vs PosStartTimes;DelayUpdate AppendToGraph/L=LeftHz ChirpBaselineHzPlus vs PosEndTimes;DelayUpdate ModifyGraph mode(ChirpBaselineHzPlus)=3,marker(ChirpBaselineHzPlus)=0, msize(ChirpBaselineHzPlus)=5;DelayUpdate ModifyGraph mode(ChirpBaselineHzPlus#1)=3,marker(ChirpBaselineHzPlus#1)=0, msize(ChirpBaselineHzPlus#1)=5;DelayUpdate AppendToGraph/L=LeftHz ChirpBaselineHzNeg vs NegStartTimes;DelayUpdate AppendToGraph/L=LeftHz ChirpBaselineHzNeg vs NegEndTimes;DelayUpdate ModifyGraph mode(ChirpBaselineHzNeg)=3,marker(ChirpBaselineHzNeg)=0, msize(ChirpBaselineHzNeg)=5, rgb(ChirpBaselineHzNeg)=(65535,43690,0);DelayUpdate ModifyGraph mode(ChirpBaselineHzNeg#1)=3,marker(ChirpBaselineHzNeg#1)=0, msize(ChirpBaselineHzNeg#1)=5, rgb(ChirpBaselineHzNeg#1)=(65535,43690,0);DelayUpdate SVAR NumChirpsFoundStr=root:eFish:NumChirpsFoundStr NumChirpsFoundStr=num2str(numpnts(ChirpBaselineHz)) SVAR ChirpOnsetsStr=root:eFish:ChirpOnsetsStr, ChirpOffsetsStr=root:eFish:ChirpOffsetsStr SVAR ChirpBaselineHzStr=root:eFish:ChirpBaselineHzStr ChirpOnsetsStr="all;"; ChirpOffsetsStr=""; ChirpBaselineHzStr="" for(i=0;iBaseLineCross-minIntervalPnts; i-=1) if(DMF[i] > UpperThresh) dxHz1=DMF[i]-(UpperThresh); dxHz2=(UpperThresh)-DMF[i+1] //return (i+1)*dx// without interpolation return ((i)*dx)+(dx*(dxHz1/(dxHz2+dxHz1))) //interpolate endif endfor Print "Couldn't find Positive End time..." return (BaseLineCross-1)*dx End Function FindNegStart(DMF, BaseLineCross, LowerThresh) Wave DMF Variable BaseLineCross, LowerThresh NVAR minIntervalDur=root:eFish:minIntervalDur Variable dx=deltax(DMF) Variable i, minIntervalPnts=round((minIntervalDur/1000)/dx), dxHz1, dxHz2 for(i=BaseLineCross; i(j-20);i-=1) if(DMF[i]>=BaselineHz) // find Hz above threshold j=i break endif endfor Variable dHz1=DMF[j]-BaselineHz, dHz2=BaselineHz-DMF[j+1] Variable dx=deltax(DMF) Return (j*dx)+(dx*(dHz1/(dHz2+dHz1))) End Function TypeOneChipTermination(DMF, ChirpMidPnt, BaselineHz, HzThresh) Wave DMF Variable ChirpMidPnt, BaselineHz, HzThresh //Printf "%g, %g, %g\r", ChirpMidPnt, BaselineHz, HzLOWThresh NVAR minChirpDur=root:eFish:minChirpDur, MaxChirpDur=root:eFish:MaxChirpDur NVAR minIntervalDur=root:eFish:minIntervalDur Variable dx=deltax(DMF) Variable minIntervalPnts=round((minIntervalDur/1000)/dx) Variable MaxChirpDurPnts=MaxChirpDur*(1/dx) Variable i, k, stop=0 for(i=ChirpMidPnt;i=BaselineHz-HzThresh) for(k=i;k<=i+minIntervalPnts; k+=1) if(DMF[k]=LeftPnt-maxAdjustment;k-=1) if(DMF[k] < ChirpBaselineHz[i]+NewStatsThresh) break endif endfor if(k<=LeftPnt-maxAdjustment) Print "Unable to adjust PosStart threshold for stats at "+num2str(PosStartTimes[i])+"!!" else //PosStartTimes[i]=pnt2x(DMF, k) // without interpolation dxHz1=DMF[k]-(ChirpBaselineHz[i]+NewStatsThresh); dxHz2=(ChirpBaselineHz[i]+NewStatsThresh)-DMF[k+1] PosStartTimes[i]=(k*dx)+(dx*(dxHz1/(dxHz2+dxHz1))) //interpolate endif // Adjust PosEnd times LeftPnt=x2pnt(DMF, PosEndTimes[i]) for(k=LeftPnt;k<=LeftPnt+maxAdjustment;k+=1) if(DMF[k] < ChirpBaselineHz[i]+NewStatsThresh) break endif endfor if(k>=LeftPnt+maxAdjustment) Print "Unable to adjust PosEnd threshold for stats at "+num2str(PosEndTimes[i])+"!!" else //PosEndTimes[i]=pnt2x(DMF, k) // without interpolation dxHz1=DMF[k-1]-(ChirpBaselineHz[i]+NewStatsThresh); dxHz2=(ChirpBaselineHz[i]+NewStatsThresh)-DMF[k] PosEndTimes[i]=((k-1)*dx)+(dx*(dxHz1/(dxHz2+dxHz1))) //interpolate endif // adjust negative times if new threshold is lower (actually higher in Hz) than the original negative threshold if( NewStatsThresh < HzLOWThresh && numtype(NegEndTimes[i]) != 2) // Adjust NegStart times LeftPnt=x2pnt(DMF, NegStartTimes[i]) for(k=LeftPnt;k>=LeftPnt-maxAdjustment;k-=1) if(DMF[k] >= ChirpBaselineHz[i]-NewStatsThresh) break endif endfor if(k>=LeftPnt+maxAdjustment) Print "Unable to adjust NegStart threshold for stats at "+num2str(PosEndTimes[i])+"!!" else //PosEndTimes[i]=pnt2x(DMF, k) // without interpolation dxHz1=DMF[k]-(ChirpBaselineHz[i]-NewStatsThresh); dxHz2=(ChirpBaselineHz[i]-NewStatsThresh)-DMF[k+1] NegStartTimes[i]=((k)*dx)+(dx*(dxHz1/(dxHz2+dxHz1))) //interpolate endif // Adjust NegEnd times LeftPnt=x2pnt(DMF, NegEndTimes[i]) for(k=LeftPnt;k<=LeftPnt+maxAdjustment;k+=1) if(DMF[k] >= ChirpBaselineHz[i]-NewStatsThresh) break endif endfor if(k>=LeftPnt+maxAdjustment) Print "Unable to adjust NegEnd threshold for stats at "+num2str(PosEndTimes[i])+"!!" else //PosEndTimes[i]=pnt2x(DMF, k) // without interpolation dxHz1=DMF[k-1]-(ChirpBaselineHz[i]-NewStatsThresh); dxHz2=(ChirpBaselineHz[i]-NewStatsThresh)-DMF[k] NegEndTimes[i]=((k-1)*dx)+(dx*(dxHz1/(dxHz2+dxHz1))) //interpolate endif ChirpBaselineHzNeg[i]=ChirpBaselineHz[i]-NewStatsThresh endif ChirpBaselineHzPlus[i]=ChirpBaselineHz[i]+NewStatsThresh endfor SVAR ChirpOnsetsStr=root:eFish:ChirpOnsetsStr, ChirpOffsetsStr=root:eFish:ChirpOffsetsStr SVAR ChirpBaselineHzStr=root:eFish:ChirpBaselineHzStr ChirpOnsetsStr="all;"; ChirpOffsetsStr=""; ChirpBaselineHzStr="" for(i=0;i=StartX && PosStartTimes[i]<=StopX) DataLogString1="", DataLogString2="", DataLogString3="", DataLogString4="", DataLogString5="" // Write times to strings WaveStats/Q/R=(PosStartTimes[i], PosEndTimes[i]) DMF Variable PosPeakHz=V_max Duplicate/O/R=(V_maxloc-(deltax(DMF)*2), V_maxloc+(deltax(DMF)*2)) DMF, TempW Execute "Interpolate/T=3/N=100/F=1/Y=TempW_SS TempW" WaveStats/Q TempW_SS SPrintf DataLogString1, "%.4f\t%.4f\t%.4f\t", PosStartTimes[i], V_maxloc, PosEndTimes[i] if(numtype(BaseCrossPoints[i])!=2) WaveStats/Q/R=(NegStartTimes[i], NegEndTimes[i]) DMF Variable NegPeakHz=V_min Duplicate/O/R=(V_minloc-(deltax(DMF)*2), V_minloc+(deltax(DMF)*2)) DMF, TempW Execute "Interpolate/T=3/N=100/F=1/Y=TempW_SS TempW" WaveStats/Q TempW_SS SPrintf DataLogString2, "%.4f\t%.4f\t%.4f\t%.4f\t", BaseCrossPoints[i], NegStartTimes[i], V_minloc, NegEndTimes[i] else SPrintf DataLogString2, "\t\t\t\t" endif // Write frequency measurements if(numtype(BaseCrossPoints[i])!=2) SPrintf DataLogString3, "%.1f\t%.1f\t%.1f\t%.1f\t%.1f\t", ChirpBaselineHz[i], PosPeakHz-ChirpBaselineHz[i], ChirpBaselineHz[i]-NegPeakHz, PosPeakHz, NegPeakHz else SPrintf DataLogString3, "%.1f\t%.1f\t\t%.1f\t\t", ChirpBaselineHz[i], PosPeakHz-ChirpBaselineHz[i], PosPeakHz endif // Write EOD amplitudes WaveStats/Q/R=(PosStartTimes[i], NegEndTimes[i]) EOD Variable OverallRMS=V_rms WaveStats/Q/R=(PosStartTimes[i], PosEndTimes[i]) EOD Variable PosRMS=V_rms if(numtype(BaseCrossPoints[i])!=2) WaveStats/Q/R=(NegStartTimes[i], NegEndTimes[i]) EOD Variable NegRMS=V_rms SPrintf DataLogString4, "%.4f\t%.4f\t%.4f\t", OverallRMS, PosRMS, NegRMS else SPrintf DataLogString4, "%.4f\t%.4f\t\t", OverallRMS, PosRMS endif // Write stats variables Wave BaselineHzPeakWidth=root:eFish:BaselineHzPeakWidth SPrintf DataLogString5, "%g\t%g\t%g\t%g\t%g\t%g\t%g\t%g\t%g\t%s\r", deltax(DMF)*1000, ChirpHzThresh, HzLOWThresh, NewStatsThresh, minChirpDur, minIntervalDur, MaxChirpDur, BaselineDur, BaselineHzPeakWidth[i], Name Notebook $nbNum text=DataLogString1+DataLogString2+DataLogString3+DataLogString4+DataLogString5 endif endfor KillWaves TempW_SS, TempW End //------------------------------------------------------------ Function efMakeNewDataLog() // Create an eFish data log String nbNum = "eFishNotebook" NewNotebook/N=$nbNum/F=0/V=1/K=0/W=(5,40,505,335) as "eFish chirp data Log" Notebook $nbNum defaultTab=72, statusWidth=238, pageMargins={72,72,72,72} Notebook $nbNum font="Geneva", fSize=10, fStyle=0, textRGB=(0,0,0) Notebook $nbNum text="eFish chirp data log created on " + date() + " at " + time() + "\r" Notebook $nbNum text="PosStartTime\tPosPeakTime\tPosEndTime\tBaseCross\tNegStartTime\tNegPeakTime\tNegEndTime\t" Notebook $nbNum text="BaselineHz\tPosPeakDev\tNegPeakDev\tPosABSHz\tNegABSHz\t" Notebook $nbNum text="OverallRMS\tPosRMS\tNegRMS\t" Notebook $nbNum text="DeltaHz\tPosThresh\tNegThresh\tStatsThresh\tMinDur\tMinICI\tMaxDur\tBaslineDur\tNoiseLevel\tFileName\r" End //------------------------------------------------------------ Function MarqueeFishStats() : GraphMarquee GetWindow $WinName(0,1) note String Name=S_Value SetDataFolder root: Wave DMF=$Name+".dmf" Wave EOD=$Name+".eod" Wave RMS=$Name+".rms" Variable StartX=leftx(DMF), StopX=pnt2x(DMF,numpnts(DMF)-1) GetMarquee bottom if(V_flag) StartX=V_left; StopX=V_right endif Variable StartPnt=x2pnt(DMF, StartX+1), StopPnt=x2pnt(DMF, StopX+1) //Stats Variables NVAR minChirpDur=root:eFish:minChirpDur, minIntervalDur=root:eFish:minIntervalDur NVAR MaxChirpDur=root:eFish:MaxChirpDur, MaxDurSkip=root:eFish:MaxDurSkip NVAR HzLOWThresh=root:eFish:ChirpHzLOWThresh, NewStatsThresh=root:eFish:NewStatsThresh NVAR BaselineDur=root:eFish:BaselineDur Wave PosEndTimes=root:eFish:PosEndTimes, PosStartTimes=root:eFish:PosStartTimes, ChirpBaselineHz=root:eFish:ChirpBaselineHz Wave BaseCrossPoints=root:eFish:BaseCrossPoints, ChirpBaselineHzPlus=root:eFish:ChirpBaselineHzPlus NVAR ChirpHzThresh=root:eFish:ChirpHzThresh, AdjustHzThresh=root:eFish:AdjustHzThresh Variable BaselineHz=efMode(DMF, max(0,x2pnt(DMF, StartX-BaselineDur)), min(x2pnt(DMF, StopX+BaselineDur), numpnts(DMF)) ) // Get baseline Hz from marquee selection NVAR TempPeakWidth=root:eFish:TempPeakWidth Variable PosStatsThresh if(AdjustHzThresh) PosStatsThresh=NewStatsThresh else PosStatsThresh=ChirpHzThresh endif Variable i String NoteBookWinList = WinList("*", ";","WIN:16") if(strsearch(NoteBookWinList, "eFishNotebook", 0)<0) DoAlert 1, "Create a new eFish Data Log?" if(V_flag == 1) efMakeNewDataLog() // Make a new notebook else return 0 endif endif String nbNum = "eFishNotebook" Variable PosOrNeg Prompt PosOrNeg,"Pick a statistic to calculate?",popup,"Positive;Negative;Time Threshold Crossed; Overall EOD amplitude" DoPrompt "Marquee stats options", PosOrNeg // 1 is pos, 2 is neg, 3 is ThreshX String DataLogString1="", DataLogString2="", DataLogString3="", DataLogString4="", DataLogString5="" // Write Threshold crossing time if(PosOrNeg==3) Variable BaseCrossTime=ZeroHzCross(DMF, x2pnt(DMF, StopX), BaselineHz) SPrintf DataLogString1, "\t\t\t%.4f\t", BaseCrossTime SPrintf DataLogString2, "\t\t\t\t" SPrintf DataLogString3, "\t\t\t\t\t" SPrintf DataLogString4, "\t\t\t" SPrintf DataLogString5, "%g\t%g\t%g\t%g\t%g\t%g\t%g\t%g\t%g\t%s\r", deltax(DMF)*1000, PosStatsThresh, AdjustHzThresh, HzLOWThresh, minChirpDur, minIntervalDur, MaxChirpDur, BaselineDur, TempPeakWidth, Name Notebook $nbNum text=DataLogString1+DataLogString2+DataLogString3+DataLogString4+DataLogString5 return 0 endif // Write Threshold crossing time if(PosOrNeg==4) WaveStats/Q/R=(StartX, StopX) EOD SPrintf DataLogString1, "\t\t\t\t" SPrintf DataLogString2, "\t\t\t\t" SPrintf DataLogString3, "\t\t\t\t" SPrintf DataLogString4, "%.4f\t\t\t", V_rms SPrintf DataLogString5, "%g\t%g\t%g\t%g\t%g\t%g\t%g\t%g\t%g\t%s\r", deltax(DMF)*1000, PosStatsThresh, AdjustHzThresh, HzLOWThresh, minChirpDur, minIntervalDur, MaxChirpDur, BaselineDur, TempPeakWidth, Name Notebook $nbNum text=DataLogString1+DataLogString2+DataLogString3+DataLogString4+DataLogString5 return 0 endif // Write Overall RMS EOD amplitue if(PosOrNeg==1) WaveStats/Q/R=(StartX,StopX) DMF Variable PosPeakHz=V_max Duplicate/O/R=(V_maxloc-(deltax(DMF)*2), V_maxloc+(deltax(DMF)*2)) DMF, TempW Execute "Interpolate/T=3/N=100/F=1/Y=TempW_SS TempW" WaveStats/Q TempW_SS SPrintf DataLogString1, "%.4f\t%.4f\t%.4f\t", StartX, V_maxloc, StopX else SPrintf DataLogString1, "\t\t\t\t" endif if(PosOrNeg==2) WaveStats/Q/R=(StartX,StopX) DMF Variable NegPeakHz=V_min Duplicate/O/R=(V_minloc-(deltax(DMF)*2), V_minloc+(deltax(DMF)*2)) DMF, TempW Execute "Interpolate/T=3/N=100/F=1/Y=TempW_SS TempW" WaveStats/Q TempW_SS SPrintf DataLogString2, "%.4f\t%.4f\t%.4f\t", StartX, V_minloc, StopX else SPrintf DataLogString2, "\t\t\t\t" endif // Write frequency measurements if(PosOrNeg==1) SPrintf DataLogString3, "%.1f\t%.1f\t\t%.1f\t\t", BaselineHz, PosPeakHz-BaselineHz, PosPeakHz else SPrintf DataLogString3, "%.1f\t\t%.1f\t\t%.1f\t", BaselineHz, BaselineHz-NegPeakHz, NegPeakHz endif // Write EOD amplitudes WaveStats/Q/R=(StartX, StopX) EOD Variable OverallRMS=V_rms if(PosOrNeg==1) SPrintf DataLogString4, "\t%.4f\t\t", OverallRMS else SPrintf DataLogString4, "\t\t%.4f\t", OverallRMS endif // Write stats variables Wave BaselineHzPeakWidth=root:eFish:BaselineHzPeakWidth SPrintf DataLogString5, "%g\t%g\t%g\t%g\t%g\t%g\t%g\t%g\t%g\t%s\r", deltax(DMF)*1000, ChirpHzThresh, HzLOWThresh, NewStatsThresh, minChirpDur, minIntervalDur, MaxChirpDur, BaselineDur, TempPeakWidth, Name Notebook $nbNum text=DataLogString1+DataLogString2+DataLogString3+DataLogString4+DataLogString5 KillWaves TempW_SS, TempW End //------------------------------------------------------------ Function efSaveWaves() GetWindow $WinName(0,1) note String Name=S_Value // Get base name for output files String BaseName=Name Variable i for(i=strlen(BaseName)-1;i>strlen(BaseName)-6;i-=1) if(stringmatch(BaseName[i,i], ".")==1) BaseName[i,strlen(BaseName)]="" break endif endfor SetDataFolder root: Wave DMF=$BaseName+".dmf" Wave EOD=$BaseName+".eod" Wave RMS=$BaseName+".rms" NewPath/O/Q/M="Choose the folder where .eod, .dmf and .rms files will be saved." SavePath PathInfo/S SavePath efSaveSingleFloat32bit(EOD, DMF, RMS) End //------------------------------------------------------------ Function RemovePlayback() : GraphMarquee RemovePlaybackProc("") End //------------------------------------------------------------ Function SaveEODProc(ctrlName) : ButtonControl String ctrlName SetDataFolder root: GetWindow $WinName(0,1) note String Name=S_Value PathInfo/S SavePath String FilePath=S_path Wave W=$Name NVAR LoadWAVFiles=root:eFish:LoadWAVFiles String SaveName if(LoadWAVFiles) W*=32767 // Save EOD file if(stringmatch(Name[strlen(Name)-4], ".")) SaveName=Name SaveName[strlen(Name)-4]="pbr" Print SaveName endif string Platform = IgorInfo(2) // use when platform related issues arise if(stringmatch(Platform, "Macintosh")) Execute "SndSaveAIFF/B=16/I/Q "+PossiblyQuoteName(Name) +" as \""+FilePath+SaveName+"\"" else Execute "SaveWAVfile/B=16/O/I/Q "+PossiblyQuoteName(Name) + " as \""+FilePath+SaveName+"\"" endif else if(stringmatch(Name[strlen(Name)-4], ".")) SaveName=Name SaveName[strlen(Name)-4]="pbr" else SaveName=Name+"pbr.ibw" endif Duplicate/O $Name, Wave0 Save/C Wave0 as SaveName Killwaves/Z root:Wave0 endif End //--------------------------------------------------------- Function RemovePlaybackProc(ctrlName) : ButtonControl String ctrlName SetDataFolder root: GetWindow $WinName(0,1) note String Name=S_Value Wave Wav=$Name Wave PBWav=PBWav Variable leftXWavPnt, rightXWavPnt, EvenPoints Variable leftWavPnt, rightWavPnt // Get wave selection GetMarquee bottom if(V_Flag) Duplicate/O/R=(V_left, V_right) Wav, WavFFT else DoAlert 1, "You should really use a marquee selection over a few seconds where there are no chirps! Continue with the whole wave?" if(V_flag==2) return 0 endif Duplicate/O Wav, WavFFT endif leftXWavPnt=V_left; rightXWavPnt=V_right leftWavPnt=x2pnt(Wav, leftXWavPnt); rightWavPnt=x2pnt(Wav, rightXWavPnt) EvenPoints=2* floor(numpnts(WavFFT)/2) if(numpnts(WavFFT)!=EvenPoints) redimension/n = (EvenPoints) WavFFT // "Odd # of points in waveform. Last point deleted" endif FFT WavFFT Redimension/R WavFFT WavFFT = sqrt(magsqr(WavFFT)) WavFFT/=numpnts(WavFFT) WavFFT = 20*log(WavFFT/0.00002) WaveStats/Q WavFFT Variable PntsInAvgCycle=ceil( (1/DeltaX(Wav))/V_maxloc)+5 // Prompt for initial setting (can speed up search for optimum scaling factor) Variable InitialScaling=1, InitialPntShift=0 Prompt InitialScaling,"initial scaling factor (decimal)" Prompt InitialPntShift,"Points to shift playback wave (approx. phase)" DoPrompt "Initial playback wave scaling/shifting", InitialScaling, InitialPntShift // Get initial waves for setting initial conditions Duplicate/O/R=[leftWavPnt, rightWavPnt] Wav, WavTemp, AdjustedWav Duplicate/O/R=[leftWavPnt, rightWavPnt] PBWav, PBWavTemp // adjust initial values entered at user prompts PBWavTemp*=InitialScaling if(InitialPntShift>0) insertpoints 0, InitialPntShift, PBWavTemp else if(InitialPntShift<0) deletepoints 0, -InitialPntShift, PBWavTemp endif endif WaveStats/Q/R=[leftWavPnt, rightWavPnt] Wav Variable LastPP=V_max-V_min, MaxY=V_max, MinY=V_max-V_sdev Variable MaxIterations=5000, ScaleDirection=1, NumFlipFlops=0 Variable/G root:eFish:Scale=1.01, root:eFish:PhasePnt, root:eFish:Output NVAR Scale=root:eFish:Scale, PhasePnt=root:eFish:PhasePnt, Output=root:eFish:Output // determine if scaling needs to be adjusted up or down WaveStats/Q AdjustedWav LastPP=(V_max-V_min) Variable/g root:eFish:OriginalExcursion=LastPP PBWavTemp*=Scale AdjustedWav=WavTemp-PBWavTemp WaveStats/Q AdjustedWav if((V_max-V_min) < LastPP) //if scaling up reduces excursion ScaleDirection=1 else ScaleDirection=0 endif // Make display -- primarily for aesthetic reasons. DoWindow/K Calculating Display/K=1/W=(20,20,420,270) WavTemp as "Calculating" DoWindow/C Calculating AppendToGraph AdjustedWav ModifyGraph rgb(WavTemp)=(1,9611,39321),rgb(AdjustedWav)=(0,39321,0) SetAxis left, MinY, MaxY ControlBar 25 ValDisplay Scale win=Calculating, pos={10,5}, size={80,20},title=" Scale:",value=#root:eFish:Scale ValDisplay Phase win=Calculating, pos={100,5}, size={80,20},title="\"Phase\":",value=#root:eFish:PhasePnt ValDisplay Output win=Calculating, pos={200,5}, size={90,20},title="Result:", value=#root:eFish:Output ValDisplay Original win=Calculating, pos={300,5}, size={50,20},title="", value=#root:eFish:OriginalExcursion // Look for optimum number of points to skip (only way I know how to adjust "phase" without interpolating) Variable BestPnt=0, LowestExc=65536 for(PhasePnt=-PntsInAvgCycle;PhasePnt<=PntsInAvgCycle;PhasePnt+=1) // get new waves from Wav Duplicate/O/R=[leftWavPnt, rightWavPnt] Wav, WavTemp, AdjustedWav Duplicate/O/R=[leftWavPnt, rightWavPnt] PBWav, PBWavTemp // adjust initial values entered at user prompts PBWavTemp*=InitialScaling if(InitialPntShift>0) insertpoints 0, InitialPntShift, PBWavTemp else if(InitialPntShift<0) deletepoints 0, -InitialPntShift, PBWavTemp endif endif if(PhasePnt>0) insertpoints 0, PhasePnt, PBWavTemp else if(PhasePnt<0) deletepoints 0, -PhasePnt, PBWavTemp endif endif AdjustedWav=WavTemp-PBWavTemp WaveStats/Q AdjustedWav Output=(V_max-V_min) If( (V_max-V_min) < LowestExc) //if this excursion is smaller than any previous excursion LowestExc=(V_max-V_min) BestPnt=PhasePnt endif doupdate endfor PhasePnt=BestPnt // set PhasePnt (#in display) to the best point at end of loop // Update Wave with "phase shift" determined above -- this may also be for aesthetic purposes? Duplicate/O/R=[leftWavPnt, rightWavPnt] Wav, WavTemp, AdjustedWav Duplicate/O/R=[leftWavPnt, rightWavPnt] PBWav, PBWavTemp // adjust initial values entered at user prompts PBWavTemp*=InitialScaling if(InitialPntShift>0) insertpoints 0, InitialPntShift, PBWavTemp else if(InitialPntShift<0) deletepoints 0, -InitialPntShift, PBWavTemp endif endif if(BestPnt>0) insertpoints 0, BestPnt, PBWavTemp else if(BestPnt<0) deletepoints 0, -BestPnt, PBWavTemp endif endif // Look for optimum scaling factor Variable scaleAdj, i for(scaleAdj=0.01;scaleAdj>0.00001;scaleAdj/=10) if(ScaleDirection) Scale-=scaleAdj*10 else Scale+=scaleAdj*10 endif for(i=0;i<=MaxIterations;i+=1) // get new waves from Wav Duplicate/O/R=[leftWavPnt, rightWavPnt] Wav, WavTemp, AdjustedWav Duplicate/O/R=[leftWavPnt, rightWavPnt] PBWav, PBWavTemp PBWavTemp*=InitialScaling // initial scaling if(InitialPntShift>0) // initial phase shift insertpoints 0, InitialPntShift, PBWavTemp else if(InitialPntShift<0) deletepoints 0, -InitialPntShift, PBWavTemp endif endif if(BestPnt>0) // phase shift determined above insertpoints 0, BestPnt, PBWavTemp else if(BestPnt<0) deletepoints 0, -BestPnt, PBWavTemp endif endif // update on this iteration PBWavTemp*=Scale AdjustedWav=WavTemp-PBWavTemp WaveStats/Q AdjustedWav Output=(V_max-V_min) doupdate if((V_max-V_min) > LastPP) //if current scaling factor increases excursion NumFlipFlops+=1 if(ScaleDirection) ScaleDirection=0 else ScaleDirection=1 endif if(NumFlipFlops>=10) // shift slightly if(ScaleDirection) Scale-=scaleAdj/10 else Scale+=scaleAdj/10 endif //Print ">10 flip-flops" LastPP=(V_max-V_min) break // break if scaling direction flip-flops 10 times without breaking otherwise endif endif if(ScaleDirection) Scale+=scaleAdj//0.001 else Scale-=scaleAdj//0.001 endif if(abs(LastPP-(V_max-V_min))<=1 && scaleAdj==0.00001) // if close enough to last excursion +- 1 point LastPP=(V_max-V_min) break else LastPP=(V_max-V_min) endif endfor endfor doupdate // Ask if user want to apply values to the original playback wave? String AlertMessage SPrintF AlertMessage, "Apply %g scaling and shift by %g points?", Scale, BestPnt DoAlert 1, AlertMessage if(V_flag==1) // adjust initial values entered at user prompts PBWav*=InitialScaling PBWav*=Scale // adjust initial values entered at user prompts if(InitialPntShift>0) // initial phase shift insertpoints 0, InitialPntShift, PBWavTemp else if(InitialPntShift<0) deletepoints 0, -InitialPntShift, PBWavTemp endif endif if(BestPnt>0) insertpoints 0, BestPnt, PBWav else if(BestPnt<0) deletepoints 0, -BestPnt, PBWav endif endif Wav=Wav-PBWav endif DoWindow/K Calculating Killwaves root:AdjustedWav,root:WavTemp,root:PBWavTemp, root:WavFFT KillVariables root:eFish:Scale, root:eFish:PhasePnt End //------------------------------------------------------------ Function efPlotwPlayBack(ctrlName) : ButtonControl String ctrlName SVAR CurrFileName=root:eFish:CurrFileName String FileName=CurrFileName PathInfo/S LoadPAth String FilePath=S_path SVAR CurrFileName=root:eFish:CurrFileName NVAR LoadWAVFiles=root:eFish:LoadWAVFiles DoWindow/K EODPB if(LoadWAVFiles) // load file string Platform = IgorInfo(2) // use when platform related issues arise if(stringmatch(Platform, "Macintosh")) String/G gNameStr=FilePath+CurrFileName Print gNameStr Execute "SndLoadWave/O/Q WAVLoaded0, gNameStr" KillStrings gNameStr SVAR S_fileName=root:S_fileName if(strlen(S_fileName)<1) Killwaves WAVLoaded0 return 0 endif else String/G gNameStr=FilePath+CurrFileName Execute "LoadWAVfile/Q/C/F=0/A=WAVLoaded gNameStr" KillStrings gNameStr SVAR S_fileName=root:S_fileName if(strlen(S_fileName)<1) Killwaves WAVLoaded0 return 0 endif endif Wave WAVLoaded0=WAVLoaded0 if(wavetype(WAVLoaded0) %& 0x01) // if file is stereo (loaded as a complex wave) Make/W/O/N=(numpnts(WAVLoaded0)) PBWav copyscales WAVLoaded0, PBWav PBWav=imag(WAVLoaded0) Redimension/R PBWav Redimension/D PBWav // redimension wave to double precision floating point PBWav/=32767 SetScale d 0,0,"V", PBWav Redimension/R WAVLoaded0 // convert complex wave to real (i.e., remove imaginary part - remove right channel) else DoAlert 0, "File does not appear to be stereo (does not include a playback wave in the right channel)" return 0 endif Redimension/D WAVLoaded0 // redimension wave to double precision floating point SetScale d 0,0,"V", WAVLoaded0 SetScale/P x leftx(WAVLoaded0)*deltax(WAVLoaded0),deltax(WAVLoaded0),"sec", WAVLoaded0 WAVLoaded0/=32767 if(WaveExists($FileName)) killWaves/Z $FileName endif Rename WAVLoaded0, $FileName else FileName=efLoad_ibw_file(FilePath+CurrFileName) String StimWaveName=FileName[0,strlen(FileName)-4]+"stim" KillWaves/Z PBWav Rename $StimWaveName, PBWav endif DoWindow/K EODPB Display/K=1/W=(5,44,495,274) $FileName as FileName//"EOD and playback waves" DoWindow/C EODPB SetWindow EODPB note=FileName ;delayupdate SetWindow EODPB, hook=eFWindowEvent2;DelayUpdate AppendToGraph/L=leftPB PBWav ModifyGraph margin(left)=54 ModifyGraph rgb=(1,3,39321) ModifyGraph lblPos(left)=50,lblPos(leftPB)=50 ModifyGraph freePos(leftPB)={0,bottom} ModifyGraph axisEnab(left)={0.52,1} ModifyGraph axisEnab(leftPB)={0,0.48} ModifyGraph lowTrip(leftPB)=1 Label left "EOD (\\U)" Label bottom "time (\\U)" Label leftPB "playback (\\U)" ControlBar 30 Button Calculate pos={150,5},size={80,20},title="Calculate", proc=RemovePlaybackProc Button SaveEOD pos={250,5},size={80,20},title="Save EOD", proc=SaveEODProc return 0 End //------------------------------------------------------------ Function efGetFilesForSubtraction() CheckeFishGlobals() String ListOfFiles NVAR LoadWAVFiles=root:eFish:LoadWAVFiles Variable refNum String FilePathName, FileName, FilePath if(LoadWAVFiles) Open/D/R/M="Select one file to get list of files"/T=".wav" refNum if(strlen(S_fileName)<=0) return 0 endif FilePathName=S_fileName FileName = StringFromList(ItemsInList(FilePathName,":")-1, FilePathName, ":") FilePath=RemoveFromList(FileName, FilePathName, ":") NewPath/C/O/Q LoadPath, FilePath NewPath/C/O/Q SavePath, FilePath ListOfFiles = IndexedFile(LoadPath,-1,".wav") else Open/D/R/M="Select one file to get list of files"/T=".ibw" refNum if(strlen(S_fileName)<=0) return 0 endif FilePathName=S_fileName FileName = StringFromList(ItemsInList(FilePathName,":")-1, FilePathName, ":") FilePath=RemoveFromList(FileName, FilePathName, ":") NewPath/C/O/Q LoadPath, FilePath NewPath/C/O/Q SavePath, FilePath ListOfFiles = IndexedFile(LoadPath,-1,".ibw") Variable i String CurrStr for(i=0;i PeakHz1+HzResolution || FREQ < PeakHz1-HzResolution ) if(dPhase<0) dPhase+=pi else dPhase-=pi endif else break endif FREQ=(dPhase/(2*pi))*(1/deltax(W)) endfor FREQ=(dPhase/(2*pi))*(1/deltax(W)) if(FREQ > PeakHz1+HzResolution || FREQ < PeakHz1-HzResolution ) //Printf "dPhase=%g, FREQ=%g\r", dPhase, FREQ Return NaN endif Return FREQ End Function/C efFFTPeakPhase(wStr) String wStr Wave W=$wStr Duplicate/O W, W2 Hanning W2 // variable OddPoints=mod(numpnts(W2),2) // if(OddPoints) // Redimension/N=(numpnts(W2)+1) W2 // W2[numpnts(W2)-1]=0 // endif fft W2 Wave/C Wc=W2 Wc= r2polar(Wc) Duplicate/O Wc, Wp Redimension/R Wp Wp= imag(W2) Wp[0]= Wp[1] // try to avoid glitch at dc UnWrap 2*Pi,Wp Redimension/R W2 WaveStats/Q W2 Variable PeakHz= V_maxloc Variable PhaseAtPeak=Wp[x2pnt(W2, V_maxloc)] KillWaves W2, Wp Variable/C CmplxPeakAndPhase=cmplx(PeakHz,PhaseAtPeak) Return CmplxPeakAndPhase End