diff --git a/gstlal-inspiral/bin/gstlal_inspiral b/gstlal-inspiral/bin/gstlal_inspiral
index 0b405c36bd4e2ac007d3f56392495c1e909d19a7..895e7bdb50ca65355f59f68ff139e92fb77a7c7f 100755
--- a/gstlal-inspiral/bin/gstlal_inspiral
+++ b/gstlal-inspiral/bin/gstlal_inspiral
@@ -177,6 +177,7 @@ from gstlal import inspiral_pipe
 from gstlal import lloidhandler
 from gstlal import lloidparts
 from gstlal import pipeparts
+from gstlal import reference_psd
 from gstlal import servicediscovery
 from gstlal import simulation
 
@@ -524,6 +525,34 @@ class OneTimeSignalHandler(object):
 			print >>sys.stderr, "*** received SIG %d %d times... ***" % (signum, self.count)
 
 
+#
+# =============================================================================
+#
+#                              Horizon Distances
+#
+# =============================================================================
+#
+
+
+def make_horizon_distance_func(banks, sngl_inspiral_table):
+	# span is [10 Hz, 0.85 * Nyquist frequency]
+	# find the Nyquist frequency for the PSD to be used for each
+	# instrument.  require them to all match
+	nyquists = set(max(rate for bank in banklist for rate in bank.get_rates()) // 2 for instrument, banklist in banks.items())
+	assert len(nyquists) == 1
+	# assume default 32 s PSD.  this is not required to be correct, but
+	# for best accuracy it should not be larger than the true value and
+	# for best performance it should not be smaller than the true
+	# value.
+	deltaF = 1. / 32.
+	# pick (m1, m2) from the median template ranked by horizon distance
+	# to provide the canonical waveform model
+	# FIXME:  look up the mass function on which the horizon distance
+	# depends
+	median_row = sorted(sngl_inspiral_table, key = lambda row: row.mtotal)[len(sngl_inspiral_table) // 2]
+	return reference_psd.HorizonDistance(10.0, 0.85 * max(nyquists), deltaF, median_row.mass1, median_row.mass2)
+
+
 #
 # =============================================================================
 #
@@ -795,6 +824,7 @@ for output_file_number, (svd_bank_url_dict, output_url, ranking_stat_output_url,
 			verbose = options.verbose
 		),
 		rankingstat = rankingstat,
+		horizon_distance_func = make_horizon_distance_func(banks, sngl_inspiral_table),
 		gracedbwrapper = inspiral.GracedBWrapper(
 			instruments = rankingstat.instruments,
 			far_threshold = options.gracedb_far_threshold,
diff --git a/gstlal-inspiral/python/lloidhandler.py b/gstlal-inspiral/python/lloidhandler.py
index 2b1d4ec223c90d837eb7d93727b19db05a6e4f96..7b9b79279908b01e582e46ec71ab730c3685ac35 100644
--- a/gstlal-inspiral/python/lloidhandler.py
+++ b/gstlal-inspiral/python/lloidhandler.py
@@ -86,7 +86,6 @@ from gstlal import bottle
 from gstlal import far
 from gstlal import inspiral
 from gstlal import pipeio
-from gstlal import reference_psd
 from gstlal import simplehandler
 from gstlal import streamthinca
 import lal
@@ -589,7 +588,7 @@ class Handler(simplehandler.Handler):
 	dumps of segment information, trigger files and background
 	distribution statistics.
 	"""
-	def __init__(self, mainloop, pipeline, coincs_document, rankingstat, gracedbwrapper, zerolag_rankingstatpdf_url = None, rankingstatpdf_url = None, ranking_stat_output_url = None, ranking_stat_input_url = None, likelihood_snapshot_interval = None, thinca_interval = 50.0, min_log_L = None, sngls_snr_threshold = None, tag = "", kafka_server = "10.14.0.112:9092", verbose = False):
+	def __init__(self, mainloop, pipeline, coincs_document, rankingstat, horizon_distance_func, gracedbwrapper, zerolag_rankingstatpdf_url = None, rankingstatpdf_url = None, ranking_stat_output_url = None, ranking_stat_input_url = None, likelihood_snapshot_interval = None, thinca_interval = 50.0, min_log_L = None, sngls_snr_threshold = None, tag = "", kafka_server = "10.14.0.112:9092", verbose = False):
 		"""!
 		@param mainloop The main application's event loop
 		@param pipeline The gstreamer pipeline that is being
@@ -618,7 +617,6 @@ class Handler(simplehandler.Handler):
 		# FIXME:   detangle this
 		self.gracedbwrapper.lock = self.lock
 
-		self.kafka_server = kafka_server
 		self.eye_candy = EyeCandy(rankingstat.instruments, self.kafka_server, self.tag, pipeline)
 		# FIXME:   detangle this
 		self.eye_candy.lock = self.lock
@@ -744,6 +742,12 @@ class Handler(simplehandler.Handler):
 			self.zerolag_rankingstatpdf = None
 		self.zerolag_rankingstatpdf_url = zerolag_rankingstatpdf_url
 
+		#
+		# set horizon distance calculator
+		#
+
+		self.horizon_distance_func = horizon_distance_func
+
 		#
 		# rankingstatpdf contains the RankingStatPDF object (loaded
 		# from rankingstatpdf_url) used to initialize the FAPFAR
@@ -826,10 +830,7 @@ class Handler(simplehandler.Handler):
 				# effect of vetoing candidates from these
 				# times.
 				if stability > 0.3:
-					# FIXME:  get canonical masses from
-					# the template bank bin that we're
-					# analyzing
-					horizon_distance = reference_psd.HorizonDistance(10.0, 0.85 * (psd.f0 + (len(psd.data.data) - 1) * psd.deltaF), psd.deltaF, 1.4, 1.4)(psd, 8.0)[0]
+					horizon_distance = self.horizon_distance_func(psd, 8.0)[0]
 					assert not (math.isnan(horizon_distance) or math.isinf(horizon_distance))
 				else:
 					horizon_distance = 0.