-
Notifications
You must be signed in to change notification settings - Fork 112
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
Fix output length of resample
#596
Conversation
@test length(resample(sin.(1:1:35546), 1/55.55)) == 640 | ||
@test length(resample(randn(1822), 0.9802414928649835)) == 1786 | ||
@test length(resample(1:16_367_000*2, 10_000_000/16_367_000)) == 20_000_000 | ||
@test resample(zeros(1000), 0.012) == zeros(12) |
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.
These lengths now all match expectations:
julia> 35546 / 55.55
639.8919891989199
julia> 1822 * 0.9802414928649835
1786.0
julia> 16_367_000*2 * 10_000_000/16_367_000
2.0e7
julia> 1000 * 0.012
12.0
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #596 +/- ##
=======================================
Coverage 97.90% 97.90%
=======================================
Files 19 19
Lines 3248 3252 +4
=======================================
+ Hits 3180 3184 +4
Misses 68 68 ☔ View full report in Codecov by Sentry. |
After some simplification, I think there is a way to make sure that the output length matches. Is there an obstruction to generating |
For
Not sure what you're trying to say here. I guess we could consider changing the state from I tried to avoid putting too many changes in this PR for sake of reviewability, and rewriting the inner workings of |
Yeah, that was what I meant. Didn't consider the numerical error...if only there was a better way to calculate |
One more thing I'm not 100% sure about: Currently, the target output length is |
This may still give results that are inconsistent with how many samples `filt!` produces as the latter accumulates time-deltas (modulo Nϕ) which may be numerically different. However, `outputlength(H, length(x)) == filt!(y, H, x)` should hold more often this way. Also adjust `inputlength` accordingly.
60f2ec7
to
3573b6e
Compare
I suppose you mean |
Of course, yes.
Hm... That's not a reason that convinces me. A single sample does not seem that much more helpful than none. Would |
Perhaps we should take reference to what scipy or Matlab does? |
Good point. Looks like they both round up (for Matlab I found that to be documented as well), so reasonable for us to do the same. I guess then this is good to go then. |
resample
resample
Merge? |
Hm, but actually the assertion is not always true... julia> resample(ones(100), 2.00)
ERROR: AssertionError: length(y) >= outLen |
Ah yes, turns out Should we reconsider this question: Are we confident with the |
dc0c77c
to
27898c7
Compare
Hm, I think the asserts should be correct. Not sure what you mean by more defensive, do you propose to remove them? |
That particular one, yes. It arguably would have been better to get a result that is one sample shorter than expected than to see an assertion error. However, that might also have meant that this error in |
I agree with that approach. Though users may still want the output that's already calculated...maybe there is a good way to do this kind of exception handling I'm unaware of? Or return |
Then let's leave the Do you want to take this for another test flight and see if you find more problems or should we merge? |
Ran it with random inputs, was ok. LGTM, go ahead. |
This comprises a few changes that can be considered in on their own, but later ones depend on earlier ones for proper results. In commit order:
outputlength(::FIRRational, inputlength)
is fixed to correctly predict the number of valid samples produced byfilt!
. (For context:filt!
expects a sufficiently large output buffer and returns the number of samples actually written.)outputlength(::FIRArbitrary, inputlength)
is improved to do this correctly more often. Unfortunately, it can still be off-by-one sometimes, which seems almost inevitable. The reason is -- somewhat simplified -- thatfilt!
increases the time incrementally, which may be numerically different from considering the whole time-span at once. More complicated in reality, but conceptually: Starting at sample 1 and going in steps of 0.1, how many steps do you need to reach sample 2? Ten, because10 * 0.1 == 1
? No, one more, because0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 < 1
. Now, to be sure to haveoutputlength
give the correct result, I think one would have to replicate the same iterative index/phase computations, which seems excessive.inputlength
, specifically forRoundUp
andRoundDown
(where the latter is the old behavior and still the default). WithRoundDown
,inputlength
returns the largest input length such that the actual output length will be at most the given one, i.e.outputlength(H, inputlength(H, yL)) <= yL < outputlength(H, inputlength(H, yL)+1)
. OTOH, withRoundUp
,inputlength
returns the shortest input length such that the actual output length will be at least the given one, i.e.outputlength(H, inputlength(H, yL, RoundUp)-1) < yL <= outputlength(H, inputlength(H, yL, RoundUp))
.resample
, useinputlength
inRoundUp
mode to pad the input to the minimum length to get at leastceil(Int, length(x)*rate)
output samples, then trim the output to the required size as necessary.Some points I'd especially appreciate feedback on:
@assert
s or should that be more defensive?RoundingMode
API forinputlength
appropriate? OnlyRoundUp
andRoundDown
really make sense there, I guess, so maybe not? (Or should we just drop theRoundDown
behavior which might not be needed at all?)Fixes #186.