-
Notifications
You must be signed in to change notification settings - Fork 9
Various PSX fixes #2362
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Various PSX fixes #2362
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
f508957 to
f23db8a
Compare
1332271 to
08deb18
Compare
a12c1dd to
193bf80
Compare
This comment was marked as outdated.
This comment was marked as outdated.
|
@t-b I also tested pasting the average fit results into Excel. The row formatting is correct. However, Excel plots all the columns in one column. Excel is dumb. To correct this, paste into Excel, select the cells, select Data from the menu, select Text to Columns, and select the appropriate delimiter in the Excel pop-up GUI. |
|
@t-b there may also be issues with the indivdual decay tau values. For example in this case I'm getting a negative weighted tau. |
These routines allow to calculate all values between 2^1 and 2^30 which have 2 and 3 as prime factors. This helps in deciding a good cut off value for FFT as it only performs when the primes are smaller than 5.
The FFT implementation in Igor Pro requires the prime factors of the length to be < 5 for speedy execution. For some cases these prime factors can be very large (~1e5) and thus FFT would be dead slow, i.e. in the order of minutes on common machines. We now employ a data shortening to a size which has only 2 and 3 as prime factors, which makes FFT very fast. This is only done if we have prime factors larger than a 1000 in the original size.
We now reserve space for the defaults in the psxDeconvBPFilter/psxSweepBPFilter operations so that we can later on add the calculated defaults, which need parameters from psxKernel, can be added via PSX_AddDefaultFilterFrequencies.
And allow querying them from psxstats.
This is convenient for documentation purposes although this is not an input parameter.
Ever since 7aa2be6 (PSX: Add average fit for all states, 2025-06-04) we are using the per-combo index for gathering the eventKernelAmp/eventOnsetTime/eventPeakTime values. This is wrong as we must use the index of the displayed events.
camel case fix Co-authored-by: Copilot <[email protected]>
camel case fix Co-authored-by: Copilot <[email protected]>
a497d56 to
ba0dc32
Compare
|
@t-b I'm trying to test this PR, but getting confused. I recall that the most recent version of the PSX operation took several other operations as input arguments. In particular, psxsweepBPfilter, which seems to no longer be present in either active PSX branch. |
|
@timjarsky I'll have a look tomorrow. |
|
@timjarsky I found something please try again. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| KillWaves/Z root:PSX_hist, root:PSX_histX | ||
| Make/O/D/N=(nBins) root:PSX_hist, root:PSX_histX | ||
| WAVE hist = root:PSX_hist | ||
| WAVE histX = root:PSX_histX |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function creates and stores waves in the root folder (root:PSX_hist, root:PSX_histX) without cleaning them up afterward. If showPlot is false (the default), these waves are created but never used again, leading to memory leaks. Consider either: 1) Using FREE waves when showPlot=0, or 2) Cleaning up these waves before returning, or 3) Moving them to a dedicated data folder with proper lifecycle management.
| KillWaves/Z root:PSX_hist, root:PSX_histX | |
| Make/O/D/N=(nBins) root:PSX_hist, root:PSX_histX | |
| WAVE hist = root:PSX_hist | |
| WAVE histX = root:PSX_histX | |
| if (showPlot) | |
| KillWaves/Z root:PSX_hist, root:PSX_histX | |
| Make/O/D/N=(nBins) root:PSX_hist, root:PSX_histX | |
| WAVE hist = root:PSX_hist | |
| WAVE histX = root:PSX_histX | |
| else | |
| Make/FREE/D/N=(nBins) hist, histX | |
| endif |
| Function GetKernelDecayTau() | ||
|
|
||
| string psxPath | ||
| string ListOfwin = WinList("*", ";", "WIN:64") // Only graph windows | ||
| variable i | ||
| variable num = ItemsInList(ListOfwin, ";") | ||
|
|
||
| // Search all graph windows for one with psxFolder user data | ||
| for(i = 0; i < num; i += 1) | ||
| string baseWin = StringFromList(i, ListOfwin, ";") | ||
| psxPath = GetUserData(baseWin, "", "psxFolder") | ||
| if(strlen(psxPath) > 0) | ||
| break | ||
| endif | ||
| endfor | ||
|
|
||
| if(strlen(psxPath) == 0) | ||
| printf "No psxFolder user data found in any graph window.\r" |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function GetKernelDecayTau() searches through all graph windows to find PSX data. This is a fragile design that: 1) May return incorrect data if multiple PSX analyses are open, 2) Couples unrelated code through global state, 3) Makes debugging difficult. Consider passing the necessary context (window name or data folder reference) as a parameter instead of searching globally.
| Function GetKernelDecayTau() | |
| string psxPath | |
| string ListOfwin = WinList("*", ";", "WIN:64") // Only graph windows | |
| variable i | |
| variable num = ItemsInList(ListOfwin, ";") | |
| // Search all graph windows for one with psxFolder user data | |
| for(i = 0; i < num; i += 1) | |
| string baseWin = StringFromList(i, ListOfwin, ";") | |
| psxPath = GetUserData(baseWin, "", "psxFolder") | |
| if(strlen(psxPath) > 0) | |
| break | |
| endif | |
| endfor | |
| if(strlen(psxPath) == 0) | |
| printf "No psxFolder user data found in any graph window.\r" | |
| /// @brief Returns the kernel decay tau from the given PSX data folder path. | |
| /// @param psxPath The path to the PSX data folder (string). | |
| Function GetKernelDecayTau(psxPath) | |
| string psxPath | |
| if(strlen(psxPath) == 0) | |
| printf "No psxFolder path provided to GetKernelDecayTau.\r" |
| duplicate/O AvgEventDiff, root:forDisp | ||
| smooth 500, root:forDisp |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code contains debugging/development artifacts (creating root:forDisp on line 6464, commented-out line 6740 about plotting). These should be removed or placed behind conditional compilation flags for production code.
| duplicate/O AvgEventDiff, root:forDisp | |
| smooth 500, root:forDisp |
| INFO("index = %d, V_numNaNs = %d, kernelAmpSign = %d", n0 = index, n1 = V_numNans, n2 = kernelAmpSign) | ||
|
|
||
| // 1 NaN for the first event only, the rest is onset Time | ||
| // 5 NaNs for the first event only, the rest is onset Time |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment indicates "5 NaNs for the first event" but the test was changed from expecting 1 NaN to expecting 5 NaNs. This suggests that additional NaN values are being introduced in the event data structure. While this might be intentional due to the addition of new tau fields (weightedTau, slowTau, fastTau) and onset/rise y-values, the comment should be more specific about which fields contain NaNs and why this is expected behavior for the first event.
| // 5 NaNs for the first event only, the rest is onset Time | |
| // For the first event, 5 fields are expected to be NaN: weightedTau, slowTau, fastTau, onsetY, and riseY. | |
| // This is because these fields are not defined for the first event due to lack of preceding data or fit results. |
| defaults to `NaN` | ||
| The default values of `NaN` are replaced inside `psx`. For the order this is | ||
| `7`, for the frequencies `500` (`lowFreq`) and `50` (`highFreq`). |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation states that the filter uses default values of 7 for order and 500/50 for frequencies, but the code in MIES_Constants.ipf shows PSX_DECONV_FILTER_DEF_ORDER = 4 (line 2415) and there are no constants defined for default frequencies. The documentation should match the actual default values used in the implementation.
| `7`, for the frequencies `500` (`lowFreq`) and `50` (`highFreq`). | |
| `4`. There are no default frequencies defined in the implementation. |
| The default values of `NaN` are replaced inside `psx`. For the order this is | ||
| `7`, for the frequencies `500` (`lowFreq`) and `50` (`highFreq`). | ||
| Here `lowFreq` is the end and `highFreq` the start of the | ||
| passband, see also the description of `/LO` and `/HI` from `FilterIIR`. | ||
| passband, see also the description of `/LO` and `/HI` from `FilterIIR`. If the | ||
| frequency values are not ordered correctly, they are swapped. |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation for psxSweepBPFilter is identical to psxDeconvBPFilter documentation, including the same default values. However, since these are two different filters (sweep vs deconvolution), they may have different default values based on the PR description. The documentation should accurately reflect the defaults for each filter type or note if they are intentionally the same.
| psxDeconvBPFilter(800, 100) | ||
| psxDeconvBPFilter(400, 50, 11) |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The example code still references psxDeconvFilter instead of the new psxDeconvBPFilter function name. This is inconsistent with the renamed operation and will cause errors if users copy this example.
| // FindLevel/EDGE=(edge)/Q average, (riselowerThreshold * extrema) | ||
| variable onsetX = PSX_CalculateOnsetTimeFromAvg(average, meanKernelAmp, meanOnsetTime, meanPeakTime) | ||
| assert(!isNaN(onsetX), "onset time calculation must cannot be Nan") | ||
| variable level = (riseLowerThreshold * (extrema - average(onsetX))) + average(onsetX) |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name riseLowerThreshold is used here (line 2588) but was defined earlier as riselowerThreshold (line 2583). Igor Pro is case-insensitive for variable names, but this inconsistency in capitalization reduces code readability and maintainability. Consider using consistent capitalization throughout the function.
| variable level = (riseLowerThreshold * (extrema - average(onsetX))) + average(onsetX) | |
| variable level = (riselowerThreshold * (extrema - average(onsetX))) + average(onsetX) |
|
With the file located here, the following SF code doesn't plot anything or show any errors. |

2^l + 3^klogic, closes: PSX: Make it faster #1663()behind numberOfSDs in the JWN to document itV_modeand all other missing entries from StatsQuantiles to postProc output forstatsinpsxStatsTim provides Poc for figuring outpsxKernelparameters from scratchOld Todos
So this should be ready for a first test. I've adapted `psxKernel` and `psxStats` to allow passing in multiple selections.
that still makes sense. The baseline search shouldn't search past the previous
peak_t. With the baseline_t of the event available, PSX_CalculateEventPeak, instead
of using prevDeconvPeak_t to constrain the search, can use the event
baseline_tinstead. The above makes adding a peakfinding operation, which I suggested
yesterday, unnecessary.
// 3 checkboxes to turn all of these on/off below suppress update
// peak_t
// baseline_t
with, see SweepFormula: Groups multiple x-axis labels #2423psxStats, see psxstats: Should accept an array of properties #2422