scriptroute 0.4.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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.