diff --git a/gstlal-inspiral/python/stats/inspiral_extrinsics.py b/gstlal-inspiral/python/stats/inspiral_extrinsics.py
index 2c72d844386ffeb71ef32030390d47eaed476d19..2fadae24cd076fd31abbbf6cc8774649b8449752 100644
--- a/gstlal-inspiral/python/stats/inspiral_extrinsics.py
+++ b/gstlal-inspiral/python/stats/inspiral_extrinsics.py
@@ -40,6 +40,7 @@ import numpy
 import os
 import random
 from scipy import stats
+from scipy import interpolate, fft, ifft
 import sys
 
 
@@ -52,6 +53,7 @@ from glue.text_progress_bar import ProgressBar
 from gstlal import stats as gstlalstats
 import lal
 from lal import rate
+from lal import LIGOTimeGPS
 from lalburst import snglcoinc
 import lalsimulation
 
@@ -997,8 +999,8 @@ class HyperRect(object):
 
 class DynamicBins(object):
 	def __init__(self, lower, upper, max_splits = None, min_vol = None):
-		self.lower = numpy.array(lower)
-		self.upper = numpy.array(upper)
+		self.lower = numpy.array(lower, dtype=float)
+		self.upper = numpy.array(upper, dtype=float)
 		if max_splits is not None:
 			self.max_splits = numpy.array(max_splits)
 		else:
@@ -1137,10 +1139,49 @@ class StaticDynamicBins(object):
 
 		return search_serialized(self.DB, point, ix = 0)
 
+class FFT(object):
+	def __init__(self):
+		self.fwdplan = {}
+		self.tvec = {}
+		self.fvec = {}
+
+	def __call__(self, arr):
+		length = len(arr)
+		if length not in self.fwdplan:
+			self.fwdplan[length] = lal.CreateForwardREAL4FFTPlan(length, 1)
+		if length not in self.tvec:
+			self.tvec[length] = lal.CreateREAL4Vector(length)
+		if length not in self.fvec:
+			self.fvec[length] = lal.CreateCOMPLEX8Vector(length / 2 + 1)
+
+		self.tvec[length].data[:] = arr[:]
+		lal.REAL4ForwardFFT(self.fvec[length], self.tvec[length], self.fwdplan[length])
+
+		return numpy.array(self.fvec[length].data, dtype=complex)
+
+class IFFT(object):
+	def __init__(self):
+		self.revplan = {}
+		self.tvec = {}
+		self.fvec = {}
+
+	def __call__(self, arr):
+		length = 2 * (len(arr) -1)
+		if length not in self.revplan:
+			self.revplan[length] = lal.CreateReverseREAL4FFTPlan(length, 1)
+		if length not in self.tvec:
+			self.tvec[length] = lal.CreateREAL4Vector(length)
+		if length not in self.fvec:
+			self.fvec[length] = lal.CreateCOMPLEX8Vector(length / 2 + 1)
+
+		self.fvec[length].data[:] = arr[:] / length
+		lal.REAL4ReverseFFT(self.tvec[length], self.fvec[length], self.revplan[length])
+
+		return numpy.array(self.tvec[length].data, dtype=float)
 
 class RandomSource(object):
 
-	def __init__(self, psd, horizons, SR = 1024, FL = 30, FH = 500):
+	def __init__(self, psd, horizons, SR = 2048, FL = 32, FH = 1024, DF = 32):
 
 		psd_data = psd.data.data
 		f = psd.f0 + numpy.arange(len(psd_data)) * psd.deltaF
@@ -1149,14 +1190,14 @@ class RandomSource(object):
 		psd = psd_data[int(FL / psd.deltaF): int(FH / psd.deltaF)]
 
 		psd = interpolate.interp1d(f, psd)
-		f = numpy.arange(FL, FH)
+		f = numpy.arange(FL, FH, DF)
 		PSD = psd(f)
 
-		self.t = numpy.arange(0, SR) / float(SR)
-		self.f = numpy.arange(-SR/2, SR/2)
+		self.t = numpy.arange(0, SR) / float(SR) / DF
+		self.f = numpy.arange(-SR/2, SR/2, DF)
 		psd = numpy.ones(len(self.f)) * 1e-30
-		psd[SR/2-FH:SR/2-FL] = PSD[::-1]
-		psd[SR/2+FL:SR/2+FH] = PSD
+		psd[(SR/2-FH)/DF:(SR/2-FL)/DF] = PSD[::-1]
+		psd[(SR/2+FL)/DF:(SR/2+FH)/DF] = PSD
 
 		self.psd = psd
 
@@ -1165,8 +1206,12 @@ class RandomSource(object):
 		self.responses = {"H1": lal.CachedDetectors[lal.LHO_4K_DETECTOR].response, "L1":lal.CachedDetectors[lal.LLO_4K_DETECTOR].response, "V1":lal.CachedDetectors[lal.VIRGO_DETECTOR].response}
 		self.locations = {"H1":lal.CachedDetectors[lal.LHO_4K_DETECTOR].location, "L1":lal.CachedDetectors[lal.LLO_4K_DETECTOR].location, "V1":lal.CachedDetectors[lal.VIRGO_DETECTOR].location}
 
