-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathSPAD_simulation.c
398 lines (331 loc) · 9.76 KB
/
SPAD_simulation.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
/*
* SPAD_simulation.c
*
* Simulation of SPAD detections - outputs counting statistics.
* Needs an afterpulsing generation table - see the comment in main()
*/
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<math.h>
#include<string.h>
#include<unistd.h>
#include<inttypes.h>
// multithreading
#define NUM_THREADS 3
// 1 MICROSECOND is the unit of time here
#define WINDOW 10. // time window for count statistics
#define DEADTIME 0.024 // recovery time
#define AP_MEAN 0.0171 // afterpulse probability
#define TWILIGHT_COEFF 0.0032 // afterpulse-mean linear coefficient.
// Input reset time in microseconds.
// for nonlinear twilight pulsing
//~ #define TWILIGHT_PROB_EXPLICIT 0.002
// number of detections to simulate
#define SAMPLES ((long long) 1e9)
// allocation constants
#define DEFAULT_AP_QUEUE_SIZE 10
#define AP_MAX_ORDER 10
#define HIST_MAX_SIZE 1000
// number of random generators
#define NUMBER_OF_PRNGS 4
#define PRNG_BUFSIZE 256
// normalization constant for generating random floats
#define RAND_M_F (((double)RAND_MAX)+1.)
// afterpulse table floating point format
typedef float APfloat;
// struct for passing to child threads
typedef struct {
int threadNum;
APfloat *APdist; // AP distribution
long dist_size; // AP dist. size
double mean; // mean incident rate
unsigned int seed[NUMBER_OF_PRNGS]; // seeds for random generators
volatile unsigned long long samples; // sample monitoring
volatile int go; // for telling the thread to stop
unsigned long long *returnHist;
double simTime;
} childArg;
// struct for afterpulses waiting to happen
typedef struct {
double *queue;
int len; // population length
int alloc_len; // allocation length
} APqueue;
void RemoveAPfromQueue(APqueue *apq, int i) {
for (apq->len--; i < apq->len; i++) {
apq->queue[i] = apq->queue[i+1];
}
}
void AddAPtoQueue(APqueue *apq, double t) {
if (apq->len == apq->alloc_len) { // check allocated space
void *realloc_ptr;
realloc_ptr = realloc( apq->queue,
(apq->alloc_len+10)*sizeof(*apq->queue) );
if (realloc_ptr == NULL) {
fprintf(stderr, "ERROR: cannot reallocate AP queue.\n");
return;
}
apq->alloc_len += 10;
apq->queue = realloc_ptr;
}
apq->queue[apq->len++] = t;
}
// the main simulation function run by each thread
void * generateHist(void *arg) {
unsigned long long i, j;
childArg *a = (childArg *) arg;
unsigned long long hist[HIST_MAX_SIZE];
for (i=0; i<HIST_MAX_SIZE; i++) hist[i] = 0LL;
#ifndef TWILIGHT_PROB_EXPLICIT
double twilightProb = a->mean*TWILIGHT_COEFF;
#else
double twilightProb = TWILIGHT_PROB_EXPLICIT;
#endif
// AP queue allocation
APqueue apq;
apq.len = 0;
apq.alloc_len = DEFAULT_AP_QUEUE_SIZE;
apq.queue = malloc(DEFAULT_AP_QUEUE_SIZE*sizeof(double));
if (apq.queue == NULL) {
fprintf(stderr, "ERROR: cannot allocate memory for AP queue\n");
}
// Poisson CDF for generating afterpulses
double poisson[AP_MAX_ORDER];
poisson[0] = exp(-AP_MEAN);
for (i=1; i<AP_MAX_ORDER; i++) {
poisson[i] = (poisson[i-1]*AP_MEAN)/i;
}
for (i=1; i<AP_MAX_ORDER; i++) {
poisson[i] += poisson[i-1];
}
// declaration of random generators
// see manpage random_r(3)
struct random_data buf[NUMBER_OF_PRNGS];
memset(&buf[0], 0, NUMBER_OF_PRNGS*sizeof(struct random_data));
char statebuf[NUMBER_OF_PRNGS][PRNG_BUFSIZE];
memset(&statebuf[0][0], 0, NUMBER_OF_PRNGS*PRNG_BUFSIZE);
int32_t r_int;
double t=0.; // time
double r, rRad; // for storing random numbers
int nAP; // number of afterpulses
int indexAP; // for generating afterpulse delay
int count=0; // main detection counter
double tOverflow = 0.; // time overflow
// PRNG initialisation
for (i=0; i<NUMBER_OF_PRNGS; i++) {
if (initstate_r( a->seed[i], &statebuf[i][0], PRNG_BUFSIZE,
&buf[i] ))
{
fprintf(stderr, "initstate_r ERROR\n");
}
}
// MAIN LOOP
// each loop generates the time of one detection
for (i=0; a->go; ++i) {
t += DEADTIME; // add after each detection
// clear old afterpulses
j=0;
while (j<apq.len) {
if (apq.queue[j] <= t) {
RemoveAPfromQueue(&apq, j);
} else j++;
}
// create afterpulses as a point process
// generate nAP as a Poissonian variable
random_r(&buf[0], &r_int); // PRNG 1
r = r_int/RAND_M_F;
for (nAP=0; nAP < AP_MAX_ORDER; nAP++) {
if (r < poisson[nAP]) break;
}
// generate delay for each afterpulse
for (j=0; j<nAP; j++) {
random_r(&buf[1], &r_int); // PRNG 2
indexAP = (int) ((r_int/RAND_M_F)*(a->dist_size));
AddAPtoQueue(&apq, t + a->APdist[indexAP]);
}
random_r(&buf[2], &r_int); // PRNG 3
r = r_int/RAND_M_F;
// only add time if there is no twilight pulse
if (r > twilightProb) {
random_r(&buf[3], &r_int); // PRNG 4
rRad = r_int/RAND_M_F;
// initialize time to the next Poissonian event
t = t - log(1.-rRad)/(a->mean);
// search for afterpulses that come before
for (j=0; j<apq.len; j++) {
if (apq.queue[j] < t) {
t = apq.queue[j];
}
}
}
// check time window and wrap around
while (t >= WINDOW) {
t -= WINDOW;
for (j=0; j<apq.len; j++) {
apq.queue[j] -= WINDOW;
}
tOverflow += WINDOW;
// increment histogram
if (count < HIST_MAX_SIZE) hist[count]++;
count = 0;
}
count++;
// update samples every now and then
if (i % 1000000 == 0) {
a->samples = i;
}
}
// simulation statistics
a->simTime = tOverflow + t;
a->samples = i;
// return the result
a->returnHist = (unsigned long long *)
malloc(sizeof(unsigned long long)*HIST_MAX_SIZE);
memcpy( a->returnHist, hist,
sizeof(unsigned long long)*HIST_MAX_SIZE);
free(apq.queue);
return 0;
}
int main(int argc, char *argv[]) {
if (argc < 4) {
printf( "ARGUMENTS: <AP dist table> <incident mean rate>"
" <output file>\n");
return 0;
}
// threading variables
pthread_t threads[NUM_THREADS];
int thread_res;
childArg thread_args[NUM_THREADS];
unsigned long long overallProgress;
// while thread_args can be close together in memory and are
// accessed by different threads, the writing is kept to a minimum
unsigned long long i;
int j;
// afterpulsing distribution
APfloat *APdist;
long dist_size;
// histogram variables
int lastElement;
unsigned long long hist[HIST_MAX_SIZE];
for (i=0; i<HIST_MAX_SIZE; i++) hist[i] = 0LL;
unsigned long int counts = 0;
double simTime = 0.;
// import afterpulse time distribution table
// needs to be a sequence of APfloats distributed so that if
// we pick an element uniformly at random, the element would be
// distributed in time according to the AP distribution
// in other words, an inverse CDF of the AP delay
FILE *d = fopen(argv[1], "rb");
if (d == NULL) {
fprintf(stderr, "Error opening file %s\n", argv[1]);
return 0;
}
fseek(d, 0, SEEK_END);
APdist = (APfloat *) malloc(ftell(d));
if (APdist == NULL) {
fprintf(stderr, "Failed to allocate memory\n");
fclose(d);
return 0;
}
if (ftell(d) % sizeof(APfloat) != 0) {
fprintf(stderr, "File does not contain APfloats\n");
fclose(d);free(APdist);
return 0;
}
dist_size = ftell(d)/sizeof(APfloat);
fseek(d, 0, SEEK_SET);
if (fread(APdist, sizeof(APfloat), dist_size, d) == 0) {
fprintf(stderr, "Error reading from %s\n", argv[1]);
fclose(d);free(APdist);
return 0;
}
fclose(d);
// open output file
FILE *f = fopen(argv[3],"w");
if (f == NULL) {
fprintf(stderr, "Cannot open output file %s\n", argv[3]);
free(APdist);
return 0;
}
// random seed
srandom(time(NULL));
// initialize thread variables
for (i=0; i<NUM_THREADS; i++) {
thread_args[i].threadNum = i;
thread_args[i].APdist = APdist;
thread_args[i].dist_size = dist_size;
sscanf(argv[2], "%lf", &thread_args[i].mean); // read mean rate
for (j=0; j<NUMBER_OF_PRNGS; j++) { // initialize random seeds
thread_args[i].seed[j] = random();
}
thread_args[i].samples = 0LL;
thread_args[i].go = 1;
}
// start threads
for (i=0; i<NUM_THREADS; i++) {
thread_res = pthread_create(&threads[i], NULL, &generateHist,
&thread_args[i]);
if (thread_res != 0) {
fprintf(stderr, "Starting thread %llu resulted in %d\n", i,
thread_res);
}
}
// waiting loop
while (1) {
overallProgress = 0LL;
// sum the numbers of samples
for (i=0; i<NUM_THREADS; i++) {
overallProgress += thread_args[i].samples;
}
// Progress tracking
printf("%2.2lf %%\r", (float) overallProgress/SAMPLES*100);
fflush(stdout);
// if enough samples have been generated, break
if (overallProgress >= SAMPLES) break;
usleep(100000);
}
// set the red light for the loops to finish
for (i=0; i<NUM_THREADS; i++) {
thread_args[i].go = 0;
}
for (i=0; i<NUM_THREADS; i++) {
thread_res = pthread_join(threads[i], NULL);
if (thread_res != 0) {
fprintf(stderr, "Joining thread %llu resulted in %d\n", i,
thread_res);
}
// add the simulated histogram to the total
for (j=0; j<HIST_MAX_SIZE; j++) {
hist[j] += thread_args[i].returnHist[j];
}
free(thread_args[i].returnHist);
// simulation time and counts
simTime += thread_args[i].simTime;
counts += thread_args[i].samples;
}
// find the index of the last non-zero element
for (lastElement=HIST_MAX_SIZE-1; lastElement>0; lastElement--) {
if (hist[lastElement] > 0) break;
}
printf( "Counts: %lu\nTime: %lf\nDet. rate: %lf\n",
counts, simTime, counts/simTime);
#ifndef TWILIGHT_PROB_EXPLICIT
double twilightProb = thread_args[0].mean*TWILIGHT_COEFF;
#else
double twilightProb = TWILIGHT_PROB_EXPLICIT;
#endif
// print file header with parameters
fprintf( f, "WINDOW %lf\nRATE_PER_TIMEUNIT %.16lf\nDEADTIME %lf\n"
"AFTERPULSE_MEAN %lf\nTWILIGHT_PROB %lf\n", WINDOW,
thread_args[0].mean, DEADTIME, AP_MEAN, twilightProb);
// print the histogram
for (i=0; i<=lastElement; i++) {
if (i>0) putc(' ', f);
fprintf(f, "%llu", hist[i]);
}
fclose(f);
free(APdist);
return 0;
}