7 from glue
import iterutils
8 from glue
import segments
9 from lal
import LIGOTimeGPS
12 default_timestamp_fuzz = 1e-9
13 default_sample_fuzz = 1e-15
22 COMPARE_FLAGS_EXACT_GAPS = 1
24 COMPARE_FLAGS_ZERO_IS_GAP = 2
26 COMPARE_FLAGS_ALLOW_STARTSTOP_MISALIGN = 4
29 COMPARE_FLAGS_DEFAULT = 0
37 def load_file(fobj, transients = (0.0, 0.0)):
38 stream = (line.strip()
for line
in fobj)
39 stream = (line.split()
for line
in stream
if line
and not line.startswith(
"#"))
40 lines = [(LIGOTimeGPS(line[0]),) + tuple(map(float, line[1:]))
for line
in stream]
41 assert lines,
"no data"
42 channel_count_plus_1 = len(lines[0])
43 assert all(len(line) == channel_count_plus_1
for line
in lines),
"not all lines have the same channel count"
44 for t1, t2
in itertools.izip((line[0]
for line
in lines), (line[0]
for line
in lines[1:])):
45 assert t2 > t1,
"timestamps not in order @ t = %s s" % str(t2)
46 start = lines[0][0] + transients[0]
47 stop = lines[-1][0] - transients[-1]
48 iterutils.inplace_filter(
lambda line: start <= line[0] <= stop, lines)
49 assert lines,
"transients remove all data"
53 def identify_gaps(lines, timestamp_fuzz = default_timestamp_fuzz, sample_fuzz = default_sample_fuzz, flags = COMPARE_FLAGS_DEFAULT):
57 dt = min(float(line1[0] - line0[0])
for line0, line1
in itertools.izip(lines, lines[1:]))
58 dt = 1.0 / round(1.0 / dt)
62 if flags & COMPARE_FLAGS_ZERO_IS_GAP:
63 sample_fuzz *= max(max(abs(x)
for x
in line[1:])
for line
in lines)
65 gaps = segments.segmentlist()
66 for i, line
in enumerate(lines):
67 if i
and (line[0] - lines[i - 1][0]) - dt > timestamp_fuzz * 2:
68 gaps.append(segments.segment((lines[i - 1][0] + dt, line[0])))
69 if flags & COMPARE_FLAGS_ZERO_IS_GAP
and all(abs(x) <= sample_fuzz
for x
in line[1:]):
71 gaps.append(segments.segment((line[0], lines[i + 1][0]
if i + 1 < len(lines)
else line[0] + (line[0] - lines[i - 1][0]))))
72 return gaps.coalesce()
75 def compare_fobjs(fobj1, fobj2, transients = (0.0, 0.0), timestamp_fuzz = default_timestamp_fuzz, sample_fuzz = default_sample_fuzz, flags = COMPARE_FLAGS_DEFAULT):
76 timestamp_fuzz = LIGOTimeGPS(timestamp_fuzz)
79 lines1 = load_file(fobj1, transients = transients)
80 lines2 = load_file(fobj2, transients = transients)
81 assert len(lines1[0]) == len(lines2[0]),
"files do not have same channel count"
84 if flags & COMPARE_FLAGS_ALLOW_STARTSTOP_MISALIGN:
85 lines1 = [line
for line
in lines1
if lines2[0][0] <= line[0] <= lines2[-1][0]]
86 assert lines1,
"time intervals do not overlap"
87 lines2 = [line
for line
in lines2
if lines1[0][0] <= line[0] <= lines1[-1][0]]
88 assert lines2,
"time intervals do not overlap"
91 gaps1 = identify_gaps(lines1, timestamp_fuzz = timestamp_fuzz, sample_fuzz = sample_fuzz, flags = flags)
92 gaps2 = identify_gaps(lines2, timestamp_fuzz = timestamp_fuzz, sample_fuzz = sample_fuzz, flags = flags)
93 if flags & COMPARE_FLAGS_EXACT_GAPS:
94 difference = gaps1 ^ gaps2
95 iterutils.inplace_filter(
lambda seg: abs(seg) > timestamp_fuzz, difference)
96 assert not difference,
"gap discrepancy: 1 ^ 2 = %s" % str(difference)
99 sample_fuzz *= max(max(abs(x)
for x
in line[1:])
for line
in itertools.chain(lines1, lines2))
101 lines1 = iter(lines1)
102 lines2 = iter(lines2)
104 line1 = lines1.next()
105 line2 = lines2.next()
108 if abs(line1[0] - line2[0]) <= timestamp_fuzz:
109 for val1, val2
in zip(line1[1:], line2[1:]):
110 assert abs(val1 - val2) <= sample_fuzz,
"values disagree @ t = %s s" % str(line1[0])
111 line1 = lines1.next()
112 line2 = lines2.next()
113 elif line1[0] < line2[0]
and line1[0]
in gaps2:
114 line1 = lines1.next()
115 elif line2[0] < line1[0]
and line2[0]
in gaps1:
116 line2 = lines2.next()
118 raise AssertionError(
"timestamp misalignment @ %s s and %s s" % (str(line1[0]), str(line2[0])))
119 except StopIteration:
124 def compare(filename1, filename2, *args, **kwargs):
126 compare_fobjs(open(filename1), open(filename2), *args, **kwargs)
127 except AssertionError
as e:
128 raise AssertionError(
"%s <--> %s: %s" % (filename1, filename2, str(e)))
136 if __name__ ==
"__main__":
137 from optparse
import OptionParser
138 parser = OptionParser()
139 parser.add_option(
"--compare-exact-gaps", action =
"store_const", const = COMPARE_FLAGS_EXACT_GAPS, default = 0)
140 parser.add_option(
"--compare-zero-is-gap", action =
"store_const", const = COMPARE_FLAGS_ZERO_IS_GAP, default = 0)
141 parser.add_option(
"--compare-allow-startstop-misalign", action =
"store_const", const = COMPARE_FLAGS_ALLOW_STARTSTOP_MISALIGN, default = 0)
142 parser.add_option(
"--timestamp-fuzz", metavar =
"seconds", type =
"float", default = default_timestamp_fuzz)
143 parser.add_option(
"--sample-fuzz", metavar =
"fraction", type =
"float", default = default_sample_fuzz)
144 options, (filename1, filename2) = parser.parse_args()
145 compare(filename1, filename2, timestamp_fuzz = options.timestamp_fuzz, sample_fuzz = options.sample_fuzz, flags = options.compare_exact_gaps | options.compare_zero_is_gap | options.compare_allow_startstop_misalign)