From 52f9c96756569e906638936c9df9191c92d6f898 Mon Sep 17 00:00:00 2001 From: Sascha Spors Date: Tue, 7 Feb 2017 18:53:14 +0100 Subject: [PATCH 01/10] Interpolation for better results --- sfs/time/source.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/sfs/time/source.py b/sfs/time/source.py index 994e15f4..c47a47b4 100644 --- a/sfs/time/source.py +++ b/sfs/time/source.py @@ -7,6 +7,8 @@ """ import numpy as np +from scipy.interpolate import interp1d +from scipy.signal import resample from .. import util from .. import defs @@ -57,9 +59,20 @@ def point(xs, signal, observation_time, grid, c=None): weights = 1 / (4 * np.pi * r) delays = r / c base_time = observation_time - signal_offset - return weights * np.interp(base_time - delays, - np.arange(len(data)) / samplerate, - data, left=0, right=0) + + oversampling = 10 + data = resample(data, oversampling * len(data)) + + p = np.interp(base_time - delays, np.arange(len(data)) / (oversampling*fs), + data, + left=0, right=0) + + + + #interpolator = interp1d(np.arange(len(data)), data, kind='cubic', bounds_error=False, fill_value=0) + #p = interpolator((base_time - delays) * fs) + + return p * weights def point_image_sources(x0, signal, observation_time, grid, L, max_order, From 19a54ea15a84a49c223d14233895bd46b41aff8e Mon Sep 17 00:00:00 2001 From: Sascha Spors Date: Tue, 7 Feb 2017 19:32:48 +0100 Subject: [PATCH 02/10] Used interpolation for calculation --- sfs/time/source.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/sfs/time/source.py b/sfs/time/source.py index c47a47b4..4aa04beb 100644 --- a/sfs/time/source.py +++ b/sfs/time/source.py @@ -13,7 +13,7 @@ from .. import defs -def point(xs, signal, observation_time, grid, c=None): +def point(xs, signal, observation_time, grid, c=None, interpolator_kind='linear'): r"""Source model for a point source: 3D Green's function. Calculates the scalar sound pressure field for a given point in @@ -59,19 +59,10 @@ def point(xs, signal, observation_time, grid, c=None): weights = 1 / (4 * np.pi * r) delays = r / c base_time = observation_time - signal_offset - - oversampling = 10 - data = resample(data, oversampling * len(data)) - - p = np.interp(base_time - delays, np.arange(len(data)) / (oversampling*fs), - data, - left=0, right=0) - - - - #interpolator = interp1d(np.arange(len(data)), data, kind='cubic', bounds_error=False, fill_value=0) - #p = interpolator((base_time - delays) * fs) - + interpolator = interp1d(np.arange(len(data)), data, + kind=interpolator_kind, bounds_error=False, + fill_value=0) + p = interpolator((base_time - delays) * samplerate) return p * weights From 3a52905a11b479c3fc8cc35465031008d72c55ca Mon Sep 17 00:00:00 2001 From: Sascha Spors Date: Tue, 7 Feb 2017 19:33:28 +0100 Subject: [PATCH 03/10] Removed import --- sfs/time/source.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sfs/time/source.py b/sfs/time/source.py index 4aa04beb..6bee3eda 100644 --- a/sfs/time/source.py +++ b/sfs/time/source.py @@ -8,7 +8,6 @@ import numpy as np from scipy.interpolate import interp1d -from scipy.signal import resample from .. import util from .. import defs From fdc0d86b7bbf9383ad362c2361be3e5c37ab921c Mon Sep 17 00:00:00 2001 From: Sascha Spors Date: Wed, 8 Feb 2017 13:20:34 +0100 Subject: [PATCH 04/10] Implemented sinc interpolation --- sfs/time/source.py | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/sfs/time/source.py b/sfs/time/source.py index 6bee3eda..2b5d4e8f 100644 --- a/sfs/time/source.py +++ b/sfs/time/source.py @@ -50,6 +50,7 @@ def point(xs, signal, observation_time, grid, c=None, interpolator_kind='linear' xs = util.asarray_1d(xs) data, samplerate, signal_offset = util.as_delayed_signal(signal) data = util.asarray_1d(data) + observation_time = util.asarray_1d(observation_time) grid = util.as_xyz_components(grid) if c is None: c = defs.c @@ -58,13 +59,38 @@ def point(xs, signal, observation_time, grid, c=None, interpolator_kind='linear' weights = 1 / (4 * np.pi * r) delays = r / c base_time = observation_time - signal_offset - interpolator = interp1d(np.arange(len(data)), data, - kind=interpolator_kind, bounds_error=False, - fill_value=0) - p = interpolator((base_time - delays) * samplerate) + if interpolator_kind == 'sinc': + p = _sinc_interp(data, np.arange(len(data)), + np.array((base_time - delays) * samplerate), samplerate) + else: + interpolator = interp1d(np.arange(len(data)), data, + kind=interpolator_kind, bounds_error=False, + fill_value=0) + p = interpolator((base_time - delays) * samplerate) return p * weights +def _sinc_interp(x, s, u, fs): + """ + Interpolates x, sampled at "s" instants + Output y is sampled at "u" instants ("u" for "upsampled") + + from Matlab: + http://phaseportrait.blogspot.com/2008/06/sinc-interpolation-in-matlab.html + """ + + #if len(x) != len(s): + # raise Exception, 'x and s must be the same length' + + # Find the period + T = s[1] - s[0] + + sincM = np.tile(u, (len(s), 1)) - np.tile(s[:, np.newaxis], (1, len(u))) + y = np.dot(x, np.sinc(sincM/T)) + + return y + + def point_image_sources(x0, signal, observation_time, grid, L, max_order, coeffs=None, c=None): """Point source in a rectangular room using the mirror image source model. From 40d3c7982d5cae52328a2cbb02971540d29486a9 Mon Sep 17 00:00:00 2001 From: Sascha Spors Date: Wed, 8 Feb 2017 13:44:19 +0100 Subject: [PATCH 05/10] Updated documentation --- sfs/time/source.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/sfs/time/source.py b/sfs/time/source.py index 2b5d4e8f..b76f59b6 100644 --- a/sfs/time/source.py +++ b/sfs/time/source.py @@ -61,7 +61,7 @@ def point(xs, signal, observation_time, grid, c=None, interpolator_kind='linear' base_time = observation_time - signal_offset if interpolator_kind == 'sinc': p = _sinc_interp(data, np.arange(len(data)), - np.array((base_time - delays) * samplerate), samplerate) + np.array((base_time - delays) * samplerate)) else: interpolator = interp1d(np.arange(len(data)), data, kind=interpolator_kind, bounds_error=False, @@ -70,21 +70,29 @@ def point(xs, signal, observation_time, grid, c=None, interpolator_kind='linear' return p * weights -def _sinc_interp(x, s, u, fs): +def _sinc_interp(x, s, u): """ - Interpolates x, sampled at "s" instants - Output y is sampled at "u" instants ("u" for "upsampled") + Ideal sinc interpolation of a signal + adapted from https://gist.github.com/endolith/1297227 - from Matlab: - http://phaseportrait.blogspot.com/2008/06/sinc-interpolation-in-matlab.html - """ + Parameters + ---------- + x : (N,) array_like + Signal to be interpolated. + s : (N,) array_like + Sampling instants of signal. + u : (N,) array_like + Sampling instants after interpolation. - #if len(x) != len(s): - # raise Exception, 'x and s must be the same length' + Returns + ------- + numpy.ndarray + Interpolated signal + """ - # Find the period + # sampling period T = s[1] - s[0] - + # perform sinc interpolation sincM = np.tile(u, (len(s), 1)) - np.tile(s[:, np.newaxis], (1, len(u))) y = np.dot(x, np.sinc(sincM/T)) From 84d0466ca0ca91e9d5d998bde77d96181c4c8337 Mon Sep 17 00:00:00 2001 From: Till Rettberg Date: Wed, 8 Feb 2017 14:23:56 +0100 Subject: [PATCH 06/10] Add time signal zeropadding to point source --- sfs/time/source.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/sfs/time/source.py b/sfs/time/source.py index b76f59b6..d23811a7 100644 --- a/sfs/time/source.py +++ b/sfs/time/source.py @@ -12,7 +12,7 @@ from .. import defs -def point(xs, signal, observation_time, grid, c=None, interpolator_kind='linear'): +def point(xs, signal, observation_time, grid, c=None, interpolator_kind='linear', zeropad=True): r"""Source model for a point source: 3D Green's function. Calculates the scalar sound pressure field for a given point in @@ -32,6 +32,8 @@ def point(xs, signal, observation_time, grid, c=None, interpolator_kind='linear' See `sfs.util.xyz_grid()`. c : float, optional Speed of sound. + zeropad : bool, optional + zeropad time signals with 2 samples for spline interpolation. Returns ------- @@ -59,11 +61,16 @@ def point(xs, signal, observation_time, grid, c=None, interpolator_kind='linear' weights = 1 / (4 * np.pi * r) delays = r / c base_time = observation_time - signal_offset - if interpolator_kind == 'sinc': - p = _sinc_interp(data, np.arange(len(data)), - np.array((base_time - delays) * samplerate)) + if zeropad: + time_instants = np.arange(-2, len(data) + 2) + data = np.concatenate([[0, 0], data, [0, 0]]) else: - interpolator = interp1d(np.arange(len(data)), data, + time_instants = np.arange(len(data)) + if interpolator_kind is 'sinc': + p = _sinc_interp(data, time_instants, + np.array((base_time - delays) * samplerate), samplerate) + else: + interpolator = interp1d(time_instants, data, kind=interpolator_kind, bounds_error=False, fill_value=0) p = interpolator((base_time - delays) * samplerate) From ea96dc6de12114c254f111cda4a3cec8e1cbc52e Mon Sep 17 00:00:00 2001 From: Till Rettberg Date: Wed, 15 Feb 2017 12:24:48 +0100 Subject: [PATCH 07/10] Modify Sinc Interpolator to cope with desired times in ndarrays --- sfs/time/source.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sfs/time/source.py b/sfs/time/source.py index d23811a7..887293a5 100644 --- a/sfs/time/source.py +++ b/sfs/time/source.py @@ -100,9 +100,11 @@ def _sinc_interp(x, s, u): # sampling period T = s[1] - s[0] # perform sinc interpolation + shape = u.shape + u = u.flatten() sincM = np.tile(u, (len(s), 1)) - np.tile(s[:, np.newaxis], (1, len(u))) y = np.dot(x, np.sinc(sincM/T)) - + y = np.reshape(y, shape) return y From cc7ecf444d637f5eed1cda32a25e57cca4da3b32 Mon Sep 17 00:00:00 2001 From: Till Rettberg Date: Fri, 17 Feb 2017 16:33:51 +0100 Subject: [PATCH 08/10] Change API for interpolation in source. Reimplement sinc interpolation. --- sfs/time/source.py | 60 ++++++++++++---------------------------------- 1 file changed, 15 insertions(+), 45 deletions(-) diff --git a/sfs/time/source.py b/sfs/time/source.py index 887293a5..23f74e2a 100644 --- a/sfs/time/source.py +++ b/sfs/time/source.py @@ -12,7 +12,8 @@ from .. import defs -def point(xs, signal, observation_time, grid, c=None, interpolator_kind='linear', zeropad=True): +def point(xs, signal, observation_time, grid, c=None, interpolator=interp1d, + zeropad=0, **kwargs): r"""Source model for a point source: 3D Green's function. Calculates the scalar sound pressure field for a given point in @@ -32,8 +33,12 @@ def point(xs, signal, observation_time, grid, c=None, interpolator_kind='linear' See `sfs.util.xyz_grid()`. c : float, optional Speed of sound. - zeropad : bool, optional - zeropad time signals with 2 samples for spline interpolation. + interpolator : function, optional + A function which constructs and returns a 1d interpolator. + (Expamples: interp1d, PchipInterpolator from scipy.interpolator) + zeropad : int, optional + pre- & post-zeropad time signals with N samples. + (For interpolation only.) Returns ------- @@ -61,51 +66,16 @@ def point(xs, signal, observation_time, grid, c=None, interpolator_kind='linear' weights = 1 / (4 * np.pi * r) delays = r / c base_time = observation_time - signal_offset - if zeropad: - time_instants = np.arange(-2, len(data) + 2) - data = np.concatenate([[0, 0], data, [0, 0]]) - else: - time_instants = np.arange(len(data)) - if interpolator_kind is 'sinc': - p = _sinc_interp(data, time_instants, - np.array((base_time - delays) * samplerate), samplerate) - else: - interpolator = interp1d(time_instants, data, - kind=interpolator_kind, bounds_error=False, - fill_value=0) - p = interpolator((base_time - delays) * samplerate) + time_instants = np.arange(-zeropad, len(data) + zeropad) + data = np.concatenate([np.zeros(zeropad), data, np.zeros(zeropad)]) + p = interpolator(time_instants, data, **kwargs)((base_time - delays) * samplerate) return p * weights -def _sinc_interp(x, s, u): - """ - Ideal sinc interpolation of a signal - adapted from https://gist.github.com/endolith/1297227 - - Parameters - ---------- - x : (N,) array_like - Signal to be interpolated. - s : (N,) array_like - Sampling instants of signal. - u : (N,) array_like - Sampling instants after interpolation. - - Returns - ------- - numpy.ndarray - Interpolated signal - """ - - # sampling period - T = s[1] - s[0] - # perform sinc interpolation - shape = u.shape - u = u.flatten() - sincM = np.tile(u, (len(s), 1)) - np.tile(s[:, np.newaxis], (1, len(u))) - y = np.dot(x, np.sinc(sincM/T)) - y = np.reshape(y, shape) - return y +def sincinterp(x, y, **kwargs): + def f(xnew): + return sum([y[i] * np.sinc(xnew - x[i]) for i in range(len(x))]) + return f def point_image_sources(x0, signal, observation_time, grid, L, max_order, From db0f788bae48083b24d33ff97e26ef206f82d0e4 Mon Sep 17 00:00:00 2001 From: Till Rettberg Date: Mon, 27 Feb 2017 14:30:20 +0100 Subject: [PATCH 09/10] Change API for interpolation in source. Add wrapper for linear interpolation. --- sfs/time/source.py | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/sfs/time/source.py b/sfs/time/source.py index 23f74e2a..4290c671 100644 --- a/sfs/time/source.py +++ b/sfs/time/source.py @@ -12,8 +12,32 @@ from .. import defs -def point(xs, signal, observation_time, grid, c=None, interpolator=interp1d, - zeropad=0, **kwargs): +def linear_interpolator(x, y): + """1d linear interpolator with zero-padding. + + Parameters + ---------- + x : (N,) array_like + Sampling points. + y : (N,) array_like + Values at sampling points. + + Returns + ------- + function + Piecewise linear interpolant. + + """ + x = util.asarray_1d(x) + y = util.asarray_1d(y) + if len(x) < 3: + x = np.concatenate([np.array([min(x)-1]), x, np.array([max(x)+1])]) + y = np.concatenate([np.array([0]), y, np.array([0])]) + return interp1d(x, y, bounds_error=False, fill_value=0) + + +def point(xs, signal, observation_time, grid, c=None, + interpolator=linear_interpolator): r"""Source model for a point source: 3D Green's function. Calculates the scalar sound pressure field for a given point in @@ -35,10 +59,7 @@ def point(xs, signal, observation_time, grid, c=None, interpolator=interp1d, Speed of sound. interpolator : function, optional A function which constructs and returns a 1d interpolator. - (Expamples: interp1d, PchipInterpolator from scipy.interpolator) - zeropad : int, optional - pre- & post-zeropad time signals with N samples. - (For interpolation only.) + see: linear_interpolator, sinc_interpolator Returns ------- @@ -66,13 +87,14 @@ def point(xs, signal, observation_time, grid, c=None, interpolator=interp1d, weights = 1 / (4 * np.pi * r) delays = r / c base_time = observation_time - signal_offset - time_instants = np.arange(-zeropad, len(data) + zeropad) - data = np.concatenate([np.zeros(zeropad), data, np.zeros(zeropad)]) - p = interpolator(time_instants, data, **kwargs)((base_time - delays) * samplerate) + p = interpolator(np.arange(len(data)), data)((base_time - delays) * samplerate) return p * weights -def sincinterp(x, y, **kwargs): +def sinc_interpolator(x, y): + x = util.asarray_1d(x) + y = util.asarray_1d(y) + def f(xnew): return sum([y[i] * np.sinc(xnew - x[i]) for i in range(len(x))]) return f From e27876495e9994d8ae7d8b09bf95054a89f97059 Mon Sep 17 00:00:00 2001 From: Till Rettberg Date: Wed, 1 Nov 2017 16:11:08 +0100 Subject: [PATCH 10/10] Fix linear interpolation: Always use zeropadding --- sfs/time/source.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sfs/time/source.py b/sfs/time/source.py index 4290c671..4804049e 100644 --- a/sfs/time/source.py +++ b/sfs/time/source.py @@ -30,9 +30,8 @@ def linear_interpolator(x, y): """ x = util.asarray_1d(x) y = util.asarray_1d(y) - if len(x) < 3: - x = np.concatenate([np.array([min(x)-1]), x, np.array([max(x)+1])]) - y = np.concatenate([np.array([0]), y, np.array([0])]) + x = np.concatenate([np.array([min(x)-1]), x, np.array([max(x)+1])]) + y = np.concatenate([np.array([0]), y, np.array([0])]) return interp1d(x, y, bounds_error=False, fill_value=0)