├── FitFuncs.ipf ├── Graphing.ipf ├── DataImport.ipf ├── ISRSMacros.ipf ├── Utilities.ipf ├── SignalProcessing.ipf ├── LICENSE ├── README.md ├── AddGroundPanel.ipf ├── DispLineShapes.ipf ├── Baseline.ipf ├── FitTimeSeriesDisp.ipf ├── PerkinElmerImport.ipf └── FitTimeSeries.ipf /FitFuncs.ipf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david-hoffman/Igor-Data-Analysis/HEAD/FitFuncs.ipf -------------------------------------------------------------------------------- /Graphing.ipf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david-hoffman/Igor-Data-Analysis/HEAD/Graphing.ipf -------------------------------------------------------------------------------- /DataImport.ipf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david-hoffman/Igor-Data-Analysis/HEAD/DataImport.ipf -------------------------------------------------------------------------------- /ISRSMacros.ipf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david-hoffman/Igor-Data-Analysis/HEAD/ISRSMacros.ipf -------------------------------------------------------------------------------- /Utilities.ipf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david-hoffman/Igor-Data-Analysis/HEAD/Utilities.ipf -------------------------------------------------------------------------------- /SignalProcessing.ipf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/david-hoffman/Igor-Data-Analysis/HEAD/SignalProcessing.ipf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 David Hoffman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Igor Data Analysis 2 | 3 | I have written these [IGOR Pro](http://www.wavemetrics.com/index.html) procedures to analyze data collected during my doctoral research, as such the user interface is primarily through the command window and the documentation is incomplete. Nevertheless, I have found these procedures to be extraordinarily useful and I'd like to share them with the rest of the research community. 4 | 5 | My research primarily uses ultrafast lasers to perform two main types of vibrational spectroscopy 1.) Femtosecond Stimulated Raman Spectroscopy, FSRS, 2.) and Impulsive Stimulated Raman Spectroscopy, ISRS (in preparation). The relevant publications can be found [here](http://scholar.google.com/citations?user=HGG__poAAAAJ&hl=en). 6 | 7 | ## Utilities.ipf 8 | A set of utilities for the processing of time resolved vibrational spectroscopic data. 9 | 10 | Some notes on specific functions 11 | 12 | - `GroundSubtract2(timepoints,ground,startpt,endpt,type,[q])`: This function loops through all the time delays subtracting the ground state spectrum from the raw excited state spectra by scaling the excited state spectra to the ground state using a solvent feature. In this way fluctuations in Raman pump power are controlled for using the solvent as an internal standard. This is particularly important when the Raman pump is near a transient absorption feature as this algorithm will correct for any transient attenuation of the Raman pump. 13 | 14 | - `SolventSubtract2(spectrum, solvent, sp, ep, type)`: This function scales the `solvent` _to_ the `spectrum`. This is necessary to be internally consistend with `GroundSubtract2`. 15 | 16 | ## DataImport.ipf 17 | Functions designed to import data from a FSRS instrument. 18 | 19 | This procedure file also adds items to the "Load Waves" menu: 20 | 21 | Menu "Load Waves" 22 | "-" 23 | "FSRS Data ... /1", LoadFSRSData() 24 | "Raw FSRS Data ... /S 1", LoadRawFSRSData() 25 | "-" 26 | "DAQ Scan Data ... /2", LoadDAQData() 27 | "Detector XC Data ... /S 2", LoadDetXCData() 28 | "-" 29 | "CW Data ... ", LoadCWData() 30 | "UV-vis Data ...", LoadUVvisData() 31 | End 32 | 33 | The `LoadUVVisData()` procedure can be used to load any file in the JCAMP (.DX) format. 34 | 35 | `LoadCWData()` can be used to load any generic txt file. 36 | 37 | The LabVIEW programs which generate the FSRS data are available [here](https://www.github.com/david-hoffman/FSRS-LabVIEW). 38 | 39 | ## FitFuncs.ipf 40 | A set of useful fitting functions for the ultrafast spectroscopist. Many different kinds of exponentials convoluted with a gaussian instrument response. 41 | 42 | _**NOTE:** the width of the gaussian IRF is defined to be the FWHM/(2*sqrt(ln(2))) which is consistent with Igor's built-in Gaussian function within the CurveFit dialog meaning that this parameter can be used directly._ 43 | 44 | ## FitTimeSeries.ipf 45 | Contains `FitTimeSeries` a versatile batch fitting function. Fits a sum of either Gaussians or Lorentzians to a subset of time resolved FSRS spectra. `Coefs` is a wave of coefficiencts for the peaks, i.e. `Coefs={0,Amp1,Center1,FWHM1,...,Ampn,Centern,FWHMn}`. The `0` is necessary for backwards compatibility. `Wavenumber` is a *text* wave containing the peak labels, this will determine the names of the output waves. I usually just use the approximate center frequency. **Make sure that your `Coefs` wave has the correct number of coefficients relative to your `Wavenumber` wave**. 46 | 47 | Options include: 48 | 49 | - `PolyNum` the order of the polynomial baseline. To remove the baseline input `PolyNum=0`. The default is cubic. 50 | 51 | - `Plot=1` will plot the results in a pleasing manner. 52 | 53 | - `PeakType=1` is Gaussian (default behaviour). `PeakType=0` is Lorentzian. 54 | 55 | ## FitTimeSeriesDisp.ipf 56 | Contains `FitTimeSeries2` which is almost identical to `FitTimeSeries` except that it uses a dispersive lorentzian lineshape function which has one parameter for the amplitude of the real part and one for the amplitude of the dispersive part. If the initial values for either of these components is 0 than the function assumes that those peaks are totally real or dispersive and holds that component to 0. 57 | 58 | ## PerkinElmerImport.ipf 59 | This file contains a single procedure, `LoadPEData()`. This procedure will ask the user which files to load and then will load Perkin Elmer's proprietary binary format into waves named after the file. It will include the experimental info stored in the binary file in the created wave's note. The procedure will also display the loaded waves. 60 | 61 | ## SignalProcessing.ipf 62 | This Igor Procedure File (.ipf) contains procedures useful for analyzing ISRS data. In fact, these procedures should be useful for analyzing any data which can be reasonably described as a sum of damped sinusoids. The two main procedures are: 63 | - `LPSVD(signal,[M,LFactor,RemoveBias])` which fits the data, in a *linear* least squares sense using the **Linear Prediction with Singular Value Decomposition** (LPSVD) algorithm. Estimates of the variances for the parameters returned by LPSVD are calculated as the [Cramer-Rao bound](http://en.wikipedia.org/wiki/Cram%C3%A9r%E2%80%93Rao_bound) and returned in a wave named `sigma_LPSVD_coefs`. 64 | - `Cadzow(signal, M, iters,[lFactor,q])` which filters the data using Cadzow's Composite Property Mapping Algorithm. 65 | -------------------------------------------------------------------------------- /AddGroundPanel.ipf: -------------------------------------------------------------------------------- 1 | #pragma rtGlobals=1 // Use modern global access method. 2 | #include ":Utilities" 3 | 4 | Menu "Macros" 5 | "Add Ground", AddGroundPanel() 6 | "-" 7 | End 8 | 9 | Function AddGroundPanel() : Panel 10 | //Function that generates the panel with the attached graphs. 11 | 12 | String/G name_Timepoints="root:timepoints" 13 | String/G name_Groundadded="root:groundadded" 14 | String/G name_FitGnd="root:fit_OCPo2" 15 | 16 | Wave timepoints=$name_Timepoints 17 | Wave groundadded=$name_Groundadded 18 | Wave fitgnd=$name_FitGnd 19 | 20 | If(!WaveExists(timepoints)||!WaveExists(groundadded)) 21 | DoAlert 0, "This panel will not work! The proper waves do not exist!" 22 | Return -1 23 | EndIf 24 | 25 | String timelist="\"_None_;" 26 | variable i=0 27 | //Build a list to show in the pop up menu 28 | for(i=0;i= 1000) 95 | titlestring += num2str(str2num(timeStr)/1000)+ " ps" 96 | Else 97 | titlestring+= timeStr + " fs" 98 | Endif 99 | titlestring+=" subg/withg" 100 | 101 | DoWindow/T GAGraph, titlestring 102 | Display/HOST=GAGraph/N=TimeTraces/W=(0,1,1,0.5)/FG=(FL,FT,FR,*) subg,withg vs shiftx 103 | ModifyGraph/W=GAGraph#TimeTraces rgb[0]=(0,0,0),rgb[1]=(0,0,65535) 104 | ModifyGraph/W=GAGraph#TimeTraces mirror=2,minor(bottom)=1,prescaleExp(left)=3 105 | Label/W=GAGraph#TimeTraces left "Raman Gain (mOD)" 106 | Label/W=GAGraph#TimeTraces bottom "Raman Shift (cm\\S-1\\M)" 107 | SetAxis/W=GAGraph#TimeTraces/A=2 left 108 | If(startUp!=0) 109 | SetAxis/W=GAGraph#TimeTraces bottom V_Min,V_Max 110 | EndIf 111 | End 112 | 113 | Function chooseTimePoint(pa) : PopupMenuControl 114 | STRUCT WMPopupAction &pa 115 | 116 | String/G name_Timepoints 117 | String/G name_Groundadded 118 | 119 | Wave timepoints=$name_Timepoints 120 | Wave groundadded=$name_Groundadded 121 | 122 | switch( pa.eventCode ) 123 | case 2: // mouse up 124 | If(CmpStr(pa.UserData,pa.popStr)==0) 125 | //The user has chosen the same timepoint, do nothing 126 | break 127 | EndIf 128 | 129 | //Remove customization for previous time point 130 | FindValue/v=(str2num(pa.UserData)) timepoints 131 | ModifyGraph/W=GAGraph#GroundAdded rgb($name_Groundadded[-V_Value-1])=(0,0,65535) 132 | ModifyGraph/W=GAGraph#GroundAdded msize($name_Groundadded[-V_Value-1])=10 133 | ModifyGraph/W=GAGraph#GroundAdded marker($name_Groundadded[-V_Value-1])=42 134 | 135 | If(CmpStr(pa.popStr,"_None_")==0) 136 | //If the user has changed to None 137 | Break 138 | EndIf 139 | 140 | plotTimePnt(pa.popStr) 141 | FindValue/v=(str2num(pa.popStr)) timepoints 142 | Slider groundToAdd, win=GAGraph#GAPanel, value= groundadded[V_value] 143 | //Cursor/W=GAGraph#GroundAdded/P A, $StringFromList(0,TraceNameList("GAGraph#GroundAdded",";",1)), V_Value 144 | ModifyGraph/W=GAGraph#GroundAdded rgb($name_Groundadded[V_Value])=(0,0,65535) 145 | ModifyGraph/W=GAGraph#GroundAdded msize($name_Groundadded[V_Value])=10 146 | ModifyGraph/W=GAGraph#GroundAdded marker($name_Groundadded[V_Value])=42 147 | 148 | //Store new timepoint in the userdata for this popup for use by other functions 149 | pa.UserData = pa.popStr 150 | break 151 | case -1: // control being killed 152 | break 153 | endswitch 154 | 155 | 156 | return 0 157 | End 158 | 159 | Function chooseGroundAdded(pa) : PopupMenuControl 160 | STRUCT WMPopupAction &pa 161 | 162 | String/G name_Timepoints 163 | String/G name_Groundadded 164 | String/G name_FitGnd 165 | 166 | Wave timepoints=$name_Timepoints 167 | 168 | 169 | switch( pa.eventCode ) 170 | case 2: // mouse up 171 | //Update all "withg" waves to reflect the new ground added 172 | AddAllGround($name_Timepoints,$name_FitGnd,$name_Groundadded) 173 | If(CmpStr(pa.UserData,pa.popStr)==0) 174 | //The user has chosen the same groundadded, do nothing 175 | break 176 | EndIf 177 | 178 | //Update user data and path to groundadded 179 | pa.UserData = pa.popStr 180 | name_Groundadded = pa.popStr 181 | 182 | Wave groundadded=$name_Groundadded//Have to do assignment here! 183 | If(!WaveExists($name_Timepoints)) 184 | //There is no timepoints wave chosen yet 185 | //Do nothing and return control 186 | Return -1 187 | EndIf 188 | 189 | //Kill the subwindow so that we can recreate it with the new ground added 190 | KillWindow GAGraph#Groundadded 191 | 192 | //Create a new subwindow with the same name 193 | Display/HOST=GAGraph/N=Groundadded/W=(0,0.5,1,1)/FG=(FL,*,FR,FB) Groundadded vs Timepoints 194 | ModifyGraph/W=GAGraph#Groundadded mirror=2,minor(bottom)=1,prescaleExp(left)=2,prescaleExp(bottom)=-3 195 | ModifyGraph/W=GAGraph#Groundadded mode=4,marker=19 196 | Label/W=GAGraph#Groundadded left "Fraction of Ground Added (%)";Label/W=GAGraph#Groundadded bottom "Time (ps)" 197 | 198 | //Make sure the slider is in the correct position 199 | FindValue/v=(str2num(GetUserData("GAGraph#GAPanel","whichTime",""))) timepoints 200 | Slider groundToAdd, win=GAGraph#GAPanel, value= groundadded[V_value] 201 | 202 | //Print "Cursor/W=GAGraph#GroundAdded/P A, "+name_Groundadded+", "+num2str(V_Value) 203 | //Print "A list of traces: "+TraceNameList("GAGraph#GroundAdded","\t",1) 204 | 205 | //Cursor/W=GAGraph#GroundAdded/P A, $StringFromList(0,TraceNameList("GAGraph#GroundAdded",";",1)), V_Value 206 | ModifyGraph/W=GAGraph#GroundAdded rgb($name_Groundadded[V_Value])=(0,0,65535),msize($name_Groundadded[V_Value])=10,marker($name_Groundadded[V_Value])=42 207 | break 208 | case -1: // control being killed 209 | break 210 | endswitch 211 | 212 | 213 | return 0 214 | End 215 | 216 | Function chooseFitGnd(pa) : PopupMenuControl 217 | STRUCT WMPopupAction &pa 218 | 219 | String/G name_Timepoints 220 | String/G name_Groundadded 221 | String/G name_FitGnd 222 | 223 | switch( pa.eventCode ) 224 | case 2: // mouse up 225 | 226 | If(CmpStr(pa.UserData,pa.popStr)==0) 227 | //The user has chosen the same groundadded, do nothing 228 | break 229 | EndIf 230 | 231 | pa.UserData = pa.popStr 232 | name_FitGnd = pa.popStr 233 | 234 | If(!WaveExists($name_Timepoints)) 235 | //There is no timepoints wave chosen yet 236 | //Do nothing and return control 237 | Return -1 238 | EndIf 239 | //Update all "withg" waves to reflect the new ground added 240 | AddAllGround($name_Timepoints,$name_FitGnd,$name_Groundadded) 241 | 242 | break 243 | case -1: // control being killed 244 | break 245 | endswitch 246 | 247 | 248 | return 0 249 | End 250 | 251 | Function AddGroundSlider(sa) : SliderControl 252 | STRUCT WMSliderAction &sa 253 | 254 | String/G name_Timepoints 255 | String/G name_Groundadded 256 | String/G name_FitGnd 257 | 258 | Wave timepoints=$name_Timepoints 259 | Wave groundadded=$name_Groundadded 260 | 261 | If(!WaveExists($name_Timepoints)) 262 | //The timepoints wave hasn't been chosen yet 263 | //Return control to calling function 264 | Return -1 265 | EndIf 266 | 267 | String S_myTime 268 | Variable myTime 269 | 270 | 271 | switch( sa.eventCode ) 272 | case -1: // control being killed 273 | break 274 | default: 275 | if( sa.eventCode & 1 ) // value set 276 | S_myTime = GetUserData("GAGraph#GAPanel","whichTime","") 277 | If(CmpStr(S_myTime,"_None_")==0) 278 | //Do nothing 279 | Break 280 | EndIf 281 | myTime = str2num(S_myTime) 282 | Variable curval = sa.curval 283 | addground3(curval) 284 | endif 285 | break 286 | endswitch 287 | 288 | return 0 289 | End 290 | 291 | Static Function AddGround3(amount) 292 | //this adds a set amount of ground to particular timepoint and updates groundadded accordingly 293 | Variable amount 294 | 295 | String/G name_Timepoints 296 | String/G name_Groundadded 297 | String/G name_FitGnd 298 | 299 | Wave timepoints=$name_Timepoints 300 | Wave groundadded=$name_Groundadded 301 | WAVE ground=$name_FitGnd 302 | 303 | Variable timepoint = str2num(GetUserData("GAGraph#GAPanel","whichTime","")) 304 | 305 | FindValue/v=(timepoint) timepoints 306 | 307 | String currenttime 308 | 309 | if(timepoint<=0) 310 | currenttime="m"+num2istr(abs(timepoint)) 311 | else 312 | currenttime="p"+num2istr(timepoint) 313 | endif 314 | WAVE withg = $(currenttime+"_withg") 315 | WAVE subg = $(currenttime+"_subg") 316 | If(WaveExists(withg)&&WaveExists(subg)) 317 | withg=subg+amount*ground 318 | groundadded[V_value]= amount 319 | Else 320 | DoAlert/T="Wave does not exist " 0, currenttime + " does not exist, please check your parameters" 321 | EndIf 322 | End 323 | -------------------------------------------------------------------------------- /DispLineShapes.ipf: -------------------------------------------------------------------------------- 1 | #pragma rtGlobals=3 // Use modern global access method. #include ":Utilities" #include ":FitFuncs" Function InitLinSim() String savDF= GetDataFolder(1) // Save current DF for restore. if( DataFolderExists("root:WMLinSimData") ) SetDataFolder root:WMLinSimData else NewDataFolder/S root:WMLinSimData // Our stuff goes in here. endif SetDataFolder savDF // Restore current DF. End Function instantaneousFreq(beginfreq,endfreq,T1,t) Variable beginfreq, endfreq, T1, t Return 2*pi*(t*endfreq+(-1+Exp(-t/T1))*T1*(endfreq-beginfreq)) End Function FID(beginfreq,endfreq,T1,t,w,td) // this is because we're modeling the actinic pump as a delta function // if we were to take into account the fact that it had some width then // we'd half to be more careful. Variable beginfreq, endfreq, T1,td // tD is the delay between actinic and the probes (actinic is always at t=0) WAVE w,t variable phase,phase0,phasecorr if(t(cutoff*V_sdev)) 24 | myTempWave[i]=NaN 25 | EndIf 26 | EndFor 27 | //Recalculate wavestats 28 | WaveStats/Z/Q myTempWave 29 | KillWaves/Z myTempWave 30 | Return v_sdev 31 | End 32 | 33 | Function BetterSDev2(myWave) 34 | //A method for calculating a better estimate for the standard deviation by iteratively removing 35 | //outliers and then calculating the standard deviation. 36 | WAVE myWave //Wave to calculate the standard deviation 37 | 38 | Variable v_npnts,v_avg,v_sdev,old_npnts//Some variables 39 | 40 | Duplicate/FREE/O myWave myTempWave//make a temp wave to do the calculations on 41 | 42 | variable i=0 43 | 44 | WaveStats/Z/Q myTempWave 45 | 46 | Do 47 | old_npnts=v_npnts 48 | 49 | For(i=0;i(2*V_sdev)) 52 | myTempWave[i]=NaN 53 | EndIf 54 | EndFor 55 | //Recalculate wavestats 56 | WaveStats/Z/Q myTempWave 57 | While(old_npnts!=v_npnts)//Check to see if any outliers were removed, continue if yes 58 | 59 | Return v_sdev 60 | 61 | End 62 | 63 | Function ForcePnts(myWave, myPnts) 64 | WAVE myWave, myPnts 65 | If(0)//WaveExists($(NameOfWave(myWave)+"_pnts"))) 66 | //Do something 67 | Concatenate/NP/O {$(NameOfWave(myWave)+"_pnts"),myPnts}, $(NameOfWave(myWave)+"_pnts") 68 | WAVE myPnts = $(NameOfWave(myWave)+"_pnts") 69 | Sort myPnts, myPnts 70 | //Remove duplicates, need to write a function to do this, I think ... 71 | Else 72 | Duplicate/o myPnts $(NameOfWave(myWave)+"_pnts") 73 | Endif 74 | Duplicate/O myPnts, $(NameOfWave(myWave)+"_Ys") 75 | WAVE myYs = $(NameOfWave(myWave)+"_Ys") 76 | 77 | myYs=myWave(myPnts) 78 | End 79 | 80 | Function RemoveBaselines(timepoints,[myPnts,InterpOrder]) 81 | // Goes through timepoints and removes the baselines 82 | // If myPnts is not provided no points are forced 83 | WAVE timepoints, myPnts 84 | Variable InterpOrder //Used for the interpolation of the baseline 85 | 86 | If(ParamIsDefault(InterpOrder)) 87 | InterpOrder=2 88 | EndIf 89 | 90 | Variable i, length = numpnts(timepoints) 91 | String currenttime 92 | 93 | for(i=0;i0) 247 | ClusterInfo[j][%Begin] = i 248 | Do 249 | i+=1 250 | While((myPnts[i+1]-myPnts[i])0) 251 | ClusterInfo[j][%End] = i 252 | j+=1 253 | EndIf 254 | EndFor 255 | Redimension/N=(j,2) ClusterInfo 256 | 257 | Variable NumClusters=j,ClusterSize=0,newNumPnts=0 258 | Variable firstValue=0, incrementValue=0 259 | 260 | For(j=0;j=0//remove the unwanted points 295 | End 296 | 297 | Function/S RemoveBaseline(myWave,[interpOrder,Name]) 298 | //After the baseline points are chosen this will interpolate a spline between the points and 299 | //remove the baseline from the data. 300 | Wave myWave 301 | Variable interpOrder 302 | String Name 303 | 304 | If(ParamIsDefault(interpOrder)) 305 | interpOrder=2//Cubic Spline 306 | EndIf 307 | 308 | If(ParamIsDefault(Name)) 309 | Name=NameOfWave(myWave)+"_nb" 310 | EndIf 311 | 312 | //My smoothing variables 313 | Variable cutoff=10, n=3 314 | 315 | Duplicate/FREE myWave myWave_smth 316 | FFB(myWave_smth,cutoff,n)//Comment this line out if the data 317 | //are already smoothed! 318 | 319 | Variable length = numpnts(myWave) 320 | 321 | Wave myPnts = $(NameOfWave(myWave)+"_pnts") 322 | If(!WaveExists(myPnts)) 323 | Print "There was an error,"+NameOfWave(myPnts)+" doesn't exist" 324 | Return "" 325 | EndIf 326 | Duplicate/O myPnts, $(NameOfWave(myWave)+"_Ys") 327 | WAVE myYs = $(NameOfWave(myWave)+"_Ys") 328 | 329 | //Calculate points 330 | myYs = myWave_smth(myPnts) 331 | 332 | //Make my result waves 333 | Make/D/O/N=(length) $(NameOfWave(myWave)+"_bl") 334 | WAVE myWave_bl = $(NameOfWave(myWave)+"_bl") 335 | Duplicate/O myWave $Name 336 | WAVE myWave_nb = $Name 337 | 338 | Interpolate2/T=(interpOrder)/I=3/E=2/Y=myWave_bl myPnts, myYs//Cubic spline interpolation 339 | 340 | myWave_nb-=myWave_bl 341 | 342 | Return GetWavesDataFolder(myWave_nb, 2) 343 | End 344 | 345 | Function/S DoBaseline(myWave,[cutoff]) 346 | //Function that picks points and removes the baseline. 347 | Wave myWave 348 | Variable cutoff 349 | 350 | If(ParamIsDefault(cutoff)) 351 | cutoff=0.1 352 | EndIf 353 | 354 | If(!WaveExists(myWave)) 355 | DoAlert 0, "The wave you gave me does not exist" 356 | Return "-1" 357 | EndIf 358 | //pick the points 359 | pickpoints(myWave,cutoff=cutoff) 360 | Return RemoveBaseline(myWave)//Return a reference to the baseline subtracted data 361 | End 362 | 363 | Function/S DoBaseline2(myWave,num) 364 | //Need to clean up this function 365 | //Questions: 366 | //Can I make this generically adaptive? Can I make it so it can correctly choose the right number of passes? 367 | //Can I fix the naming scheme to make this less retarded? 368 | Wave myWave 369 | Variable num 370 | String wl=GetWavesDataFolder(myWave, 2)+";"//My wave list 371 | If(!WaveExists(myWave)) 372 | DoAlert 0, "The wave you gave me does not exist" 373 | Return "" 374 | EndIf 375 | 376 | Variable i=0 377 | Variable myCutoff=0.1 378 | pickpoints(myWave,cutoff=myCutoff) 379 | String myName="",myName_nb=(NameOfWave(myWave)+"_nb"+num2str(i)) 380 | RemoveBaseline(myWave,Name=myName_nb) 381 | wl+=GetWavesDataFolder($myName_nb, 2)+";" 382 | For(i=0;i=npts ) 11 | break 12 | EndIf 13 | newx = (x-w[i+2])/(w[i+3]/2) 14 | r += (w[i]+w[i+1]*newx)/(newx^2+1) 15 | i+=4 16 | while(1) 17 | return r 18 | End 19 | 20 | Function Fano(w,x) 21 | WAVE w; Variable x 22 | 23 | Variable r= w[0] 24 | variable npts= numpnts(w),i=1 25 | variable newx = 0 26 | do 27 | if( i>=npts ) 28 | break 29 | EndIf 30 | newx = (x-w[i+2])/(w[i+3]/2) 31 | r += w[i]*(w[i+1]+newx)^2/(newx^2+1) 32 | i+=4 33 | while(1) 34 | return r 35 | End 36 | 37 | Function fitTimeSeries2(timepoints, pnt1, pnt2,wavenumber,Coefs,[gnd,Wiggle,Width,Plot,PolyNum,subrangeStart,subrangeEnd,suffix]) 38 | //modified David Hoffman 39 | WAVE timepoints 40 | Variable pnt1, pnt2 41 | WAVE/T wavenumber 42 | WAVE Coefs 43 | WAVE gnd 44 | WAVE wiggle 45 | WAVE Width 46 | String Suffix 47 | Variable Plot 48 | Variable PolyNum 49 | Variable subrangeStart,subrangeEnd 50 | 51 | Variable includeGND = !ParamIsDefault(gnd) //The user has indicated that the ground state should be included in the fitting procedure 52 | 53 | if(ParamIsDefault(Plot)) 54 | Plot=0 55 | EndIf 56 | 57 | if(ParamIsDefault(suffix)) 58 | If(includeGND) 59 | Suffix="_subg" 60 | Else 61 | Suffix="_withg" 62 | EndIf 63 | EndIf 64 | 65 | If(ParamIsDefault(PolyNum)) 66 | PolyNum = 4 67 | EndIf 68 | 69 | If(ParamIsDefault(Wiggle)) 70 | Make/N=(numpnts(wavenumber))/O/D/FREE Vlimit = 30 71 | Else 72 | Duplicate/O/FREE Wiggle Vlimit 73 | EndIf 74 | 75 | If(ParamIsDefault(Width)) 76 | Make/N=(numpnts(wavenumber))/O/D/FREE MaxWidth = 100 77 | Else 78 | Duplicate/O/FREE Width MaxWidth 79 | EndIf 80 | 81 | IF(WaveExists(root:shiftx)) 82 | WAVE shiftx=root:shiftx 83 | Else 84 | DoAlert/T="fitTimeSeries Failed!" 0, "Why are you trying to run this macro so early!?" 85 | Return -1 86 | EndIf 87 | 88 | //***************************************************************// 89 | // time to do some error checking// 90 | if(numpnts(coefs)!=(numpnts(wavenumber)*4+1)) //Check to see if there are enough 91 | //coefficients for the number of peaks you will fit to. 92 | Print "The number of peak coefficients DID NOT match the number of peaks" 93 | Return 0 // Exit the function 94 | EndIf 95 | //***************************************************************// 96 | 97 | //This string holds the name of the current time point being fit 98 | String currenttime 99 | 100 | //These variables hold the lengths of the various waves so the don't need to be calculated again. 101 | Variable lengthT=numpnts(timepoints),lengthW=numpnts(wavenumber) 102 | //Some index variables for use later 103 | Variable i,j,k 104 | 105 | //Let's see if the user has opted to use a subrange, if not lets set the subrange to the full range 106 | If(ParamIsDefault(subrangeStart)) 107 | subrangeStart = 0 108 | EndIf 109 | 110 | If(ParamIsDefault(subrangeEnd)) 111 | subrangeEnd = lengthT 112 | EndIf 113 | 114 | //Now that we know we don't have any fundamental errors, let's print out the coefficients, 115 | // the wavenumbers and the points so that the user can find them later if need be 116 | 117 | //Print a row with the wavenumbers 118 | Print " " 119 | Print "Initial guesses:" 120 | PrintF "Wavenumber:" 121 | For(i=0;i"+num2str(coefs[j+3]-Vlimit[k]) 227 | CTextWave[i+1]="K"+num2str(j+3)+"<"+num2str(coefs[j+3]+Vlimit[k]) 228 | 229 | //width constraint, these values seem to work for the red table 230 | CTextWave[i+2]="K"+num2str(j+4)+">3" 231 | CTextWave[i+3]="K"+num2str(j+4)+"<"+num2str(MaxWidth[k]) 232 | k+=1 233 | EndFor 234 | 235 | For(i=1;i"+num2str(Alimit) 241 | EndIf 242 | If(coefs[i+1]) 243 | k=numpnts(CTextWave) 244 | InsertPoints k, 1, CTextWave 245 | CTextWave[k]="K"+num2str(i+1)+">"+num2str(Alimit) 246 | EndIf 247 | EndFor 248 | Print "This is my constraint WAVE:" 249 | Print CTextWave 250 | Print " " 251 | Print "H_string is "+H_string 252 | 253 | Duplicate/O coefs tempPeak_Coefs 254 | 255 | String F_String="" 256 | F_String="{fDispLorFit, tempPeak_Coefs, hold=\""+H_string+"\",EPSW=epsilonWave}" 257 | 258 | If(PolyNum>2) 259 | Print "Including an order", polynum-1, "polynomial for the baseline" 260 | Make/D/O/N=(PolyNum) tempBaseln_Coefs=0 261 | F_String+="{poly_XOffset "+num2str(polynum)+", tempBaseln_Coefs}" 262 | ElseIf(PolyNum!=0) 263 | Print "Including line for the baseline" 264 | Make/D/O/N=2 tempBaseln_Coefs=0 265 | F_String+="{line, tempBaseln_Coefs}" 266 | Polynum=2 267 | Else 268 | Print "No baseline!" 269 | EndIf 270 | 271 | If(includeGND) 272 | //Declare my structure for fitting 273 | Struct scaledGroundStruct gndStruct 274 | //Apparently the WAVE keyword is necessary to create a wave reference 275 | WAVE gndStruct.gnd = gnd 276 | WAVE gndStruct.shift = shiftx 277 | 278 | Make/D/O/N=1 scaleFactor = -0.5 279 | 280 | Make/D/O/N=(LengthT) ScaleFactors, sigma_ScaleFactors 281 | 282 | F_String += "{scaleGround, scaleFactor,STRC=gndStruct}" 283 | Else 284 | Print "No ground included." 285 | EndIf 286 | 287 | Print "" 288 | 289 | Printf " Start fitting: -/" 290 | 291 | //String to hold the timepoints which weren't fit properly 292 | Variable v_avg 293 | String badFits = "Bad Fits:\r" 294 | //PauseUpdate 295 | for(i=subrangeStart;ipnt2) 376 | tempBaseLineWave=tempBaseLineWave*(xpnt2) 377 | //Now fill in only that region 378 | If(PolyNum>2) 379 | For(j=0;j<(PolyNum);j+=1) 380 | tempBaseLineWave+=(tempBaseln_Coefs[j]*(shiftx-shiftx[pnt1])^j)*(x>=pnt1 && x<=pnt2) 381 | tempBaseLineWave2+=(tempBaseln_Coefs[j]*(x-shiftx[pnt1])^j) 382 | EndFor 383 | Else 384 | tempBaseLineWave2=tempBaseln_Coefs[0]+tempBaseln_Coefs[1]*x 385 | EndIf 386 | //We're doing this so that different regions can be fit separately. 387 | 388 | WAVE myWave = $currenttime 389 | Make/D/O/N=1340 $(currenttime+"_nb")=myWave-tempBaseLineWave 390 | EndIf 391 | EndIf 392 | 393 | DoUpdate 394 | 395 | V_FitError=0 396 | if (GetKeyState(0) & 32) // Is Escape key pressed now? 397 | Printf "User abort: " 398 | Plot=0 399 | Break 400 | EndIf 401 | EndFor 402 | 403 | DoUpdate 404 | 405 | PrintF "/-\r" 406 | Print " " 407 | 408 | Print "There were "+num2istr(fitsuccess)+" successful fittings and "+num2istr(fitfail)+" fitting failures in this run" 409 | If(fitfail!=0) 410 | Print badFits 411 | EndIf 412 | 413 | If(Plot!=0)//Plotting the results! 414 | for(k=0;k=npts ) 11 | break 12 | EndIf 13 | r += w[i]*2^(-((x-w[i+1])/(w[i+2]/2))^2) 14 | i+=3 15 | while(1) 16 | return r 17 | End 18 | 19 | Function fLorFit(w,x) 20 | WAVE w; Variable x 21 | 22 | Variable r= w[0] 23 | variable npts= numpnts(w),i=1 24 | do 25 | if( i>=npts ) 26 | break 27 | EndIf 28 | r += w[i]/(((x-w[i+1])/(w[i+2]/2))^2+1) 29 | i+=3 30 | while(1) 31 | return r 32 | End 33 | 34 | Function RecreateFits(timepoints,wavenumber) 35 | WAVE timepoints 36 | WAVE/T wavenumber 37 | WAVE Shiftx 38 | Variable counter, length,numpeaks,i 39 | String currenttime 40 | numpeaks=numpnts(wavenumber) 41 | length = numpnts(timepoints) 42 | Make/D/O/N=(numpeaks*3+1) Peak_coef_W 43 | for(counter=0;counter0) 243 | CTextWave[i]="K"+num2str(j+1)+">"+num2str(Alimit) 244 | Else 245 | CTextWave[i]="K"+num2str(j+1)+"<"+num2str(-Alimit) 246 | EndIf 247 | 248 | //frequency constraint, adjustable by the user, look above 249 | CTextWave[i+1]="K"+num2str(j+2)+">"+num2str(coefs[j+2]-Vlimit[k]) 250 | CTextWave[i+2]="K"+num2str(j+2)+"<"+num2str(coefs[j+2]+Vlimit[k]) 251 | 252 | //width constraint, these values seem to work for the red table 253 | CTextWave[i+3]="K"+num2str(j+3)+">0.1" 254 | CTextWave[i+4]="K"+num2str(j+3)+"<"+num2str(MaxWidth[k]) 255 | k+=1 256 | EndFor 257 | 258 | If(q) 259 | Print "This is my constraint WAVE:" 260 | Print CTextWave 261 | Print " " 262 | //Print "H_string is "+H_string 263 | //Print " " 264 | //Print Coefs 265 | EndIf 266 | Duplicate/O coefs tempPeak_Coefs 267 | 268 | String F_String="" 269 | 270 | If(PeakType) 271 | If(q) 272 | Print "Peak Type is Gaussian" 273 | Endif 274 | F_String="{fGFit, tempPeak_Coefs, hold=\"1\",EPSW=epsilonWave}" 275 | Else 276 | If(q) 277 | Print "Peak Type is Lorentzian" 278 | EndIf 279 | F_String="{fLorFit, tempPeak_Coefs, hold=\"1\",EPSW=epsilonWave}" 280 | EndIf 281 | 282 | If(PolyNum>2) 283 | If(q) 284 | Print "Including an order", polynum-1, "polynomial for the baseline" 285 | EndIf 286 | Make/D/O/N=(PolyNum) tempBaseln_Coefs=0 287 | F_String+="{poly_XOffset "+num2str(polynum)+", tempBaseln_Coefs}" 288 | ElseIf(PolyNum!=0) 289 | If(q) 290 | Print "Including line for the baseline" 291 | EndIf 292 | Make/D/O/N=2 tempBaseln_Coefs=0 293 | F_String+="{line, tempBaseln_Coefs}" 294 | Polynum=2 295 | Else 296 | If(q) 297 | Print "No baseline!" 298 | Endif 299 | Make/D/O/N=1 tempBaseln_Coefs=0 300 | EndIf 301 | 302 | If(includeGND) 303 | //Declare my structure for fitting 304 | Struct scaledGroundStruct gndStruct 305 | //Apparently the WAVE keyword is necessary to create a wave reference 306 | WAVE gndStruct.gnd = gnd 307 | WAVE gndStruct.shift = shiftx 308 | 309 | Make/D/O/N=1 scaleFactor = -0.5 310 | 311 | Make/D/O/N=(LengthT) ScaleFactors, sigma_ScaleFactors 312 | 313 | F_String += "{scaleGround, scaleFactor,STRC=gndStruct}" 314 | ElseIf(q) 315 | Print "No ground included." 316 | EndIf 317 | 318 | if(q) 319 | Print "" 320 | Printf " Start fitting: -/" 321 | EndIf 322 | 323 | //String to hold the timepoints which weren't fit properly 324 | 325 | String badFits = "Bad Fits:\r" 326 | 327 | for(i=subrangeStart;ipnt2) 432 | tempBaseLineWave=tempBaseLineWave*(xpnt2) 433 | //Now fill in only that region 434 | If(PolyNum>2) 435 | For(j=0;j=pnt1 && x<=pnt2) 437 | tempBaseLineWave2+=tempBaseln_Coefs[j]*(x-shiftx[pnt1])^j 438 | EndFor 439 | Else 440 | //Lines are handled slightly differently 441 | tempBaseLineWave+=(tempBaseln_Coefs[0]+tempBaseln_Coefs[1]*shiftx)*(x>=pnt1 && x<=pnt2) 442 | tempBaseLineWave2=tempBaseln_Coefs[0]+tempBaseln_Coefs[1]*x 443 | EndIf 444 | //We're doing this so that different regions can be fit separately. 445 | 446 | WAVE myWave = $currenttime 447 | Make/D/O/N=1340 $(currenttime+"_nb")=myWave-tempBaseLineWave 448 | EndIf 449 | EndIf 450 | if(q) 451 | DoUpdate 452 | EndIf 453 | 454 | V_FitError=0 455 | 456 | if (GetKeyState(0) & 32) // Is Escape key pressed now? 457 | Printf "User abort: " 458 | Break 459 | EndIf 460 | EndFor 461 | If(q) 462 | PrintF "/-\r" 463 | Print " " 464 | Print "There were "+num2istr(fitsuccess)+" successful fittings and "+num2istr(fitfail)+" fitting failures in this run" 465 | EndIf 466 | 467 | If(fitfail!=0 && q) 468 | Print badFits 469 | EndIf 470 | 471 | If(Plot)//Plotting the results! 472 | for(k=0;k