scriptroute 0.4.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1260 @@
1
+ #!/usr/bin/awk -f
2
+
3
+ # "FIXCLOCK" COPYRIGHT NOTICE, LICENSE AND DISCLAIMER.
4
+ #
5
+ # Copyright 2002 by Raphael S. Ryger, Dept. of Computer Science, Yale University
6
+ #
7
+ # Permission to use, copy, modify, and distribute this software and its
8
+ # documentation for any purpose and without fee is hereby granted, provided
9
+ # that the above copyright notice appear in all copies and that both the
10
+ # copyright notice and this permission notice and warranty disclaimer appear
11
+ # in supporting documentation, and that the name of the author not be used
12
+ # in advertising or publicity pertaining to distribution of the software
13
+ # without specific, written prior permission.
14
+ #
15
+ # The author disclaims all warranties with regard to this software, including
16
+ # all implied warranties of merchantability and fitness. In no event shall he
17
+ # be liable for any special, indirect or consequential damages or any damages
18
+ # whatsoever resulting from loss of use, data or profits, whether in an action
19
+ # of contract, negligence or other tortious action, arising out of or in
20
+ # connection with the use or performance of this software.
21
+
22
+ # Usage: fixclock <datafile>
23
+ #
24
+ # The behavior of this script is modulated by parameters derived from the
25
+ # command line, else from the environment, else from the defaults following.
26
+ # (In sh and ksh, environment variable settings may be prepended in the command
27
+ # line to the invocation of the script, to apply only to that invocation.)
28
+ # parameter default
29
+ # --------- -------
30
+ # parsing group:
31
+ # datalinepattern null (effectively, all lines)
32
+ # pastdatapattern null (effectively, no such line)
33
+ # fieldseparator null (effectively, whitespace)
34
+ # hostfieldnum null (effectively, all data lines)
35
+ # sentfieldnum *must* be specified
36
+ # receivedfieldnum *must* be specified
37
+ # bouncedfieldnum *must* be specified
38
+ # target extracted from (the string) <datafile>
39
+ #
40
+ # tuning group:
41
+ # segmentsecs 12
42
+ # intrastretchmaxerr 2
43
+ # interstretchmaxerr 10
44
+ # sourceskew 0
45
+ #
46
+ # verbosity group (binary values: null or 0 is false; else true):
47
+ # showall null
48
+ # showtarget null
49
+ # showreadings null
50
+ # showminima null
51
+ # showintrastretchtests null
52
+ # showonewaystretches null
53
+ # showstdroundtrips null
54
+ # showquality null
55
+ # showcompositestretches null
56
+ # showregions null
57
+ # showcoverage null
58
+ #
59
+ # data file side effects:
60
+ # writefixeddir null
61
+ # writeorigfixedplotdir null
62
+ #
63
+ # Notes on parameters:
64
+ # datalinepattern AWK pattern picking out the data lines. The data lines
65
+ # need not be successive.
66
+ # pastdatapattern AWK pattern recognizing a line which is past the relevent
67
+ # data, upon which the script may wind up and exit. (This
68
+ # pattern will only be consulted if a line does not match
69
+ # datalinepattern.)
70
+ # target Any IP address for which there are readings in the file
71
+ # may have its timing data analyzed and revised, not only the
72
+ # nominal "target" of the data.
73
+ # segmentsecs Segments should be long enough to make it likely (not
74
+ # necessarily definite) that segment minima for one-way (not
75
+ # necessarily round-trip) transit times are the ideal,
76
+ # congestion-less, times;
77
+ # but, on the other hand, short enough to allow four consecutive
78
+ # jump-less segments, in absence of congestion, even for target
79
+ # clocks that are reset by jumps on a very frequent schedule.
80
+ # The default, 12 seconds, allows for jump resets as frequent
81
+ # as once per minute.
82
+ # intrastretchmaxerr The maximum absolute millisecond difference between an
83
+ # actual segment minimum one-way transit time and a linearly
84
+ # interpolated transit time for that timepoint, in a stretch of
85
+ # segments to be considered adjustment-less.
86
+ # interstretchmaxerr The maximum absolute millisecond difference between an
87
+ # actual segment minimum one-way transit time and a linearly
88
+ # interpolated transit time for that timepoint, in considering
89
+ # a pair of "adjustment-less" stretches for coalescing.
90
+ # sourceskew A guess at the skew of the source machine's clock with respect
91
+ # to standard time. If the guess is good, considerations of
92
+ # the sign of apparent target skew can become meaningful after
93
+ # compensation for the guessed source skew. But we avoid them
94
+ # anyway! We do innocuously do the compensation.
95
+ # showall Turns on all the following "show" parameters, displaying all
96
+ # available information -- unless explicitly overridden for
97
+ # individual "show" parameters by setting them to 0.
98
+ # showtarget Shows the target IP address at the top of the output.
99
+ # showreadings Displays preliminarily-normalized data data for the target:
100
+ # "there" time, "back" time, round-trip time.
101
+ # showminima Displays the segment minima of the transit times, as well as
102
+ # the global minima of these times.
103
+ # showintrastretchtests Displays the skews and errors involved in the
104
+ # collinearity tests for adding segments to stretches.
105
+ # showonewaystretches Displays the jump-less stretches, with their skews,
106
+ # for each direction, as determined without regard to the other
107
+ # direction.
108
+ # showstdroundtrips Displays the data and conclusions for the individual
109
+ # local inferences of "standard round-trip time". (The average
110
+ # is what we finally use.)
111
+ # showquality Displays the ratio of the inferred "standard" round-trip
112
+ # time to the measured global minimum round-trip time. This
113
+ # is around 1.0 with "good" data, and drops with persistent
114
+ # congestion and, especially, clock craziness.
115
+ # showcompositestretches Displays the final stretch information, after the
116
+ # one-way stretch information from the two directions has been
117
+ # synthesized into a single coherent picture.
118
+ # showregions Displays the final map that will drive the correction pass.
119
+ # The segment artifice is gone. Regions are defined by source
120
+ # "sent" timestamps.
121
+ # showcoverage Displays the total time coverage of the identified regions,
122
+ # hence of the fixed data (if requested), relative to the
123
+ # duration of the probing run.
124
+ # writefixeddir The value is taken as the name of the directory to which to
125
+ # write the fixed file as <datafile>.fix, and the rejected
126
+ # (unfixable) record file as <datafile>.rej .
127
+ # (A command-line setting of "//" may be used to override an
128
+ # environment setting and will be interpreted as null.)
129
+ # writeorigfixedplotdir The value is taken as the name of the directory
130
+ # to which to write three space-delimited data files -- one
131
+ # isolating the (relevant target) triples of the original
132
+ # data file, another containing the fixed triples, and a
133
+ # third containing the rejected triples, those that could not
134
+ # be fixed -- named, respectively, <datafile>.plot,
135
+ # <datafile>.fix.plot, and <datafile>.rej.plot . The shifting
136
+ # and scaling of the data in these .plot files is chosen for
137
+ # effectiveness of resulting graphs, and for compatibility
138
+ # with the analytical text output.
139
+ # (A command-line setting of "//" may be used to override an
140
+ # environment setting and will be interpreted as null.)
141
+
142
+ # Assumptions in the analysis:
143
+ # - There is no change in the routing to the target, or in the networking
144
+ # infrastructure along the path, during the course of the data.
145
+ # - *Regular* clock jumps do not happen more frequently than once per
146
+ # 5 * segmentsecs.
147
+ # - Congestion-free one-way transits occur often, so that maximal runs of
148
+ # at least 4 consecutive segments with congestion-free transit times in
149
+ # the same direction bracket most of each adjustment-free portion of the
150
+ # data.
151
+ # - There is at least one overlap of stretches of collinear-min-OTT
152
+ # segments for the two directions;
153
+ # else, there is at least one (reasonably) congestion-free round trip
154
+ # in the data.
155
+ # Adjustments by high-absolute-value temporary skew require low congestion as
156
+ # they occur for proper analysis by the present method.
157
+
158
+ function setparameters( default) {
159
+ # default["datalinepattern"] = null
160
+ # default["pastdatapattern"] = null
161
+ # default["fieldseparator"] = null
162
+ # default["hostfieldnum"] = null
163
+ # default["sentfieldnum"] = *must* be specified
164
+ # default["receivedfieldnum"] = *must* be specified
165
+ # default["bouncedfieldnum"] = *must* be specified
166
+ # default["target"] = targetfromfilename()
167
+ default["segmentsecs"] = 12
168
+ default["intrastretchmaxerr"] = 2
169
+ default["interstretchmaxerr"] = 10
170
+ default["sourceskew"] = 0
171
+ # default["showall"] = null
172
+ # default["showtarget"] = null
173
+ # default["showreadings"] = null
174
+ # default["showminima"] = null
175
+ # default["showintrastretchtests"] = null
176
+ # default["showonewaystretches"] = null
177
+ # default["showstdroundtrips"] = null
178
+ # default["showstdquality"] = null
179
+ # default["showcompositestretches"] = null
180
+ # default["showregions"] = null
181
+ # default["showcoverage"] = null
182
+ # default["writefixeddir"] = null
183
+ # default["writeorigfixedplotdir"] = null
184
+
185
+ (datalinepattern != "") \
186
+ || datalinepattern = ENVIRON["datalinepattern"]
187
+ (pastdatapattern != "") \
188
+ || pastdatapattern = ENVIRON["pastdatapattern"]
189
+ (fieldseparator != "") \
190
+ || fieldseparator = ENVIRON["fieldseparator"]
191
+ (hostfieldnum != "") \
192
+ || hostfieldnum = ENVIRON["hostfieldnum"]
193
+ (sentfieldnum != "") \
194
+ || sentfieldnum = ENVIRON["sentfieldnum"]
195
+ (receivedfieldnum != "") \
196
+ || receivedfieldnum = ENVIRON["receivedfieldnum"]
197
+ (bouncedfieldnum != "") \
198
+ || bouncedfieldnum = ENVIRON["bouncedfieldnum"]
199
+ (target != "") \
200
+ || target = ENVIRON["target"]
201
+ (segmentsecs != "") \
202
+ || ((segmentsecs = ENVIRON["segmentsecs"]) != "") \
203
+ || segmentsecs = default["segmentsecs"]
204
+ (intrastretchmaxerr != "") \
205
+ || ((intrastretchmaxerr = ENVIRON["intrastretchmaxerr"]) != "") \
206
+ || intrastretchmaxerr = default["intrastretchmaxerr"]
207
+ (interstretchmaxerr != "") \
208
+ || ((interstretchmaxerr = ENVIRON["interstretchmaxerr"]) != "") \
209
+ || interstretchmaxerr = default["interstretchmaxerr"]
210
+ (sourceskew != "") \
211
+ || ((sourceskew = ENVIRON["sourceskew"]) != "") \
212
+ || sourceskew = default["sourceskew"]
213
+ (showall != "") \
214
+ || showall = ENVIRON["showall"]
215
+ (showtarget != "") \
216
+ || ((showtarget = ENVIRON["showtarget"]) != "") \
217
+ || showtarget = showall
218
+ (showreadings != "") \
219
+ || ((showreadings = ENVIRON["showreadings"]) != "") \
220
+ || showreadings = showall
221
+ (showminima != "") \
222
+ || ((showminima = ENVIRON["showminima"]) != "") \
223
+ || showminima = showall
224
+ (showintrastretchtests != "") \
225
+ || ((showintrastretchtests = ENVIRON["showintrastretchtests"]) \
226
+ != "") \
227
+ || showintrastretchtests = showall
228
+ (showonewaystretches != "") \
229
+ || ((showonewaystretches = ENVIRON["showonewaystretches"]) \
230
+ != "") \
231
+ || showonewaystretches = showall
232
+ (showstdroundtrips != "") \
233
+ || ((showstdroundtrips = ENVIRON["showstdroundtrips"]) \
234
+ != "") \
235
+ || showstdroundtrips = showall
236
+ (showquality != "") \
237
+ || ((showquality = ENVIRON["showquality"]) \
238
+ != "") \
239
+ || ((showstdroundtrips || showcompositestretches) &&
240
+ showquality = 1) \
241
+ || showquality = showall
242
+ (showcompositestretches != "") \
243
+ || ((showcompositestretches = ENVIRON["showcompositestretches"]) \
244
+ != "") \
245
+ || showcompositestretches = showall
246
+ (showregions != "") \
247
+ || ((showregions = ENVIRON["showregions"]) != "") \
248
+ || showregions = showall
249
+ (showcoverage != "") \
250
+ || ((showcoverage = ENVIRON["showcoverage"]) != "") \
251
+ || showcoverage = showall
252
+ (writefixeddir != "" && writefixeddir != "//") \
253
+ || writefixeddir = ENVIRON["writefixeddir"]
254
+ (writeorigfixedplotdir != "" && writeorigfixedplotdir != "//") \
255
+ || writeorigfixedplotdir = ENVIRON["writeorigfixedplotdir"]
256
+ }
257
+
258
+ function targetfromfilename( fromfilename) {
259
+ fromfilename = FILENAME
260
+ sub(/^.*\//, "", fromfilename)
261
+ sub(/^[^0-9.]*/, "", fromfilename)
262
+ sub(/[^0-9.].*/, "", fromfilename)
263
+ sub(/^\./, "", fromfilename)
264
+ sub(/\.$/, "", fromfilename)
265
+ return fromfilename
266
+ }
267
+
268
+ function abs(number) {
269
+ return (number < 0) ? -number : number
270
+ }
271
+
272
+ function fixbyteorder (integer, revinteger, byte, ii) {
273
+ if (!needs_bytes_reversed) return integer
274
+ revinteger = 0
275
+ for (ii = 0; ii < 4; ++ii) {
276
+ byte = integer % 256
277
+ revinteger = revinteger*256 + byte
278
+ integer = (integer - byte) / 256
279
+ }
280
+ return revinteger
281
+ }
282
+
283
+ # Note: the variable tb holds the "there or back" direction code:
284
+ # 0 is "there" (outbound), 1 is "back" (inbound).
285
+
286
+ function segmentboundary() {
287
+ if (showminima)
288
+ print "Segment", segments \
289
+ ": minthere", min[0,segments] \
290
+ "; minback", min[1,segments] \
291
+ "; minroundtrip, fictive",
292
+ (min[0,segments] + min[1,segments]) \
293
+ ", actual", min[2,segments]
294
+ if (min[0,segments] < globalminthere)
295
+ globalminthere = min[0,segments]
296
+ if (min[1,segments] < globalminback)
297
+ globalminback = min[1,segments]
298
+ if (min[2,segments] < globalminroundtrip)
299
+ globalminroundtrip = min[2,segments]
300
+
301
+ # For each of the "there" and "back" directions, examine the
302
+ # collinearity of the following four points:
303
+ # the respective minimum of the respective candidate/extended
304
+ # stretch's startseg;
305
+ # the respective minimum of the mid-stretch segment;
306
+ # the respective minimum of the immediate predecessor of the
307
+ # present (old) segment; and
308
+ # the respective minimum of the present (old) segment.
309
+ # Note that in testing the first foursome for a (potential) stretch,
310
+ # it could be that the first two and last two minima immediately
311
+ # straddle segment boundaries, in which case this instance of the
312
+ # test establishes almost nothing; but if so, the minima collinearity
313
+ # test for the next segment minimum will be all the more stringent.
314
+ for (tb = 0; tb < 2; ++tb) {
315
+ if (segments - this_stretch_startseg[tb] < 3) continue
316
+ seg1 = this_stretch_startseg[tb]
317
+ seg2 = int((seg1 + segments) / 2)
318
+ seg3 = segments - 1
319
+ # for "there", we use the "sent" timestamps;
320
+ # for "back", we use the "bounced" timestamps;
321
+ if (tb == 0) {
322
+ if (min[tb,segments] > min[tb,seg1]) {
323
+ use_sent1 = min_sent_late[tb,seg1]
324
+ use_sent2 = min_sent_late[tb,seg2]
325
+ use_sent3 = min_sent_late[tb,seg3]
326
+ use_sent = min_sent_late[tb,segments]
327
+ }
328
+ else {
329
+ use_sent1 = min_sent[tb,seg1]
330
+ use_sent2 = min_sent[tb,seg2]
331
+ use_sent3 = min_sent[tb,seg3]
332
+ use_sent = min_sent[tb,segments]
333
+ }
334
+ min2shouldbe = ((use_sent - use_sent2) * min[tb,seg1] + \
335
+ (use_sent2 - use_sent1) * min[tb,segments]) / \
336
+ (use_sent - use_sent1)
337
+ min3shouldbe = ((use_sent - use_sent3) * min[tb,seg1] + \
338
+ (use_sent3 - use_sent1) * min[tb,segments]) / \
339
+ (use_sent - use_sent1)
340
+ this_skew = (min[tb,segments] - min[tb,seg1]) / \
341
+ (use_sent - use_sent1)
342
+ }
343
+ else {
344
+ if (min[tb,segments] > min[tb,seg1]) {
345
+ use_bounced1 = min_bounced_late[tb,seg1]
346
+ use_bounced2 = min_bounced_late[tb,seg2]
347
+ use_bounced3 = min_bounced_late[tb,seg3]
348
+ use_bounced = min_bounced_late[tb,segments]
349
+ }
350
+ else {
351
+ use_bounced1 = min_bounced[tb,seg1]
352
+ use_bounced2 = min_bounced[tb,seg2]
353
+ use_bounced3 = min_bounced[tb,seg3]
354
+ use_bounced = min_bounced[tb,segments]
355
+ }
356
+ min2shouldbe = \
357
+ ((use_bounced - use_bounced2) * min[tb,seg1] + \
358
+ (use_bounced2 - use_bounced1) * min[tb,segments]) / \
359
+ (use_bounced - use_bounced1)
360
+ min3shouldbe = \
361
+ ((use_bounced - use_bounced3) * min[tb,seg1] + \
362
+ (use_bounced3 - use_bounced1) * min[tb,segments]) / \
363
+ (use_bounced - use_bounced1)
364
+ this_skew = (min[tb,segments] - min[tb,seg1]) / \
365
+ (use_bounced - use_bounced1)
366
+ }
367
+ min2error = min[tb,seg2] - min2shouldbe
368
+ min3error = min[tb,seg3] - min3shouldbe
369
+ if (showintrastretchtests) {
370
+ print " segment", seg2, "min" thereback[tb], "off by",
371
+ min2error ",",
372
+ "segment", seg3, "min" thereback[tb], "off by",
373
+ min3error
374
+ print " " thereback[tb], "skew from segment", seg1 ": ",
375
+ this_skew
376
+ }
377
+ # act on the collinearity test results
378
+ if (abs(min2error) <= intrastretchmaxerr &&
379
+ abs(min3error) <= intrastretchmaxerr) {
380
+ # collinearity test good
381
+ if (in_stretch[tb]) {
382
+ # extend existing (confirmed) stretch with present
383
+ # (old) segment
384
+ stretch_index[tb,segments] = stretches[tb]
385
+ stretch_endseg[tb,stretches[tb]] = segments
386
+ }
387
+ else {
388
+ # confirm candidate stretch
389
+ in_stretch[tb] = 1
390
+ ++stretches[tb]
391
+ stretch_index[tb,segments-3] = \
392
+ stretch_index[tb,segments-2] = \
393
+ stretch_index[tb,segments-1] = \
394
+ stretch_index[tb,segments] = stretches[tb]
395
+ stretch_startseg[tb,stretches[tb]] = segments - 3
396
+ stretch_endseg[tb,stretches[tb]] = segments
397
+ }
398
+ stretch_skew[tb,stretches[tb]] = this_skew
399
+ if (in_data == 2)
400
+ # no more readings: adjust choices of minimum times ...
401
+ if (min[tb,segments] > min[tb,seg1])
402
+ if (tb == 0)
403
+ for (ii = seg1; ii <= segments; ++ii)
404
+ min_sent[tb,ii] = min_sent_late[tb,ii]
405
+ else
406
+ for (ii = seg1; ii <= segments; ++ii)
407
+ min_bounced[tb,ii] = min_bounced_late[tb,ii]
408
+ }
409
+ else {
410
+ # collinearity test bad
411
+ if (in_stretch[tb]) {
412
+ # existing (confirmed) stretch cannot be extended:
413
+ # adjust choices of minimum times ...
414
+ if (min[tb,segments] > min[tb,seg1])
415
+ if (tb == 0)
416
+ for (ii = seg1; ii <= segments - 1; ++ii)
417
+ min_sent[tb,ii] = min_sent_late[tb,ii]
418
+ else
419
+ for (ii = seg1; ii <= segments - 1; ++ii)
420
+ min_bounced[tb,ii] = min_bounced_late[tb,ii]
421
+ # ... and start a new candidate stretch
422
+ in_stretch[tb] = 0
423
+ this_stretch_startseg[tb] = segments
424
+ }
425
+ else
426
+ # shift candidate stretch start segment
427
+ ++this_stretch_startseg[tb]
428
+ }
429
+ }
430
+ }
431
+
432
+ function compatiblecounterskews(there_skew, back_skew) {
433
+ # Test: is average of the two skews off from 0 no more than
434
+ # intrastretchmaxerr / (2 * segmentsec * 1000)
435
+ # In the collinearity tests we are essentially allowing for an error
436
+ # no greater than this in the individual skews, hence in their average.
437
+ return (abs(there_skew + back_skew) <= \
438
+ intrastretchmaxerr / (segmentsecs * 1000))
439
+ }
440
+
441
+ function continuation(first_tb, first_seg, second_tb, second_seg,
442
+ ii, x, y, zz) {
443
+ if (showcompositestretches)
444
+ print "continuation:", thereback[first_tb], "seg", first_seg ",",
445
+ thereback[second_tb], "seg", second_seg
446
+ first_stretch_startseg = \
447
+ stretch_startseg[first_tb,stretch_index[first_tb,first_seg]]
448
+ first_stretch_endseg = \
449
+ stretch_endseg[first_tb,stretch_index[first_tb,first_seg]]
450
+ second_stretch_startseg = \
451
+ stretch_startseg[second_tb,stretch_index[second_tb,second_seg]]
452
+ second_stretch_endseg = \
453
+ stretch_endseg[second_tb,stretch_index[second_tb,second_seg]]
454
+ if (first_tb == 0) {
455
+ x[1] = min_sent[0,first_stretch_startseg]
456
+ y[1] = min[0,first_stretch_startseg]
457
+ x[2] = min_sent[0,first_stretch_endseg]
458
+ y[2] = min[0,first_stretch_endseg]
459
+ }
460
+ else {
461
+ x[1] = min_bounced[1,first_stretch_startseg] - stdroundtrip
462
+ y[1] = stdroundtrip - min[1,first_stretch_startseg]
463
+ x[2] = min_bounced[1,first_stretch_endseg] - stdroundtrip
464
+ y[2] = stdroundtrip - min[1,first_stretch_endseg]
465
+ }
466
+ if (second_tb == 0) {
467
+ x[3] = min_sent[0,second_stretch_startseg]
468
+ y[3] = min[0,second_stretch_startseg]
469
+ x[4] = min_sent[0,second_stretch_endseg]
470
+ y[4] = min[0,second_stretch_endseg]
471
+ }
472
+ else {
473
+ x[3] = min_bounced[1,second_stretch_startseg] - stdroundtrip
474
+ y[3] = stdroundtrip - min[1,second_stretch_startseg]
475
+ x[4] = min_bounced[1,second_stretch_endseg] - stdroundtrip
476
+ y[4] = stdroundtrip - min[1,second_stretch_endseg]
477
+ }
478
+ if (showcompositestretches) {
479
+ print " y[1]", y[1], "y[2]", y[2], "y[3]", y[3], "y[4]", y[4]
480
+ print " x[1]", x[1], "x[2]", x[2], "x[3]", x[3], "x[4]", x[4]
481
+ }
482
+
483
+ # take the "x" extrema as endpoints
484
+ reordered = 0
485
+ for (ii = 1; ii <= 4; ++ii) {
486
+ if (x[ii] < x[1]) {
487
+ zz = x[1]; x[1] = x[ii]; x[ii] = zz
488
+ zz = y[1]; y[1] = y[ii]; y[ii] = zz
489
+ reordered = 1
490
+ }
491
+ if (x[ii] > x[4]) {
492
+ zz = x[4]; x[4] = x[ii]; x[ii] = zz
493
+ zz = y[4]; y[4] = y[ii]; y[ii] = zz
494
+ reordered = 1
495
+ }
496
+ }
497
+ if (showcompositestretches)
498
+ if (reordered) {
499
+ print " reordered ..."
500
+ print " y[1]", y[1], "y[2]", y[2], "y[3]", y[3], "y[4]", y[4]
501
+ print " x[1]", x[1], "x[2]", x[2], "x[3]", x[3], "x[4]", x[4]
502
+ }
503
+ min2shouldbe = ((x[4] - x[2]) * y[1] + (x[2] - x[1]) * y[4]) \
504
+ / (x[4] - x[1])
505
+ min3shouldbe = ((x[4] - x[3]) * y[1] + (x[3] - x[1]) * y[4]) \
506
+ / (x[4] - x[1])
507
+ min2error = y[2] - min2shouldbe
508
+ min3error = y[3] - min3shouldbe
509
+ if (showcompositestretches)
510
+ print " min2error", min2error, "min3error", min3error
511
+
512
+ return (abs(min2error) <= interstretchmaxerr && \
513
+ abs(min3error) <= interstretchmaxerr)
514
+ }
515
+
516
+ function inferstdroundtrip(seg) {
517
+ # We assume that segment seg is in both "there" and "back" stretches.
518
+ # We work with the "there" minima for the two end-segments of seg's
519
+ # "there" stretch, as well as with the "back" minimum for seg iteself...
520
+ #
521
+ # there_1(sent_3 - (bounced_2 - RT)) +
522
+ # there_3((bounced_2 - RT) - sent_1)
523
+ # RT - back_2 = -------------------------------------
524
+ # sent_3 - sent_1
525
+ #
526
+ seg_1 = stretch_startseg[0,stretch_index[0,seg]] # use its "there" min
527
+ seg_2 = seg # use its "back" min
528
+ seg_3 = stretch_endseg[0,stretch_index[0,seg]] # use its "there" min
529
+ tmpstdroundtrip_a = \
530
+ (min[0,seg_1] * (min_sent[0,seg_3] - min_bounced[1,seg_2]) + \
531
+ min[1,seg_2] * (min_sent[0,seg_3] - min_sent[0,seg_1]) + \
532
+ min[0,seg_3] * (min_bounced[1,seg_2] - min_sent[0,seg_1])) \
533
+ / ((min_sent[0,seg_3] + min[0,seg_3]) - \
534
+ (min_sent[0,seg_1] + min[0,seg_1]))
535
+ if (showstdroundtrips)
536
+ print "segments", seg_1, "there,", seg_2, "back,", seg_3, "there:",
537
+ "ROUNDTRIP", tmpstdroundtrip_a
538
+ # ... and vice versa,
539
+ # using "back" stretch end-segment "back" data, and seg "there" data
540
+ seg_1 = stretch_startseg[1,stretch_index[1,seg]] # use its "back" min
541
+ seg_2 = seg # use its "there" min
542
+ seg_3 = stretch_endseg[1,stretch_index[1,seg]] # use its "back" min
543
+ tmpstdroundtrip_b = \
544
+ (min[1,seg_1] * (min_bounced[1,seg_3] - min_sent[0,seg_2]) +\
545
+ min[0,seg_2] * (min_bounced[1,seg_3] - min_bounced[1,seg_1])+\
546
+ min[1,seg_3] * (min_sent[0,seg_2] - min_bounced[1,seg_1])) \
547
+ / ((min_bounced[1,seg_3] - min[1,seg_3]) - \
548
+ (min_bounced[1,seg_1] - min[1,seg_1]))
549
+ if (showstdroundtrips)
550
+ print "segments", seg_1, "back,", seg_2, "there,", seg_3, "back:",
551
+ "ROUNDTRIP", tmpstdroundtrip_b
552
+ return (tmpstdroundtrip_a + tmpstdroundtrip_b) / 2
553
+ }
554
+
555
+ function stretch_length(tb, str) {
556
+ return stretch_endseg[tb, str] - stretch_startseg[tb, str] + 1
557
+ }
558
+
559
+ function set_region_start(tb, seg, reg) {
560
+ region_first_seg[reg] = seg
561
+ $0 = data[min_index[tb,seg]]
562
+ region_first_tb[reg] = tb
563
+ region_first_orig_sent[reg] = $sentfieldnum
564
+ region_first_sent[reg] = min_sent[tb,seg]
565
+ region_first_orig_received[reg] = fixbyteorder($receivedfieldnum)
566
+ region_first_received[reg] = min_received[tb,seg]
567
+ region_first_orig_bounced[reg] = $bouncedfieldnum
568
+ region_first_bounced[reg] = min_bounced[tb,seg]
569
+
570
+ region_first_received_shouldbe[reg] = \
571
+ (tb == 0) ? region_first_orig_sent[reg] + stdroundtrip / 2 \
572
+ : region_first_orig_bounced[reg] - stdroundtrip / 2
573
+ }
574
+
575
+ function set_region_end(tb, seg, reg) {
576
+ region_last_seg[reg] = seg
577
+ $0 = data[min_index[tb,seg]]
578
+ region_last_tb[reg] = tb
579
+ region_last_orig_sent[reg] = $sentfieldnum
580
+ region_last_sent[reg] = min_sent[tb,seg]
581
+ region_last_orig_received[reg] = fixbyteorder($receivedfieldnum)
582
+ region_last_received[reg] = min_received[tb,seg]
583
+ region_last_orig_bounced[reg] = $bouncedfieldnum
584
+ region_last_bounced[reg] = min_bounced[tb,seg]
585
+
586
+ region_last_received_shouldbe[reg] = \
587
+ (tb == 0) ? region_last_orig_sent[reg] + stdroundtrip / 2 \
588
+ : region_last_orig_bounced[reg] - stdroundtrip / 2
589
+ }
590
+
591
+ BEGIN {
592
+ setparameters()
593
+ if (fieldseparator != "") FS = fieldseparator # must be set here
594
+ init = 1
595
+ }
596
+
597
+ init {
598
+ init = 0
599
+ error = 0
600
+
601
+ # Note: FILENAME for targetfromfilename() was not available in BEGIN
602
+ if (target == "")
603
+ target = targetfromfilename()
604
+ if (hostfieldnum != "" &&
605
+ (!(hostfieldnum > 0) || target == "")) {
606
+ printf "Initialization:",
607
+ "hostfieldnum and/or target not properly set.\n" \
608
+ > "/dev/stderr"
609
+ error = 1
610
+ exit
611
+ }
612
+ if ( ! (sentfieldnum > 0 &&
613
+ receivedfieldnum > 0 &&
614
+ bouncedfieldnum > 0 &&
615
+ sentfieldnum != receivedfieldnum &&
616
+ receivedfieldnum != bouncedfieldnum &&
617
+ bouncedfieldnum != sentfieldnum) ) {
618
+ printf "Initialization: data field numbers not properly set.\n" \
619
+ > "/dev/stderr"
620
+ error = 2
621
+ exit
622
+ }
623
+ sourcescale = 1 / (1 + sourceskew)
624
+ if (writefixeddir == "//") writefixeddir = ""
625
+ if (writeorigfixedplotdir == "//") writeorigfixedplotdir = ""
626
+
627
+ if (showtarget) print "Target:", target
628
+ if (writefixeddir) {
629
+ no_dir_filename = FILENAME
630
+ sub(/^.*\//, "", no_dir_filename)
631
+ fixedfilename = writefixeddir "/" no_dir_filename ".fix"
632
+ printf "" > fixedfilename
633
+ rejectedfilename = writefixeddir "/" no_dir_filename ".rej"
634
+ printf "" > rejectedfilename
635
+ }
636
+ if (writeorigfixedplotdir) {
637
+ no_dir_filename = FILENAME
638
+ sub(/^.*\//, "", no_dir_filename)
639
+ plotfilename = writeorigfixedplotdir "/" no_dir_filename ".plot"
640
+ printf "" > plotfilename
641
+ fixedplotfilename = \
642
+ writeorigfixedplotdir "/" no_dir_filename ".fix.plot"
643
+ printf "" > fixedplotfilename
644
+ rejectedplotfilename = \
645
+ writeorigfixedplotdir "/" no_dir_filename ".rej.plot"
646
+ printf "" > rejectedplotfilename
647
+ }
648
+ intpattern = "^[ \t]*-*[0-9][0-9]*[ \t]*$"
649
+ datalines = 0
650
+ in_data = 0 # before "data" (portion possibly containing readings)
651
+ readings = 0
652
+ segments = 0
653
+ thereback[0] = "there" # word for "there" direction code 0
654
+ thereback[1] = "back" # word for "back" direction code 1
655
+ # variable "tb" ranges over "there" and "back" direction codes
656
+ for (tb = 0; tb < 2; ++tb) {
657
+ stretches[tb] = 0
658
+ in_stretch[tb] = 0
659
+ }
660
+ needs_bytes_reversed = 0
661
+ }
662
+
663
+ in_data == 2 {
664
+ # will have exited if not intending to write fixed file
665
+ print >> fixedfilename
666
+ next
667
+ }
668
+
669
+ $0 ~ datalinepattern {
670
+ in_data = 1
671
+ ++datalines
672
+ data[datalines] = $0
673
+ if (hostfieldnum && $hostfieldnum != target) next
674
+ if ($sentfieldnum !~ intpattern || $receivedfieldnum !~ intpattern ||
675
+ $bouncedfieldnum !~ intpattern)
676
+ next
677
+ if (readings == 0) {
678
+ first_NR = NR
679
+ first_index = datalines
680
+ first_sent = $sentfieldnum
681
+ first_received = $receivedfieldnum
682
+ first_bounced = $bouncedfieldnum
683
+ # before setting the cosmetic shifts, we wait till the next reading,
684
+ # which, in comparison with this reading, will indicate whether
685
+ # we need to reverse the byte order of the target timestamps.
686
+ readings = 1
687
+ next
688
+ }
689
+ else if (readings == 1) {
690
+ if (abs($receivedfieldnum - first_received) > 256 * 256)
691
+ needs_bytes_reversed = 1
692
+ sourceshift = -first_sent
693
+ targetshift = int((first_sent + first_bounced)/2 + sourceshift \
694
+ - fixbyteorder(first_received))
695
+ sent = 0
696
+ received = fixbyteorder(first_received) + targetshift
697
+ bounced = int((first_bounced + sourceshift) * sourcescale)
698
+ there = received - sent
699
+ back = bounced - received
700
+ roundtrip = bounced - sent
701
+ segments = 1
702
+ min_index[0,1] = min_index[1,1] = \
703
+ min_index[0,1] = min_index[1,1] = \
704
+ first_index
705
+ min_sent[0,1] = min_sent[1,1] = \
706
+ min_sent_late[0,1] = min_sent_late[1,1] = \
707
+ sent # 0
708
+ min_received[0,1] = min_received[1,1] = \
709
+ min_received_late[0,1] = min_received_late[1,1] = \
710
+ received
711
+ min_bounced[0,1] = min_bounced[1,1] = \
712
+ min_bounced_late[0,1] = min_bounced_late[1,1] = \
713
+ bounced
714
+ globalminthere = min[0,1] = there
715
+ globalminback = min[1,1] = back
716
+ globalminroundtrip = min[2,1] = roundtrip
717
+ this_stretch_startseg[0] = 1 # "there", candidate only!
718
+ this_stretch_startseg[1] = 1 # "back", candidate only!
719
+ if (showreadings) {
720
+ print "Segment 1:"
721
+ print "1 (line", first_NR ") at", sent \
722
+ ": there", there ", back", back ", roundtrip", roundtrip
723
+ }
724
+ }
725
+ ++readings
726
+ sent = int(($sentfieldnum + sourceshift) * sourcescale)
727
+ received = fixbyteorder($receivedfieldnum) + targetshift
728
+ bounced = int(($bouncedfieldnum + sourceshift) * sourcescale)
729
+ there = received - sent
730
+ back = bounced - received
731
+ roundtrip = bounced - sent
732
+ if (sent >= segments * segmentsecs * 1000) {
733
+ # the present reading begins a new segment; report on the old ...
734
+ segmentboundary()
735
+
736
+ # ... and initialize the new
737
+ ++segments
738
+ if (showreadings)
739
+ print "\nSegment", segments ":"
740
+ min_index[0,segments] = min_index[1,segments] = \
741
+ min_index_late[0,segments] = min_index_late[1,segments] = \
742
+ datalines
743
+ min_sent[0,segments] = min_sent[1,segments] = \
744
+ min_sent_late[0,segments] = min_sent_late[1,segments] = \
745
+ sent
746
+ min_received[0,segments] = min_received[1,segments] = \
747
+ min_received_late[0,segments] = min_received_late[1,segments] =\
748
+ received
749
+ min_bounced[0,segments] = min_bounced[1,segments] = \
750
+ min_bounced_late[0,segments] = min_bounced_late[1,segments] = \
751
+ bounced
752
+ min[0,segments] = there
753
+ min[1,segments] = back
754
+ min[2,segments] = roundtrip
755
+ }
756
+ else {
757
+ if (there < min[0,segments]) {
758
+ min_index[0,segments] = min_index_late[0,segments] = datalines
759
+ min_sent[0,segments] = min_sent_late[0,segments] = sent
760
+ min_received[0,segments] = min_received_late[0,segments] = \
761
+ received
762
+ min_bounced[0,segments] = min_bounced_late[0,segments] = bounced
763
+ min[0,segments] = there
764
+ }
765
+ else if (there == min[0,segments]) {
766
+ min_index_late[0,segments] = datalines
767
+ min_sent_late[0,segments] = sent
768
+ min_received_late[0,segments] = received
769
+ min_bounced_late[0,segments] = bounced
770
+ }
771
+ if (back < min[1,segments]) {
772
+ min_index[1,segments] = min_index_late[1,segments] = datalines
773
+ min_sent[1,segments] = min_sent_late[1,segments] = sent
774
+ min_received[1,segments] = min_received_late[1,segments] = \
775
+ received
776
+ min_bounced[1,segments] = min_bounced_late[1,segments] = bounced
777
+ min[1,segments] = back
778
+ }
779
+ else if (back == min[1,segments]) {
780
+ min_index_late[1,segments] = datalines
781
+ min_sent_late[1,segments] = sent
782
+ min_received_late[1,segments] = received
783
+ min_bounced_late[1,segments] = bounced
784
+ }
785
+ if (roundtrip < min[2,segments])
786
+ min[2,segments] = roundtrip
787
+ }
788
+ if (showreadings)
789
+ print readings, "(line", NR ") at", sent \
790
+ ": there", there ", back", back ", roundtrip", roundtrip
791
+ next
792
+ }
793
+
794
+ in_data == 0 {
795
+ if (writefixeddir)
796
+ print >> fixedfilename
797
+ next
798
+ }
799
+
800
+ in_data == 1 {
801
+ if (pastdatapattern == "" || $0 !~ pastdatapattern) {
802
+ ++datalines
803
+ data[datalines] = $0
804
+ next
805
+ }
806
+ savepostdataline = $0
807
+
808
+ in_data = 2
809
+ pastdata()
810
+
811
+ if (writefixeddir) {
812
+ # write first post-data line to fixed file
813
+ print savepostdataline >> fixedfilename
814
+ next
815
+ }
816
+ exit
817
+ }
818
+
819
+ function pastdata() {
820
+ if (!readings) {
821
+ error = 3
822
+ printf "No readings found for target %s.\n", target \
823
+ > "/dev/stderr"
824
+ exit
825
+ }
826
+
827
+ duration=sent
828
+ segmentboundary()
829
+ if (showminima)
830
+ print "\nglobalminthere", globalminthere \
831
+ "; globalminback", globalminback \
832
+ ";\n\t\t\tglobalminroundtrip, fictive",
833
+ (globalminthere + globalminback) \
834
+ ", actual", globalminroundtrip
835
+
836
+ if (showonewaystretches) {
837
+ print "\nJumpless one-way stretches and their skews:"
838
+ for (ii = 1; ii <= segments; ++ii)
839
+ print "Segment", ii \
840
+ ":\tthere stretch", stretch_index[0,ii] \
841
+ ", skew", stretch_skew[0,stretch_index[0,ii]] \
842
+ ";\n\t\t back stretch", stretch_index[1,ii] \
843
+ ", skew", stretch_skew[1,stretch_index[1,ii]]
844
+ }
845
+
846
+ # The ideal, congestion-free, round-trip time -- needed for the
847
+ # reconciliation and coalescing of one-way stretches for opposite
848
+ # directions -- may not be available in globalminroundtrip: there
849
+ # may not have been a single reading without congestion in at least
850
+ # one of the directions. However, any pair of (non-spurious)
851
+ # stretches for opposite directions which overlap on at least two
852
+ # segments should, in their segment minima for their respective
853
+ # directions, represent collinear target clock deviation points.
854
+ # Expressing this collinearity by intertranslating the one-way
855
+ # transit-time minima for the opposite directions in terms of the
856
+ # unknown ideal round-trip time allows us to derive the ideal
857
+ # round-trip time -- provided the one-way transit-time minima are
858
+ # perfectly congestion-free and error-free. Practically, however,
859
+ # what we can expect to derive by this method is a useful "standard
860
+ # round-trip time" that, as said, may well be smaller than
861
+ # globalminroundtrip, perhaps considerably. It might also be just
862
+ # a bit bigger than globalminroundtrip, perhaps by a millisecond or
863
+ # two (depending on intrastretchmaxerr), as if the effective baseline
864
+ # round-trip time, given our limited sampling and our error tolerance
865
+ # for "collinearity", is a bit higher than the super-good time that
866
+ # may be observed as a fluke.
867
+ # Choosing the very lowest of the values for "standard round-trip
868
+ # time" that we can obtain by calculations of the sort suggested
869
+ # would give us the value closest to the true "ideal round-trip time".
870
+ # But this value may not be consistent with our generally imperfect
871
+ # one-way transit time minima, and so would not be the best choice of
872
+ # standard value to mediate the inter-direction translation we need
873
+ # to reconcile and coalesce counter-directional stretches. We must
874
+ # bear in mind that our objective is not to emerge from the analysis
875
+ # with as accurate an estimate as we can for the congestion-free
876
+ # transit times; but rather to emerge with a reliable assessment of
877
+ # whether no clock adjustments occurred between non-overlapping
878
+ # adjustment-free stretches.
879
+ # Therefore, we instead take as the effective stdroundtrip the
880
+ # average of all the values we can calculate as described above.
881
+ # We use globalminroundtrip as a guess at stdroundtrip if there
882
+ # are no approriate pairs of overlapping segments at all to admit
883
+ # our calculation -- and then with low expectations, after issuing
884
+ # a warning.
885
+ if (showstdroundtrips)
886
+ print "\nInferring \"ideal round-trip time\":"
887
+ idealcues = 0
888
+ total = 0
889
+ for (ii = 1; ii <= segments; ++ii) {
890
+ if ( (stretch_index_0 = stretch_index[0,ii]) == "" ||
891
+ (stretch_index_1 = stretch_index[1,ii]) == "" ||
892
+ min_sent[0,stretch_endseg[0,stretch_index_0]] < \
893
+ min_sent[1,stretch_startseg[1,stretch_index_1]] ||
894
+ min_sent[1,stretch_endseg[1,stretch_index_1]] < \
895
+ min_sent[0,stretch_startseg[0,stretch_index_0]] )
896
+ continue
897
+ if (!compatiblecounterskews(stretch_skew[0,stretch_index[0,ii]],
898
+ stretch_skew[1,stretch_index[1,ii]])) {
899
+ for (tb = 0; tb < 2; ++tb) {
900
+ this_stretch_index = stretch_index[tb,ii]
901
+ if (stretch_length(tb, this_stretch_index) == 4 &&
902
+ stretch_length(1-tb, this_stretch_index) >= 6) {
903
+ for (jj = stretch_startseg[tb, this_stretch_index];
904
+ jj <= stretch_endseg[tb, this_stretch_index];
905
+ ++jj)
906
+ stretch_index[tb,ii] = ""
907
+ if (showrejectstretches)
908
+ print "REJECTING length 4", "\"" thereback[tb] "\"",
909
+ "stretch at Segment", ii ": incompatible skews"
910
+ break
911
+ }
912
+ }
913
+ continue
914
+ }
915
+ ++stdcuesegs
916
+ total += inferstdroundtrip(ii)
917
+ }
918
+ if (stdcuesegs)
919
+ stdroundtrip_for_quality = stdroundtrip = total / stdcuesegs
920
+ else {
921
+ print "WARNING: no usable overlapping counter-stretches." \
922
+ > "/dev/null"
923
+ # local change -- ratul > "/dev/stderr"
924
+ stdroundtrip_for_quality = 0
925
+ stdroundtrip = globalminroundtrip
926
+ }
927
+ if (showquality) {
928
+ print "\nThe following provides a measure of the quality of",
929
+ "the timing data:"
930
+ #local change to avoid divide by zero errors
931
+ if (globalminroundtrip != 0) {
932
+ printf "inferred stdroundtrip / globalminroundtrip: " \
933
+ "%s / %s = %s\n",
934
+ stdroundtrip_for_quality, globalminroundtrip,
935
+ stdroundtrip_for_quality / globalminroundtrip
936
+ }
937
+ else {
938
+ printf "inferred stdroundtrip / globalminroundtrip: " \
939
+ "%s / %s = %s\n",
940
+ stdroundtrip_for_quality, globalminroundtrip,
941
+ "nan"
942
+ }
943
+ }
944
+ if (showstdroundtrips || showcompositestretches)
945
+ print "\nUsing stdroundtrip", stdroundtrip
946
+
947
+ # synthesize composite stretches
948
+ if (showcompositestretches) {
949
+ print "\nComposite stretch processing:"
950
+ }
951
+ last_stretch_segment = 0
952
+ last_stretch_tb = "" # (will prefer "there" data when both available)
953
+ for (ii = 1; ii <= segments; ++ii) {
954
+ there_skew = stretch_skew[0,stretch_index[0,ii]]
955
+ back_skew = stretch_skew[1,stretch_index[1,ii]]
956
+ if (there_skew == "" && back_skew == "") {
957
+ if (showcompositestretches)
958
+ print "Segment " ii ": not in a stretch."
959
+ segment_composite[ii] = "unknown" # may be overwritten
960
+ continue
961
+ }
962
+ # skews have been registered from "there" and/or from "back" data
963
+ if ((there_skew != "" && back_skew == "") ||
964
+ (there_skew == "" && back_skew != "")) {
965
+ # skews have been registered from "there" XOR from "back" data
966
+ this_tb = (there_skew != "") ? 0 : 1
967
+ if (stretch_index[this_tb,ii-1] == stretch_index[this_tb,ii]) {
968
+ # past start of stretch
969
+ if (showcompositestretches)
970
+ print "Segment " ii ":",
971
+ "past start of only stretch. CONTINUE."
972
+ segment_composite[ii] = "CONTINUE"
973
+ }
974
+ else if (last_stretch_segment &&
975
+ continuation(last_stretch_tb, last_stretch_segment,
976
+ this_tb, ii)) {
977
+ # new stretch resumes last stretch;
978
+ # assume intervening congestion, and fill the gap, if any
979
+ if (showcompositestretches)
980
+ print "Segment " ii ":",
981
+ "new stretch resumes last stretch. Back fill CONTINUE."
982
+ for (jj = last_stretch_segment + 1; jj <= ii; ++jj)
983
+ segment_composite[jj] = "CONTINUE"
984
+ }
985
+ else {
986
+ # start of the very first stretch, or following jump or turn
987
+ # ... which we may someday take the trouble to locate ...
988
+ if (showcompositestretches)
989
+ print "Segment " ii ":",
990
+ "START of the very first stretch, or following jump/turn."
991
+ segment_composite[ii] = "START"
992
+ }
993
+ last_stretch_segment = ii
994
+ last_stretch_tb = this_tb
995
+ continue
996
+ }
997
+ # skews have been registered both from "there" and from "back" data
998
+ if (stretch_index[0,ii-1] == stretch_index[0,ii] &&
999
+ stretch_index[1,ii-1] == stretch_index[1,ii]) {
1000
+ # past start of both of the stretches
1001
+ # (we save repeated continuation checks)
1002
+ if (showcompositestretches)
1003
+ print "Segment " ii ":",
1004
+ "in two stretches; past start of both. CONTINUE."
1005
+ segment_composite[ii] = "CONTINUE"
1006
+ last_stretch_segment = ii
1007
+ last_stretch_tb = 0
1008
+ continue
1009
+ }
1010
+ # at least one of the two stretches is just starting
1011
+ if (continuation(0, ii, 1, ii)) {
1012
+ # (The "continuation" test should work for arbitrarily
1013
+ # positioned stretches.)
1014
+ if (stretch_index[0,ii-1] == stretch_index[0,ii] ||
1015
+ stretch_index[1,ii-1] == stretch_index[1,ii]) {
1016
+ # one stretch continuing, another starting, compatibly
1017
+ if (showcompositestretches)
1018
+ print "Segment " ii ":",
1019
+ "CONTINUE test succeeded for start new mid-existing."
1020
+ segment_composite[ii] = "CONTINUE"
1021
+ }
1022
+ else if (last_stretch_segment &&
1023
+ continuation(last_stretch_tb, last_stretch_segment,
1024
+ 0, ii)) {
1025
+ # both stretches are just starting, but resume last stretch;
1026
+ # assume intervening congestion, and fill the gap, if any
1027
+ if (showcompositestretches)
1028
+ print "Segment " ii ":",
1029
+ "new stretches resume last stretch. Back fill CONTINUE."
1030
+ for (jj = last_stretch_segment + 1; jj <= ii; ++jj)
1031
+ segment_composite[jj] = "CONTINUE"
1032
+ }
1033
+ else {
1034
+ # both stretches are just starting
1035
+ if (showcompositestretches)
1036
+ print "Segment " ii ":",
1037
+ "double START, very first or after jump/turn."
1038
+ segment_composite[ii] = "START"
1039
+ }
1040
+ last_stretch_segment = ii
1041
+ last_stretch_tb = 0
1042
+ continue
1043
+ }
1044
+ else {
1045
+ # "continuation" test failed: the two stretches can't be merged.
1046
+ # The stretches should be hinged on this single segment,
1047
+ # NOT overlapping on two or more segments. One must be ending.
1048
+ if (stretch_index[0,ii] == stretch_index[0,ii+1] &&
1049
+ stretch_index[1,ii] == stretch_index[1,ii+1]) {
1050
+ # BAD: neither is ending.
1051
+ if (showcompositestretches)
1052
+ print "Segment " ii ":",
1053
+ "continuation test failed; BAD non-hinge case."
1054
+ printf "ERROR: Stretches overlappiing at segment %d " \
1055
+ "are incompatible.\n",
1056
+ ii > "/dev/stderr"
1057
+ error = 4
1058
+ exit
1059
+ }
1060
+ if ((stretch_index[0,ii] != stretch_index[0,ii+1] &&
1061
+ min_sent[0,ii] > min_bounced[1,ii] - stdroundtrip) \
1062
+ || (stretch_index[1,ii] != stretch_index[1,ii+1] &&
1063
+ min_bounced[1,ii] - stdroundtrip > min_sent[0,ii])) {
1064
+ # BAD: overlapping at a fine granularity, within segment.
1065
+ if (showcompositestretches)
1066
+ print "Segment " ii ":",
1067
+ "continuation test failed;",
1068
+ "BAD micro-overlap in hinge case."
1069
+ printf "ERROR: Stretches overlappiing inside segment %d " \
1070
+ "are incompatible.\n",
1071
+ ii > "/dev/stderr"
1072
+ error = 5
1073
+ exit
1074
+ }
1075
+ if (compatiblecounterskews(there_skew, back_skew)) {
1076
+ if (showcompositestretches)
1077
+ print "Segment " ii ": continuation test failed; JUMP."
1078
+ segment_composite[ii] = "JUMP"
1079
+ }
1080
+ else {
1081
+ if (showcompositestretches)
1082
+ print "Segment " ii ": continuation test failed; TURN."
1083
+ segment_composite[ii] = "TURN"
1084
+ }
1085
+ last_stretch_segment = ii
1086
+ last_stretch_tb = 0
1087
+ continue
1088
+ }
1089
+ }
1090
+ if (showcompositestretches) {
1091
+ print "\nComposite stretch end result:"
1092
+ for (ii = 1; ii <= segments; ++ii)
1093
+ print "Segment", ii \
1094
+ ":\t" segment_composite[ii]
1095
+ }
1096
+
1097
+ # map final regions
1098
+ # Note: we assume no out-of-sequence packet delivery, at least at the
1099
+ # data's spacing of packets ... but perhaps necessarily, if moving
1100
+ # through a fixed path, with fixed hardware interfaces.
1101
+ regions = 0
1102
+ for (ii = 1; ii <= segments; ++ii) {
1103
+ if (segment_composite[ii] == "unknown") continue
1104
+ there_stretch = stretch_index[0,ii]
1105
+ back_stretch = stretch_index[1,ii]
1106
+ if (segment_composite[ii] == "START") {
1107
+ # region start point
1108
+ ++regions
1109
+ if (there_stretch &&
1110
+ (!back_stretch || min_sent[0,ii] <= min_sent[1,ii]))
1111
+ set_region_start(0, ii, regions)
1112
+ else
1113
+ set_region_start(1, ii, regions)
1114
+ }
1115
+ if (segment_composite[ii] == "CONTINUE") {
1116
+ if (segment_composite[ii+1] == "CONTINUE" ||
1117
+ segment_composite[ii+1] == "JUMP" ||
1118
+ segment_composite[ii+1] == "TURN")
1119
+ continue
1120
+ # region end point
1121
+ if (there_stretch &&
1122
+ (!back_stretch || min_sent[0,ii] >= min_sent[1,ii]))
1123
+ set_region_end(0, ii, regions)
1124
+ else
1125
+ set_region_end(1, ii, regions)
1126
+ }
1127
+ else if (segment_composite[ii] == "JUMP" ||
1128
+ segment_composite[ii] == "TURN") {
1129
+ # region start and end points
1130
+ if (min_sent[0,ii] < min_sent[1,ii]) {
1131
+ set_region_end(0, ii, regions)
1132
+ set_region_start(1, ii, regions+1)
1133
+ }
1134
+ else {
1135
+ set_region_end(1, ii, regions)
1136
+ set_region_start(0, ii, regions+1)
1137
+ }
1138
+ ++regions
1139
+ }
1140
+ }
1141
+ if (showregions) {
1142
+ print "\nFinal map of regions:"
1143
+ for (ii = 1; ii <= regions; ++ii)
1144
+ printf "Region %d (segments %d - %d):\n" \
1145
+ " %s %d->%d->%d to %s %d->%d->%d\n",
1146
+ ii, region_first_seg[ii], region_last_seg[ii],
1147
+ (region_first_tb[ii] == 0) ? "(\"there\")" \
1148
+ : "(\"back\")",
1149
+ region_first_orig_sent[ii],
1150
+ region_first_orig_received[ii],
1151
+ region_first_orig_bounced[ii],
1152
+ (region_last_tb[ii] == 0) ? "(\"there\")" \
1153
+ : "(\"back\")",
1154
+ region_last_orig_sent[ii],
1155
+ region_last_orig_received[ii],
1156
+ region_last_orig_bounced[ii]
1157
+ }
1158
+ if (showcoverage) {
1159
+ coverage = 0
1160
+ for (ii = 1; ii <= regions; ++ii)
1161
+ coverage += region_last_sent[ii] - region_first_sent[ii]
1162
+ printf "\nRelative coverage: %s / %s = %s\n",
1163
+ coverage, duration, coverage / duration
1164
+ }
1165
+
1166
+ if (!writefixeddir && !writeorigfixedplotdir) return
1167
+
1168
+ # write corrected data lines to fixed file
1169
+ saveOFS = OFS
1170
+ OFS = FS
1171
+ search_from_region = 1
1172
+ if (writeorigfixedplotdir)
1173
+ # display globalminthere as half the standard round trip.
1174
+ plottargetshift = targetshift + int(stdroundtrip/2) - globalminthere
1175
+ for (ii = 1; ii <= datalines; ++ii) {
1176
+ $0 = data[ii]
1177
+
1178
+ if ($0 !~ datalinepattern ||
1179
+ (hostfieldnum && $hostfieldnum != target) ||
1180
+ $sentfieldnum !~ intpattern ||
1181
+ $receivedfieldnum !~ intpattern ||
1182
+ $bouncedfieldnum !~ intpattern) {
1183
+ if (writefixeddir)
1184
+ print >> fixedfilename
1185
+ continue
1186
+ }
1187
+
1188
+ # locate region, if any, covering this reading; else reject.
1189
+ for (jj = search_from_region; jj <= regions; ++jj) {
1190
+ if ($sentfieldnum <= region_last_orig_sent[jj]) break
1191
+ }
1192
+ search_from_region = jj
1193
+
1194
+ # fix the target byte order, even for the rejected data points
1195
+ $receivedfieldnum = fixbyteorder($receivedfieldnum)
1196
+
1197
+ if (writeorigfixedplotdir) {
1198
+ # shift and scale to match analytical output
1199
+ sent = int(($sentfieldnum + sourceshift) * sourcescale)
1200
+ received = $receivedfieldnum + plottargetshift
1201
+ bounced = int(($bouncedfieldnum + sourceshift) * sourcescale)
1202
+ printf "%d %d %d\n", sent, received, bounced \
1203
+ >> plotfilename
1204
+ }
1205
+
1206
+ if (jj > regions || $sentfieldnum < region_first_orig_sent[jj]) {
1207
+ # didn't find one
1208
+ if (writefixeddir)
1209
+ print >> rejectedfilename
1210
+ if (writeorigfixedplotdir) {
1211
+ printf "%d %d %d\n", sent, received, bounced \
1212
+ >> rejectedplotfilename
1213
+ }
1214
+ continue
1215
+ }
1216
+
1217
+ # found the region
1218
+ first_orig_received = region_first_orig_received[jj]
1219
+ last_orig_received = region_last_orig_received[jj]
1220
+ first_received_shouldbe = region_first_received_shouldbe[jj]
1221
+ last_received_shouldbe = region_last_received_shouldbe[jj]
1222
+ received = $receivedfieldnum
1223
+ $receivedfieldnum = int( \
1224
+ ((last_orig_received - received) * first_received_shouldbe + \
1225
+ (received - first_orig_received) * last_received_shouldbe) / \
1226
+ (last_orig_received - first_orig_received) )
1227
+ if (writefixeddir)
1228
+ print >> fixedfilename
1229
+ if (writeorigfixedplotdir) {
1230
+ # shift and scale fixed received to match sent shift and scale
1231
+ received = int(($receivedfieldnum + sourceshift) * sourcescale)
1232
+ printf "%d %d %d\n", sent, received, bounced \
1233
+ >> fixedplotfilename
1234
+ }
1235
+ }
1236
+ OFS = saveOFS
1237
+ }
1238
+
1239
+ END {
1240
+ if (error) exit error
1241
+ if (in_data != 2) pastdata()
1242
+ }
1243
+
1244
+ # The objective is to compose a map of the target's clock skews and jumps over
1245
+ # the duration of the probing run. This allows compensation, removing target
1246
+ # clock artifacts from the probe's timing data.
1247
+ #
1248
+ # Note: I refer to the outgoing direction as "there" and the incoming direction
1249
+ # as "back".
1250
+ #
1251
+ # We analyze the "there" data and the "back" data independently, at first, for
1252
+ # maximal stretches of length at least 4 of segments whose segment minima for
1253
+ # one-way packet transit times, in the respective directions, are collinear.
1254
+ # The idea in the independent analysis of the two directions is two-fold:
1255
+ # foremost, a lack of persuasive data for one direction, due to obscuring of
1256
+ # clock behavior by network congestion in that direciton, can be covered for
1257
+ # by persuasive data for the other direction; and secondly, if data for both
1258
+ # directions are independently persuasive, the redundancy provides a measure
1259
+ # of error-checking and allows us to infer an uncongested round-trip time that
1260
+ # should be more reliable than the global minimum round-trip time.