fpfind 3.3.0__py3-none-any.whl

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.
fpfind/freqcd ADDED
@@ -0,0 +1,81 @@
1
+ #!/bin/sh
2
+ # Stand-in script for the 'freqcd' binary. Do not modify.
3
+ #
4
+ # This stub is required for compliance with pyproject.toml specification,
5
+ # when the 'freqcd' binary has not yet been compiled. For convenience,
6
+ # this script will automatically trigger the compilation and then proceed
7
+ # to run the desired binary :)
8
+ #
9
+ # Works only on Linux, with 'ps', 'awk' and 'python3' dependencies.
10
+
11
+ # Gets the full call to current script, then retrieves program path
12
+ # e.g. /bin/sh /home/justin/.pyenv/versions/3.10.6/bin/freqcd -x ...
13
+ PROGRAM=$(ps -q $$ -o args= | awk -F ' ' '{print $2}')
14
+
15
+ # Get root directory of installed library, where postinstall script is located
16
+ ENTRYPOINT=$(python3 -c "import importlib.util; print(importlib.util.find_spec('fpfind').origin)")
17
+ INSTALL_ROOT=$(dirname "$(readlink -f ${ENTRYPOINT})")
18
+
19
+ # Keep a record of program path, in case one desires a manual recompilation;
20
+ # will be created in-situ only in an editable installation.
21
+ rm -f ${INSTALL_ROOT}/freqcd_path \
22
+ && echo "${PROGRAM}" > ${INSTALL_ROOT}/freqcd_path
23
+
24
+ # Trigger post-install compilation and write directly to target
25
+ cd ${INSTALL_ROOT} && ./_postinstall ${PROGRAM} \
26
+ || { echo "'freqcd.c' failed to compile... not running."; exit 1; }
27
+
28
+ # Pass arguments and directly execute
29
+ exec "${PROGRAM}" "$@"
30
+
31
+
32
+
33
+ # Explanation of this script as follows.
34
+ #
35
+ # We go through this trouble of adding console scripts via the build backend
36
+ # is so that the backend can manage its installation/uninstallation, as
37
+ # opposed to manual aliases/symlinks.
38
+ #
39
+ # Some issues need to be addressed before this will work:
40
+ # 1. The use of 'pyenv' for Python management causes the binaries
41
+ # to be called as a shim, i.e. the call occurs within a nested script
42
+ # that first sources the Python environment dynamically. The actual
43
+ # binary location needs to be identified.
44
+ # 2. The binaries, during the library installation process, are copied
45
+ # over from the repo directly, and is not a soft link. The binary
46
+ # itself will thus need to be replaced.
47
+ #
48
+ # Using the 'importlib' mechanism, i.e.
49
+ #
50
+ # import importlib; print(importlib.util.find_spec('fpfind').origin)
51
+ #
52
+ # does not work when the library is installed as an editable package,
53
+ # although it does work otherwise. The same applies to the pip discovery
54
+ # method, with the additional 'pip' dependency, i.e.
55
+ #
56
+ # python3 -m pip show ${LIBNAME} | grep 'Location' | awk -F ': ' '{print $2}'
57
+ #
58
+ # Instead, we rely on the fact that this script will be triggered by 'sh',
59
+ # with the desired path supplied as an argument. Extracting the path is thus
60
+ # a simple matter of a 'ps' + 'awk' hack.
61
+ #
62
+ # A small side-note, use of '$PPID' to extract the parent path will not work,
63
+ # which may end up deferring to the shell emulator's process instead.
64
+ #
65
+ # Soft links could have been used instead of directly copying the binary,
66
+ # so that editable installations can still freely recompile, independently
67
+ # of the Python library install flow. However, Git will continue to track
68
+ # this file (potentially leading to accidental binary commits), and requiring
69
+ # users to run 'git-skip-worktree' manually is not ideal. A security concern
70
+ # may also arise when the editable installation is publicly modifiable.
71
+ #
72
+ # We instead the binary directly. To accomodate the need for updates, the
73
+ # path to the binary is provided during the compilation process, at:
74
+ #
75
+ # 'src/fpfind/freqcd_path'
76
+ #
77
+ # To use the soft link behaviour (at your risk), replace the code with:
78
+ #
79
+ # cd ${INSTALL_ROOT} && ./_postinstall ${INSTALL_ROOT}/freqcd \
80
+ # || { echo "'freqcd.c' failed to compile... not running."; exit 1; }
81
+ # rm -f ${PROGRAM} && ln -s ${INSTALL_ROOT}/freqcd ${PROGRAM}
fpfind/freqcd.c ADDED
@@ -0,0 +1,549 @@
1
+ /* freqcd.c : Part of the quantum key distribution software for correcting
2
+ timestamps emitted by a timestamp unit running at a relative
3
+ frequency offset. Proof-of-concept.
4
+
5
+ Copyright (C) 2024 Justin Peh, Xu Zifang, Christian Kurtsiefer,
6
+ National University of Singapore
7
+
8
+ This source code is free software; you can redistribute it and/or
9
+ modify it under the terms of the GNU Public License as published
10
+ by the Free Software Foundation; either version 2 of the License,
11
+ or (at your option) any later version.
12
+ This source code is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
+ Please refer to the GNU Public License for more details.
16
+ You should have received a copy of the GNU Public License along with
17
+ this source code; if not, write to:
18
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
+
20
+ --
21
+
22
+ Program that receives a frequency offset between two parties calculated
23
+ by fpfind, and performs a software correction of the timestamps emitted
24
+ by readevents. Parameters are optimized such that the correction can
25
+ be performed using purely 64-bit constructs.
26
+
27
+ Note that there is no support for Windows - many of the functionality
28
+ used are native to Linux.
29
+
30
+
31
+ usage:
32
+ freqcd [-i infilename] [-o outfilename] [-X]
33
+ [-t timecorr] [-f freqcorr] [-F freqfilename] [-d] [-u]
34
+
35
+
36
+ DATA STREAM OPTIONS:
37
+ -i infilename: Filename of source events. Can be a file or a socket
38
+ and has to supply binary data according to the raw data
39
+ spec from the timestamp unit. If unspecified, data
40
+ is read from stdin.
41
+ -o outfilename: Outfile name for timing corrected events, which can
42
+ either be a file or socket. Output format is the same as
43
+ with input. If unspecified, data is written to stdout.
44
+ -F freqfilename: Filename of frequency correction values. Needs to be
45
+ a readable+writeable socket storing newline-delimited
46
+ frequency offset values (see '-f' option for format).
47
+ Takes priority over '-f' supplied offsets.
48
+ If unspecified, the frequency offset will be static.
49
+
50
+ ENCODING OPTIONS:
51
+ -X: Specifies if both the raw input and output data streams
52
+ are to be read in legacy format, as specified in the
53
+ timestamp unit.
54
+ -t timecorr: Timing offset of the timestamps, in units of ps. If
55
+ unspecified, offset = 0. The ps unit is chosen for
56
+ legibility; the actual resolution is 1/256ns (~4ps),
57
+ in line with the high resolution timestamp spec.
58
+ -f freqcorr: Frequency offset of the current clock relative to some
59
+ reference clock, in units of 2^-34 (or 0.6e-10).
60
+ If unspecified, offset = 0. Maximum absolute value is
61
+ 2097151 (i.e. 2^21-1), see [1] for explanation.
62
+ -d: Decimal mode. Changes the frequency offset read by '-f'
63
+ and '-F' to be in units of 0.1 ppb instead, with
64
+ maximum absolute value of 1220703 (i.e. 2^-13). For
65
+ human-readability.
66
+ -u: Update mode. Changes the frequencies read by '-F' to
67
+ represent frequency differentials instead, i.e. the
68
+ current frequency offset is shifted by df' rather than
69
+ replaced with df, given by:
70
+
71
+ df = (1+df)*(1+df')-1
72
+ ≈ df+df' (to nearest 0.1ppb, if df*df' < 0.05ppb)
73
+
74
+ Potential improvements:
75
+ - Consider using epoll() if necessary
76
+ - Merge write procedure with select() call
77
+ - Optimize buffer parameters
78
+
79
+ References:
80
+ [1]: Formulae, https://github.com/s-fifteen-instruments/fpfind/issues/6
81
+ */
82
+
83
+ #include <stdio.h>
84
+ #include <stdlib.h>
85
+ #include <fcntl.h> // open, O_RDONLY, O_NONBLOCK
86
+ #include <sys/select.h> // fd_set, usually implicitly declared
87
+ #include <unistd.h> // getopt, select
88
+ #include <errno.h> // select errno
89
+ #include <limits.h> // INT_MAX
90
+ #include <math.h> // pow(..., 10), round(...)
91
+ #include <string.h> // strcmp(...)
92
+
93
+ /* default definitions */
94
+ #define FNAMELENGTH 200 /* length of file name buffers */
95
+ #define FNAMEFORMAT "%200s" /* for sscanf of filenames */
96
+ #define FILE_PERMISSONS 0644 /* for all output files */
97
+ #define INBUFENTRIES 1024000 /* max. elements in input buffer */
98
+ #define OUTBUFENTRIES 1024000 /* max. elements in output buffer */
99
+ #define FREQBUFSIZE 100 /* max. length of frequency correction values */
100
+ #define RETRYREADWAIT 500000 /* sleep time in usec after an empty read */
101
+ #define FCORR_ARESBITS -34 /* absolute resolution of correction, in power of 2 */
102
+ #define FCORR_AMAXBITS -13 /* absolute maximum allowed correction, in power of 2 */
103
+ #define FCORR_DEFAULT 0 /* frequency correction in units of 2^FCORR_ARESBITS */
104
+ #define FCORR_ARESDECIMAL -10 /* absolute resolution of correction, in power of 10, specifically for '-d' mode */
105
+ #define FCORR_OFLOWRESBITS 64 /* overflow correction resolution, in power of 2 */
106
+ //#define __DEBUG__ /* global debug flag, uncomment to disable, send -v flag for more debug msgs */
107
+
108
+ // Formatting colors
109
+ #define COLOR_ERROR "\x1b[31m"
110
+ #define COLOR_RESET "\x1b[0m"
111
+
112
+ // struct defined in non-legacy format
113
+ typedef struct rawevent {
114
+ unsigned int low;
115
+ unsigned int high;
116
+ } rawevent;
117
+
118
+ typedef long long ll;
119
+ typedef unsigned long long ull;
120
+ #ifdef __SIZEOF_INT128__
121
+ typedef unsigned __int128 u128;
122
+ typedef __int128 i128;
123
+ #else
124
+ #warning "128-bit integers unsupported in current compiler: \
125
+ slight undercompensation of frequency will occur from rounding errors."
126
+ #endif
127
+
128
+ /* error handling */
129
+ char *errormessage[] = {
130
+ "No error",
131
+ "Generic error", /* 1 */
132
+ "Error reading in infilename",
133
+ "Error opening input stream source",
134
+ "Error parsing freq correction as integer",
135
+ "Freq correction value out of range", /* 5 */
136
+ "Unable to allocate memory to inbuffer",
137
+ "Unable to allocate memory to outbuffer",
138
+ "Error reading in outfilename",
139
+ "Error opening output stream",
140
+ "Error reading in freqfilename", /* 10 */
141
+ "Error opening freq correction stream source",
142
+ "Freq correction value not newline terminated",
143
+ "Unable to allocate memory to freqbuffer",
144
+ "Error parsing time correction as integer",
145
+ "Duplicate filename specified for reading and writing", /* 15 */
146
+ };
147
+ void wmsg(char *message) {
148
+ fprintf(stderr, COLOR_ERROR "%s\n" COLOR_RESET, message);
149
+ };
150
+ void wmsg_s(size_t n, char *fmt, char *v) { // dirty fix for overloading signatures
151
+ char *buffer = (char *)malloc(n*sizeof(char));
152
+ snprintf(buffer, n, fmt, v); wmsg(buffer); free(buffer);
153
+ };
154
+ void wmsg_i(size_t n, char *fmt, int v) {
155
+ char *buffer = (char *)malloc(n*sizeof(char));
156
+ snprintf(buffer, n, fmt, v); wmsg(buffer); free(buffer);
157
+ };
158
+ int emsg(int code) {
159
+ wmsg(errormessage[code]);
160
+ return code;
161
+ };
162
+
163
+ /* for reading freqcorr values */
164
+ int readint(char *buff) {
165
+ char *end;
166
+ long value;
167
+ value = strtol(buff, &end, 10);
168
+ // string is a decimal, zero-terminated, and fits in an int
169
+ if ((end != buff) && (*end == 0) && (abs(value) < INT_MAX))
170
+ return (int)value;
171
+ return INT_MAX;
172
+ }
173
+
174
+ void usage() {
175
+ fprintf(stderr, "\
176
+ Usage: freqcd [-i infilename] [-o outfilename] [-X]\n\
177
+ [-t timecorr] [-f freqcorr] [-F freqfilename] [-d] [-u]\n\
178
+ Performs frequency correction of timestamps emitted by qcrypto's readevents.\n\
179
+ \n\
180
+ Supports up to a timing resolution of 1/256ns, but should work with 1/8ns and\n\
181
+ 2ns timing resolutions as well. An optional static timing correction can be\n\
182
+ applied. If '-u' is not supplied, reads from '-F' replace the current freq.\n\
183
+ \n\
184
+ Data stream options:\n\
185
+ -i infilename File/socket name for source events. Defaults to stdin.\n\
186
+ -o outfilename File/socket name for corrected events. Defaults to stdout.\n\
187
+ -F freqfilename File/socket name of frequency correction values.\n\
188
+ \n\
189
+ Encoding options:\n\
190
+ -X Use legacy timestamp format.\n\
191
+ -t timecorr Timing offset, in units of ps (resolution of 1/256 ns).\n\
192
+ -f freqcorr Frequency offset, in units of 2^-34 (range: 0-2097151).\n\
193
+ -d Use units of 0.1ppb for '-f'/'-F' (range: 0-1220703).\n\
194
+ -u Modify current frequency relative to reads from '-F'.\n\
195
+ \n\
196
+ Shows this help with '-h' option. More descriptive documentation:\n\
197
+ <https://github.com/s-fifteen-instruments/fpfind/blob/main/src/fpfind/freqcd.c>\n\
198
+ ");
199
+ }
200
+
201
+
202
+ int main(int argc, char *argv[]) {
203
+
204
+ /* other constants */
205
+ const int INBUFSIZE = INBUFENTRIES * sizeof(struct rawevent);
206
+ const int OUTBUFSIZE = OUTBUFENTRIES * sizeof(struct rawevent);
207
+ const int FCORR_MAX = 1 << (FCORR_AMAXBITS - FCORR_ARESBITS);
208
+ const int FCORR_TBITS1 = -FCORR_AMAXBITS - 1; // bit truncations when correcting timestamp
209
+ const int FCORR_TBITS2 = (FCORR_AMAXBITS - FCORR_ARESBITS) + 1;
210
+ const double FCORR_BTO1 = pow(2, FCORR_ARESBITS);
211
+
212
+ // Conversion factor for decimal mode, i.e. 1e-10/2^-34 = ~1.7179869
213
+ // Ratio inverted due to negative signs in exponents.
214
+ const double FCORR_DTOB = ((long long)1 << -FCORR_ARESBITS) / pow(10, -FCORR_ARESDECIMAL);
215
+
216
+ /* parse options */
217
+ int fcorr = FCORR_DEFAULT; // frequency correction value
218
+ ll tcorr = 0; // time correction value
219
+ char infilename[FNAMELENGTH] = {}; // store filename
220
+ char outfilename[FNAMELENGTH] = {}; // store filename
221
+ char freqfilename[FNAMELENGTH] = {}; // store filename
222
+ int islegacy = 0; // mark if format is legacy
223
+ int isdecimal = 0; // mark if frequency input is in decimal units
224
+ int isupdate = 0; // mark if relative frequency is used
225
+ int isdebugverbose = 0; // mark if need to show more verbose debug
226
+ int opt; // for getopt options
227
+ opterr = 0; // be quiet when no options supplied
228
+ while ((opt = getopt(argc, argv, "i:o:F:f:t:xXduhv")) != EOF) {
229
+ switch (opt) {
230
+ case 'i':
231
+ if (sscanf(optarg, FNAMEFORMAT, infilename) != 1) return -emsg(2);
232
+ infilename[FNAMELENGTH-1] = 0; // security termination
233
+ break;
234
+ case 'o':
235
+ if (sscanf(optarg, FNAMEFORMAT, outfilename) != 1) return -emsg(8);
236
+ outfilename[FNAMELENGTH-1] = 0; // security termination
237
+ break;
238
+ case 'F':
239
+ if (sscanf(optarg, FNAMEFORMAT, freqfilename) != 1) return -emsg(10);
240
+ freqfilename[FNAMELENGTH-1] = 0; // security termination
241
+ break;
242
+ case 'f':
243
+ if (sscanf(optarg, "%d", &fcorr) != 1) return -emsg(4);
244
+ break;
245
+ case 't':
246
+ if (sscanf(optarg, "%lld", &tcorr) != 1) return -emsg(14);
247
+ tcorr = (ll) tcorr * 0.256; // convert ps -> 1/256ns units
248
+ break;
249
+ case 'x': // retained for legacy purposes, use '-X' instead
250
+ wmsg("'-x' has been deprecated and will be removed not before fpfind:v1.2024.15 - use '-X' instead");
251
+ case 'X':
252
+ islegacy = 1;
253
+ break;
254
+ case 'd':
255
+ isdecimal = 1;
256
+ break;
257
+ case 'u':
258
+ isupdate = 1;
259
+ break;
260
+ case 'h':
261
+ usage(); exit(1);
262
+ break;
263
+ case 'v': // leave undocumented, for internal use only
264
+ isdebugverbose = 1;
265
+ break;
266
+ }
267
+ }
268
+
269
+ /* check specified frequency correction is within-bounds */
270
+ // needs to be done after argument reading for position-agnostic '-f'
271
+ if (isdecimal) fcorr = (int) fcorr * FCORR_DTOB;
272
+ if (abs(fcorr) >= FCORR_MAX) return -emsg(5);
273
+
274
+ /* set input and output handler */
275
+ int inhandle = 0; // stdin by default
276
+ if (infilename[0]) {
277
+ inhandle = open(infilename, O_RDONLY | O_NONBLOCK);
278
+ if (inhandle == -1) return -emsg(3);
279
+ }
280
+
281
+ int freqhandle = 0; // null by default (not stdin)
282
+ if (freqfilename[0]) {
283
+ freqhandle = open(freqfilename, O_RDONLY | O_NONBLOCK);
284
+ if (freqhandle == -1) return -emsg(11);
285
+ }
286
+
287
+ int outhandle = 1; // stdout by default
288
+ if (outfilename[0]) {
289
+ if ((infilename[0]) && (strcmp(outfilename, infilename) == 0)) return -emsg(15);
290
+ if ((freqfilename[0]) && (strcmp(outfilename, freqfilename) == 0)) return -emsg(15);
291
+ outhandle = open(outfilename, O_WRONLY | O_CREAT | O_TRUNC, FILE_PERMISSONS);
292
+ if (outhandle == -1) return -emsg(9);
293
+ }
294
+
295
+ /* initialize input and output buffers */
296
+ struct rawevent *inbuffer;
297
+ inbuffer = (struct rawevent *)malloc(INBUFSIZE);
298
+ if (!inbuffer) return -emsg(6);
299
+ struct rawevent *eventptr; // pointer to position within inbuffer
300
+ int eventnum = 0; // number of available rawevents for processing
301
+ char *inbufferbytes = (char *)inbuffer; // lower level byte buffer
302
+ char *inbufferbytes_next; // pointer to next byte write destination
303
+
304
+ struct rawevent *outbuffer;
305
+ outbuffer = (struct rawevent *)malloc(OUTBUFSIZE);
306
+ if (!outbuffer) return -emsg(7);
307
+ int outevents = 0;
308
+
309
+ char *freqbuffer;
310
+ int freqbytesread = 0;
311
+ int freqbytesread_next = 0;
312
+ int freqbytespartial = 0; // size of partial freqcorr value remaining
313
+ char *freqbuffer_next;
314
+ if (freqhandle) { // allocate memory only if needed
315
+ freqbuffer = (char *)malloc(FREQBUFSIZE);
316
+ if (!freqbuffer) return -emsg(13);
317
+ freqbuffer_next = freqbuffer; // pointer to next char write destination
318
+ }
319
+
320
+ /* parameters for select call */
321
+ fd_set rfds;
322
+ struct timeval tv;
323
+ int retval;
324
+
325
+ /* inbuffer reading variables */
326
+ int i, j;
327
+ int inbytesread = 0;
328
+ int inbytesread_next = 0;
329
+ int inbytespartial; // size of partial rawevent remaining in inbufferbyte
330
+
331
+ /* timestamp variables */
332
+ ull tsref = 0; // reference timestamp to scale by frequency correction,
333
+ // noting subsequent initializations should zero 'tsref'
334
+ int isset_tsref = 0; // initialization marker for tsref
335
+ ull ts, tsmeas, tsdiff; // timestamp
336
+ ll tscorr; // timestamp correction
337
+ ll tsoverflowcorr = tcorr; // timestamp overflow corrections
338
+ // overloading with the desired timing corr
339
+ #ifdef __SIZEOF_INT128__
340
+ u128 _tsdiff; // 128-bit equivalents to mitigate rounding error
341
+ i128 _tscorr;
342
+ i128 _tsoverflowcorr = (i128)tcorr << FCORR_OFLOWRESBITS;
343
+ #endif
344
+ unsigned int high; // high word in timestamp
345
+ unsigned int low; // low word in timestamp
346
+ unsigned int _swp; // temporary swap variable, support 'legacy' option
347
+
348
+ /* main loop */
349
+ while (1) {
350
+
351
+ /* discard previously processed rawevents and
352
+ retain partial rawevent left in buffer */
353
+ // TODO: Fix potential bug when 'continue' is called, which
354
+ // clears the inputbuffer without writing to output stream.
355
+ inbytespartial = inbytesread % sizeof(struct rawevent);
356
+ for (i = inbytesread - inbytespartial, j = 0; j < inbytespartial; i++, j++) {
357
+ inbufferbytes[j] = inbufferbytes[i];
358
+ }
359
+ inbufferbytes_next = &inbufferbytes[inbytespartial];
360
+
361
+ /* wait for data on inhandle and freqhandle */
362
+ // TODO: Consider whether to use poll/epoll mechanisms, if frequent
363
+ // pipe recreation is a concern (high fd).
364
+ FD_ZERO(&rfds);
365
+ FD_SET(inhandle, &rfds);
366
+ if (freqhandle) FD_SET(freqhandle, &rfds);
367
+ tv.tv_sec = 0;
368
+ tv.tv_usec = RETRYREADWAIT;
369
+ retval = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
370
+ if (retval == -1) {
371
+ wmsg_i(50, "Error %d on select.", errno);
372
+ break; // graceful close
373
+ }
374
+
375
+ if (FD_ISSET(inhandle, &rfds)) {
376
+
377
+ /* read data from inhandle */
378
+ // TODO: Highlight corresponding bug in chopper.c. Note that assigning
379
+ // to inbytesread directly can potentially corrupt events.
380
+ inbytesread_next = read(inhandle, inbufferbytes_next, INBUFSIZE - inbytespartial);
381
+ if (inbytesread_next == 0) {
382
+ wmsg("Input stream closed.");
383
+ break; // no bytes read (i.e. EOF)
384
+ // TODO: Check if this should be continue instead,
385
+ // when running ad-infinitum
386
+ }
387
+ if (inbytesread_next == -1) {
388
+ wmsg_i(50, "Error %d on input read.", errno);
389
+ break; // graceful close
390
+ }
391
+
392
+ /* concatenate new data */
393
+ inbytesread = inbytespartial + inbytesread_next;
394
+ eventnum = inbytesread / sizeof(struct rawevent);
395
+ eventptr = inbuffer;
396
+
397
+ /* micro-optimization to initialize reference timestamp */
398
+ if ((!isset_tsref) && (eventnum > 0)) {
399
+ low = eventptr->low;
400
+ high = eventptr->high;
401
+
402
+ // Shift burden of swapping if 'legacy' format is used
403
+ // TODO: Consider a more efficient implementation.
404
+ if (islegacy) {
405
+ _swp = low;
406
+ low = high;
407
+ high = _swp;
408
+ }
409
+ tsref = ((ull)high << 22) | (low >> 10);
410
+ isset_tsref = 1; // we are done initializing
411
+ }
412
+
413
+ /* digest events */
414
+ for (i = 0; i < eventnum; i++) {
415
+
416
+ /* extract timestamp value */
417
+ // Assumed 4ps timestamps used
418
+ low = eventptr->low;
419
+ high = eventptr->high;
420
+ if (islegacy) {
421
+ _swp = low;
422
+ low = high;
423
+ high = _swp;
424
+ }
425
+ tsmeas = ((ull)high << 22) | (low >> 10);
426
+
427
+ /* calculate timestamp correction */
428
+ tsdiff = (tsmeas - tsref) & 0x3fffffffffffff; // truncate to 54-bit LSB per timestamp spec
429
+ tscorr = ((ll)(tsdiff >> FCORR_TBITS1) * fcorr) >> FCORR_TBITS2;
430
+ ts = tsmeas + tscorr + tsoverflowcorr;
431
+
432
+ /* write corrected timestamp to output buffer */
433
+ eventptr->high = ts >> 22;
434
+ eventptr->low = (ts << 10) | (low & 0x3ff);
435
+ #ifdef __DEBUG__
436
+ if (isdebugverbose) {
437
+ fprintf(stderr, "[debug] Raw event - %08x %08x\n", high, low);
438
+ fprintf(stderr, "[debug] | t_i: %014llx (%020llu)\n", tsmeas, tsmeas);
439
+ fprintf(stderr, "[debug] | t'_i: %014llx (%020llu)\n", ts, ts);
440
+ fprintf(stderr, "[debug] +---------- %08x %08x\n", eventptr->high, eventptr->low);
441
+ fflush(stderr);
442
+ }
443
+ #endif
444
+ if (islegacy) {
445
+ _swp = eventptr->low;
446
+ eventptr->low = eventptr->high;
447
+ eventptr->high = _swp;
448
+ }
449
+ outbuffer[outevents++] = *eventptr;
450
+ eventptr++;
451
+ }
452
+
453
+ /* accumulate timestamp corrections across batches */
454
+ #ifdef __SIZEOF_INT128__
455
+ _tsdiff = ((u128)tsdiff) << FCORR_OFLOWRESBITS;
456
+ _tscorr = ((i128)(_tsdiff >> FCORR_TBITS1) * fcorr) >> FCORR_TBITS2;
457
+ _tsoverflowcorr += _tscorr; // accumulates using higher resolution units
458
+ tsoverflowcorr = (ll)(_tsoverflowcorr >> FCORR_OFLOWRESBITS);
459
+ #else
460
+ tsoverflowcorr += tscorr;
461
+ #endif
462
+
463
+ /* update reference timestamp to keep within 20 hour overflow condition */
464
+ tsref = tsmeas;
465
+ }
466
+
467
+ // Read frequency correction values
468
+ // - Note partial buffer must be maintained, since there is no other
469
+ // check to verify integrity of values broken up between separate reads.
470
+ // - Note also this falls after reading the input buffer, but there is no
471
+ // particular reason why this order is chosen.
472
+ if (freqhandle && FD_ISSET(freqhandle, &rfds)) {
473
+ freqbytesread_next = read(freqhandle, freqbuffer_next, FREQBUFSIZE - freqbytespartial - 1);
474
+
475
+ // File/pipe closed -> proceed without any further fcorr updates
476
+ if (freqbytesread_next == 0) {
477
+ freqhandle = 0;
478
+ wmsg_s(FNAMELENGTH+25, "File/pipe '%s' closed.", freqfilename);
479
+ // no break here
480
+ }
481
+ if (freqbytesread_next == -1) {
482
+ wmsg_i(32, "Error %d on freqhandle read.", errno);
483
+ break;
484
+ }
485
+
486
+ /* concatenate new data */
487
+ freqbytesread = freqbytespartial + freqbytesread_next;
488
+
489
+ /* search for valid fcorr values */
490
+ int next_num_idx = 0, fcorr_tmp;
491
+ for (i = 0; i < freqbytesread; i++) {
492
+ if (freqbuffer[i] == '\n') {
493
+ freqbuffer[i] = 0; // zero-terminate for readint
494
+ fcorr_tmp = readint(&freqbuffer[next_num_idx]);
495
+ if (isdecimal) fcorr_tmp = (int) fcorr_tmp * FCORR_DTOB;
496
+ if (isupdate) fcorr_tmp = (int)round(((1+fcorr*FCORR_BTO1) * (1+fcorr_tmp*FCORR_BTO1) - 1)/FCORR_BTO1);
497
+ if (abs(fcorr_tmp) < FCORR_MAX) {
498
+ fcorr = fcorr_tmp;
499
+ #ifdef __DEBUG__
500
+ if (isdecimal) {
501
+ fprintf(stderr, "[debug] 'fcorr' updated to '%.3f' ppb.\n", fcorr / FCORR_DTOB / 10);
502
+ } else {
503
+ fprintf(stderr, "[debug] 'fcorr' updated to '%d' x 2^-34.\n", fcorr);
504
+ }
505
+ fflush(stderr);
506
+ #endif
507
+ }
508
+ next_num_idx = i+1; // continue reading
509
+ }
510
+ }
511
+
512
+ /* clear parsed numbers from freqbuffer */
513
+ if (next_num_idx > 0) {
514
+ freqbytespartial = freqbytesread - next_num_idx;
515
+ for (i = freqbytesread - freqbytespartial, j = 0; j < freqbytespartial; i++, j++) {
516
+ freqbuffer[j] = freqbuffer[i];
517
+ }
518
+ freqbuffer_next = &freqbuffer[freqbytespartial];
519
+ }
520
+ }
521
+
522
+ // TODO: Shift this back to select call to write only when pipe available,
523
+ // and increase buffer size of output pipe in case write unavailable.
524
+ // By same measure, do not flush only when output buffer is full.
525
+ /* write out events */
526
+ retval = write(outhandle, outbuffer, eventnum * sizeof(struct rawevent));
527
+ #ifdef __DEBUG__
528
+ if (isdebugverbose) {
529
+ for (i = 0; i < eventnum; i++) {
530
+ fprintf(stderr, "[debug] Verify: %08x %08x\n", outbuffer[i].high, outbuffer[i].low);
531
+ }
532
+ }
533
+ #endif
534
+ if (retval != eventnum * sizeof(struct rawevent)) {
535
+ wmsg_i(30, "Error %d on write.", errno);
536
+ break; // graceful close
537
+ }
538
+ outevents = 0; // clear outbuffer only after successful write
539
+ eventnum = 0; // clear events to avoid rewriting:
540
+ // occurs when 'freqhandle' available for reading, but
541
+ // 'inhandle' has no more events
542
+ }
543
+
544
+ /* free buffers */
545
+ free(inbuffer);
546
+ free(outbuffer);
547
+ if (freqfilename[0]) free(freqbuffer);
548
+ return 0;
549
+ }