| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- /*
- * RadAngel gamma spectrometer USB HID device control and readout program
- *
- * gcc -std=gnu99 -Wall radangel.c -o radangel -pthread -lm -lhidapi-libusb
- *
- * strip -s radangel
- *
- * echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="04d8", ATTR{idProduct}=="0100", MODE="0666"' | sudo tee /etc/udev/rules.d/99-kromek-radangel.rules
- *
- */
-
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <pthread.h>
- #include <string.h>
- #include <getopt.h>
- #include <sys/time.h>
- #include <hidapi/hidapi.h>
- #include <unistd.h> // usleep()
-
-
- #define MAX_STR 255
- #define MAX_CHANNEL 4096
- #define QUEUESIZE 1024
- #define DEV_VID 0x4d8
- #define DEV_PID 0x100
- #define LINE_MAX 4096
-
- // non blocking
- #define HID_TIMEOUT -1
- // 500 ms timeout
- //#define HID_TIMEOUT 500
-
-
- typedef struct {
- struct timespec start_time;
- } config;
-
- typedef struct {
- FILE* file;
- char* name;
- } output;
-
- typedef struct {
- hid_device* handle;
- config config;
- output event;
- output stats;
- } context;
-
- typedef struct {
- struct timespec event_time;
- unsigned short channel;
- } data;
-
- typedef struct {
- data data[QUEUESIZE];
- long head, tail;
- int full, empty;
- pthread_mutex_t *mutex;
- pthread_cond_t *not_full, *not_empty;
- context context;
- } queue;
-
- void usage(char* name);
- void info(const char* fmt, ...);
- void timespec_diff(struct timespec *start, struct timespec *stop, struct timespec *result);
- queue *queue_init(void);
- void queue_free(queue *q);
- void queue_add(queue *q, data src);
- void queue_del(queue *q, data *dst);
- void *event_source(void *args);
- void *event_sink(void *args);
- //void *update_context(void *args);
-
-
- static int verbose_flag; // Flag set by -v | --verbose
-
-
- void usage(char* name) {
- info("%s [<arguments>]\n\n", name);
- info("\t-h | --help Usage message.\n");
- info("\t-v | --verbose Verbose output.\n");
- info("\t-e <string> | --event <string> Event data file path.\n");
- info("\t-s <string> | --stats <string> Stats data file path.\n");
- info("\n");
- }
-
-
- int main(int argc, char **argv) {
- queue *fifo = NULL;
- pthread_t pro, con;
-
- struct timespec start_time = { 0, 0 };
- int c;
- int option_index = 0;
-
- fifo = queue_init();
- if (fifo == NULL) {
- info("Error: main queue_init() failed.\n");
- exit(1);
- }
- fifo->context.event.name = "-";
- fifo->context.event.file = stdout;
- fifo->context.stats.name = NULL;
- fifo->context.stats.file = NULL;
-
- while (1) {
- static struct option long_options[] = {
- {"verbose", no_argument, &verbose_flag, 1 },
- {"help", no_argument, NULL, 'h'},
- {"event", required_argument, NULL, 'e'},
- {"stats", required_argument, NULL, 's'},
- {0, 0, 0, 0 } // end of options
- };
- option_index = 0;
- c = getopt_long(argc, argv, "he:s:", long_options, &option_index);
- if (c == -1) { // end of options
- break;
- }
- switch (c) {
- case 0:
- if (long_options[option_index].flag != 0) { // option sets a flag
- break;
- }
- info("option %s", long_options[option_index].name);
- if (optarg) {
- info(" with arg %s", optarg);
- }
- info("\n");
- break;
- case 'e':
- fifo->context.event.name = optarg;
- break;
- case 's':
- fifo->context.stats.name = optarg;
- break;
- case 'h':
- usage(argv[0]);
- queue_free(fifo);
- exit(1);
- break;
- case '?': // getopt_long already printed an error message.
- break;
- default:
- abort();
- }
- }
- if (optind < argc) { // remaining command line arguments (not options).
- info("unknown argument: ");
- while (optind < argc) {
- info("%s ", argv[optind++]);
- }
- info("\n");
- queue_free(fifo);
- exit(1);
- }
-
-
- // ensure input
- if (hid_init()) {
- info("Error: unable to initialize hidapi.\n");
- queue_free(fifo);
- exit(1);
- }
- // open device later in read thread, might not be connected yet
- // measurement start
- clock_gettime(CLOCK_MONOTONIC, &start_time);
- fifo->context.config.start_time = start_time;
-
- // start the producer and consumer threads
- pthread_create(&pro, NULL, event_source, fifo);
- pthread_create(&con, NULL, event_sink, fifo);
- pthread_join(pro, NULL);
- pthread_join(con, NULL);
-
- if (fifo->context.event.file != NULL) {
- fclose(fifo->context.event.file);
- }
- if (fifo->context.stats.file != NULL) {
- fclose(fifo->context.stats.file);
- }
- if (fifo->context.handle != NULL) {
- hid_close(fifo->context.handle);
- }
- hid_exit(); // Free static HIDAPI objects
- queue_free(fifo);
-
- return 0;
- }
-
-
- void info(const char* fmt, ...) {
- va_list args;
- va_start(args, fmt);
- if (verbose_flag) {
- fprintf(stderr, fmt, args);
- }
- va_end(args);
- }
-
-
- queue *queue_init(void) {
- queue *q;
-
- q = (queue *) malloc (sizeof (queue));
- if (q == NULL) return (NULL);
-
- q->empty = 1;
- q->full = 0;
- q->head = 0;
- q->tail = 0;
-
- q->mutex = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t));
- pthread_mutex_init(q->mutex, NULL);
-
- q->not_full = (pthread_cond_t *) malloc (sizeof (pthread_cond_t));
- pthread_cond_init(q->not_full, NULL);
-
- q->not_empty = (pthread_cond_t *) malloc (sizeof (pthread_cond_t));
- pthread_cond_init(q->not_empty, NULL);
-
- return (q);
- }
-
-
- void queue_free(queue *q) {
- pthread_mutex_destroy(q->mutex);
- free(q->mutex);
-
- pthread_cond_destroy(q->not_full);
- free(q->not_full);
-
- pthread_cond_destroy(q->not_empty);
- free(q->not_empty);
-
- free(q);
- }
-
-
- void queue_add(queue *q, data src) {
- q->data[q->tail] = src;
- q->tail++;
-
- if (q->tail == QUEUESIZE) {
- q->tail = 0;
- }
-
- if (q->tail == q->head) {
- q->full = 1;
- }
-
- q->empty = 0;
-
- return;
- }
-
-
- void queue_del(queue *q, data *dst) {
- *dst = q->data[q->head];
- q->head++;
-
- if (q->head == QUEUESIZE) {
- q->head = 0;
- }
-
- if (q->head == q->tail) {
- q->empty = 1;
- }
-
- q->full = 0;
-
- return;
- }
-
-
- void *event_source(void *q) {
- queue *fifo;
- fifo = (queue *)q;
- data data;
-
- hid_device* handle = fifo->context.handle;
- unsigned char buffer[64];
- int result;
-
- struct timespec event_time = { 0, 0L };
- struct timespec sleep_time = { 0, 500000000L}; // retry time
-
- do { // read events loop
- while (handle == NULL) { // try to aquire handle
- handle = hid_open(DEV_VID, DEV_PID, NULL);
- if (handle != NULL) {
- fifo->context.handle = handle;
- } else {
- nanosleep(&sleep_time, NULL); // wait to retry
- }
- }
- while (handle != NULL) { // try to read data
- memset(buffer, 0, sizeof(buffer));
- result = hid_read_timeout(handle, buffer, sizeof(buffer), HID_TIMEOUT);
- if (result > 0) { // got some bytes
- break;
- } else if (result < 0) { // something went wrong: disconnect?
- hid_close(handle);
- handle = NULL; // invalidate handle for reaquire
- fifo->context.handle = handle;
- }
- }
-
- clock_gettime(CLOCK_MONOTONIC, &event_time);
-
- if (buffer[0] == 4) { // got an event
- data.channel = (((buffer[1] << 8) | buffer[2]) >> 4);
- data.event_time = event_time;
-
- // lock the queue, wait for space, push
- pthread_mutex_lock(fifo->mutex);
- while (fifo->full) { // queue is full, wait for consumer
- pthread_cond_wait(fifo->not_full, fifo->mutex); // unlocks, block, relock
- }
- queue_add(fifo, data);
- pthread_mutex_unlock(fifo->mutex);
- pthread_cond_signal(fifo->not_empty);
- }
-
- } while (1); // read events loop
-
- return (NULL);
- }
-
-
- void timespec_diff(struct timespec *start, struct timespec *stop, struct timespec *result) {
- if ((stop->tv_nsec - start->tv_nsec) < 0) {
- result->tv_sec = stop->tv_sec - start->tv_sec - 1;
- result->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000;
- } else {
- result->tv_sec = stop->tv_sec - start->tv_sec;
- result->tv_nsec = stop->tv_nsec - start->tv_nsec;
- }
- return;
- }
-
-
- void *event_sink(void *q) {
- queue *fifo;
- fifo = (queue *)q;
- data data;
-
- char *events_filename = fifo->context.event.name;
- char *stats_filename = fifo->context.stats.name;
- char *events_filemode = "a+";
- char *stats_filemode = "w";
- FILE *events_file = events_filename ? ((strncmp(events_filename, "-", 1) == 0) ? stdout : fopen(events_filename, events_filemode)) : NULL;
- FILE *stats_file = stats_filename ? ((strncmp(stats_filename, "-", 1) == 0) ? stdout : fopen(stats_filename, stats_filemode )) : NULL;
- fifo->context.event.file = events_file;
- fifo->context.stats.file = stats_file;
-
- struct timespec start_time = fifo->context.config.start_time;
- struct timespec event_time = { 0, 0L };
- struct timespec sleep_time = { 0, 500000000L}; // retry time
- long int tv_sec;
- unsigned long int tv_nsec;
-
- unsigned int channel = 0;
- unsigned long histogram[MAX_CHANNEL];
- memset(histogram, 0, MAX_CHANNEL * sizeof(unsigned long));
-
- const char event_header[] = "time\tchannel\n";
- const char event_format[] = "%ld.%03ld\t%d\n";
- const char stats_header[] = "channel\n";
- const char stats_format[] = "%ld\n";
- char line[LINE_MAX];
-
- int i, result = 1;
-
- // reconstruct event_time and histogram
- if (events_filename && events_file && (events_file != stdout)) {
- rewind(events_file);
- while (fgets(line, LINE_MAX, events_file) != NULL) {
- if(sscanf(line, event_format, &tv_sec, &tv_nsec, &channel) == 3) {
- event_time.tv_sec = tv_sec;
- event_time.tv_nsec = tv_nsec;
- histogram[channel % MAX_CHANNEL] += 1; // update histogram
- }
- }
- }
- if (events_filename && events_file && ((events_file == stdout) || (ftell(events_file) == 0))) {
- fprintf(events_file, event_header);
- }
-
- // readjust start_time
- timespec_diff(&event_time, &start_time, &start_time);
- fifo->context.config.start_time = start_time;
-
-
- if (stats_file && stats_filename) {
- stats_file = (strncmp(stats_filename, "-", 1) == 0) ? stdout : freopen(stats_filename, stats_filemode, stats_file);
- fifo->context.stats.file = stats_file;
- result = !!stats_file;
- if (stats_file) {
- result = fprintf(stats_file, stats_header);
- for (i = 0; i < MAX_CHANNEL; i++) {
- result = result && (fprintf(stats_file, stats_format,
- histogram[i]
- ) > 0);
- }
- fflush(stats_file);
- }
- }
-
-
- do { // output loop
- // lock the queue, wait for data, pull
- pthread_mutex_lock(fifo->mutex);
- while (fifo->empty) { // queue is empty, wait for producer
- pthread_cond_wait(fifo->not_empty, fifo->mutex); // unlock, block, relock
- }
- queue_del(fifo, &data);
- pthread_mutex_unlock (fifo->mutex);
- pthread_cond_signal(fifo->not_full);
-
- // event time since measurement start
- event_time = data.event_time;
- channel = data.channel;
- timespec_diff(&start_time, &event_time, &event_time);
-
- // update histogram
- histogram[channel % MAX_CHANNEL] += 1;
-
- if (events_file) {
- result = (fprintf(events_file, event_format,
- event_time.tv_sec,
- event_time.tv_nsec / 1000000,
- channel
- ) > 0);
- fflush(events_file);
- } else if (events_filename) {// try reopen
- nanosleep(&sleep_time, NULL); // wait to retry
- events_file = (strncmp(events_filename, "-", 1) == 0) ? stdout : freopen(events_filename, events_filemode, events_file);
- fifo->context.event.file = events_file;
- result = !!events_file;
- }
-
- if (stats_file && stats_filename) {
- stats_file = (strncmp(stats_filename, "-", 1) == 0) ? stdout : freopen(stats_filename, stats_filemode, stats_file);
- fifo->context.stats.file = stats_file;
- result = !!stats_file;
- if (stats_file) {
- for (i = 0; i < MAX_CHANNEL; i++) {
- result = result && (fprintf(stats_file, stats_format,
- histogram[i]
- ) > 0);
- }
- fflush(stats_file);
- }
- }
-
- } while (result); // output loop
-
- return (NULL);
- }
-
-
- /*
- void *update_context (void *q) {
- queue *fifo;
- fifo = (queue *)q;
-
- return (NULL);
- }
- */
|