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/__init__.py +14 -0
- fpfind/_postinstall +5 -0
- fpfind/apps/fpplot.py +286 -0
- fpfind/apps/g2_two_timestamps.py +38 -0
- fpfind/apps/generate_freqcd_testcase.py +150 -0
- fpfind/apps/generate_freqdetuneg2.py +50 -0
- fpfind/apps/show-timestamps +16 -0
- fpfind/apps/show-timestamps-hex +16 -0
- fpfind/apps/tsviz.py +50 -0
- fpfind/fpfind.py +1008 -0
- fpfind/freqcd +81 -0
- fpfind/freqcd.c +549 -0
- fpfind/freqservo.py +333 -0
- fpfind/lib/_logging.py +177 -0
- fpfind/lib/constants.py +98 -0
- fpfind/lib/getopt.c +106 -0
- fpfind/lib/getopt.h +12 -0
- fpfind/lib/parse_epochs.py +699 -0
- fpfind/lib/parse_timestamps.py +1316 -0
- fpfind/lib/typing.py +27 -0
- fpfind/lib/utils.py +716 -0
- fpfind-3.3.0.data/scripts/freqcd +81 -0
- fpfind-3.3.0.data/scripts/show-timestamps +16 -0
- fpfind-3.3.0.data/scripts/show-timestamps-hex +16 -0
- fpfind-3.3.0.dist-info/METADATA +126 -0
- fpfind-3.3.0.dist-info/RECORD +29 -0
- fpfind-3.3.0.dist-info/WHEEL +4 -0
- fpfind-3.3.0.dist-info/entry_points.txt +7 -0
- fpfind-3.3.0.dist-info/licenses/LICENSE +339 -0
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
|
+
}
|