-		self.w1 = self.waveform(0., 0.)
-		self.w2 = self.waveform(0., numpy.pi / 2.)
+		self.fft = FFT()
+		self.ifft = IFFT()
+
+		self.w1t = self.waveform(0., 0.)
+		self.w1 = self.fft(self.w1t)
+		self.w2 = self.fft(self.waveform(0., numpy.pi / 2.))
 
 	def __call__(self):
 		# We will draw uniformly in extrinsic parameters
@@ -1176,9 +1221,9 @@ class RandomSource(object):
 		RA = numpy.random.uniform(0.0,2.0*numpy.pi)
 		DEC = numpy.arcsin(numpy.random.uniform(-1.0,1.0))
 		PSI = numpy.random.uniform(0.0,2.0*numpy.pi)
-		D = numpy.random.power(2) * max(self.horizons.values()) * 2 # not! volume weighted - according to numpy doc, the distribution is drawn from arg -1
-		# since we are sampling in D instead of D^2, set the probability to scale with D
-		PROB = D
+		D = numpy.random.power(1) * max(self.horizons.values()) * 2 # not! volume weighted - according to numpy doc, the distribution is drawn from arg -1
+		# since we are sampling uniform in D instead of D^2, set the probability to scale with D
+		PROB = D**2
 		T = lal.LIGOTimeGPS(0)
 
 		# Derived quantities
@@ -1218,14 +1263,14 @@ class RandomSource(object):
 		return numpy.real((numpy.conj(w1) * w2).sum())
 
 	def matchfft(self, w1, w2):
-		return numpy.real(scipy.ifft(numpy.conj(scipy.fft(w1)) * scipy.fft(w2)))
+		return numpy.real(self.ifft(numpy.conj(w1) * w2))
 
 	def waveform(self, tc, phi):
 		a = abs(self.f)**(-7./6.)
 		a[len(a)/2] = 0
 		w = a * numpy.exp(numpy.pi * 2 * self.f * 1j * tc + phi * 1.j) / self.psd **.5
-		w = numpy.real(scipy.ifft(w))
-		return w / self.match(w, w)**.5
+		w = numpy.real(fft(w))
+		return numpy.array(w / self.match(w, w)**.5, dtype=float)
 
 	def noise(self):
 		return numpy.random.randn(len(self.f))
@@ -1236,7 +1281,7 @@ class RandomSource(object):
 	def sample_from_matched_filter(self, snr):
 		while True:
 			n = self.noise()
-			d = self.inject(self.w1, n, snr)
+			d = self.fft(self.inject(self.w1t, n, snr))
 			m1 = self.matchfft(self.w1,d)
 			m2 = self.matchfft(self.w2,d)
 			snr = ((m1**2 + m2**2)**.5)
@@ -1282,22 +1327,23 @@ class InspiralExtrinsicParameters(object):
 		out = {}
 		def boundary(ifos):
 			ifos = sorted(ifos)
+			assert 1 <= len(ifos) <= 3
 			lower = []
 			upper = []
 			max_splits = []
 			# we have pairs of dt and dphi
-			for pair in itertools.combinations(ifos, 2):
+			for pair in list(itertools.combinations(ifos, 2))[:2]:
 				lower.append(-0.032)
 				upper.append(0.032)
-				max_splits.append(128)
-			for pair in itertools.combinations(ifos, 2):
+				max_splits.append(64)
+			for pair in list(itertools.combinations(ifos, 2))[:2]:
 				lower.append(-numpy.pi)
 				upper.append(numpy.pi)
-				max_splits.append(64)
+				max_splits.append(32)
 			for ifo in ifos:
 				lower.append(0)
 				upper.append(numpy.pi / 2.)
-				max_splits.append(128)
+				max_splits.append(64)
 			return lower, upper, max_splits
 		for combo in self.combos:
 			out[combo] = boundary(combo)
@@ -1305,12 +1351,13 @@ class InspiralExtrinsicParameters(object):
 
 	def pointfunc(self, time, phase, snr):
 		ifos = sorted(time)
+		assert 1 <= len(ifos) <= 3
 		point = []
 		# first time
-		for pair in itertools.combinations(ifos, 2):
+		for pair in list(itertools.combinations(ifos, 2))[:2]:
 			point.append(time[pair[0]] - time[pair[1]])
 		# then phase
-		for pair in itertools.combinations(ifos, 2):
+		for pair in list(itertools.combinations(ifos, 2))[:2]:
 			unwrapped = numpy.unwrap([phase[pair[0]], phase[pair[1]]])
 			point.append(unwrapped[0] - unwrapped[1])
 		for ifo in ifos: