/* * Abstraction of the counter device. * */ #include "dio.h" #include "params.h" #include "sock.h" #include "hdio.h" #include #include #include #define HCTR_TEST(functionCall) \ if( hdio_failed(error=(functionCall)) ) \ goto Error; \ else COUNTER counter; /* * get a pointer to the current sample */ static SAMPLE* cur_sample(COUNTER* cp) { return &cp->sample_array[cp->sample_index]; } /* * get a pointer to the num'th previous sample */ static SAMPLE* prv_sample(COUNTER* cp, int num) { int idx = (cp->sample_index + SAMPLE_ARRAY_SZ - num) % SAMPLE_ARRAY_SZ; return &cp->sample_array[idx]; } void make_report(COUNTER* cp) { dprintf(0, "make_report\n"); int i; SAMPLE* sp = cur_sample(cp); cp->report = *sp; cp->report.average_rate = cp->report.counter_rate; cp->report.minimum_rate = cp->report.counter_rate; cp->report.maximum_rate = cp->report.counter_rate; for (i = 1; i <= cp->params.report_period; ++i) { SAMPLE* psp; psp = prv_sample(cp, i); if (psp->valid) { cp->report.time_delta = time_diff(&sp->timestamp, &psp->timestamp); cp->report.count_delta = sp->count64 - psp->count64; if (cp->report.time_delta > 0.0) cp->report.average_rate = cp->report.count_delta / cp->report.time_delta; if (i < cp->params.report_period) { cp->report.num_polls += psp->num_polls; if (psp->counter_rate < cp->report.minimum_rate) cp->report.minimum_rate = psp->counter_rate; if (psp->counter_rate > cp->report.maximum_rate) cp->report.maximum_rate = psp->counter_rate; } } } sp = &cp->report; } #if 0 /* * given two samples, compute the count-rate */ static double compute_rate(COUNTER* cp, SAMPLE* cur, SAMPLE* prv) { double result = 0.0; uInt32 delta_counter; if (cp->params.direction == COUNT_DOWN) delta_counter = prv->counter_value - cur->counter_value; else delta_counter = cur->counter_value - prv->counter_value; double delta_timer = time_diff(&cur->timestamp, &prv->timestamp); result = 1.0 * delta_counter / delta_timer; return result; } /* * given two samples, compute the average rate */ static double compute_average(COUNTER* cp, SAMPLE* cur, SAMPLE* prv) { double result = 0.0; if (cur == prv) result = cur->counter_rate; else result = compute_rate(cp, cur, prv); return result; } #endif void cntr_send(COUNTER* cp, int n) { SAMPLE* sp = cur_sample(cp); BUFFER buffer; buffer.length = 0; snprintf(buffer.body, sizeof(buffer.body), "Time: %s, Count: %10llu, Delta: %6d, Time: %8.6f, Rate: %8.2f, Ave: %8.2f\r\n", make_timestamp(&sp->timestamp), sp->counter_value, sp->count_delta, sp->time_delta, sp->counter_rate, sp->average_rate); buffer.length = strlen(buffer.body); sock_send(n, &buffer); } void cntr_read(COUNTER* cp, int n) { SAMPLE* sp = cur_sample(cp); BUFFER buffer; buffer.length = 0; snprintf(buffer.body, sizeof(buffer.body), "READ %c%c%c%c %s %.6f %10llu %8.2f\r\n", cp->state == counter_idle ? 'I' : cp->state == counter_stopped ? 'S' : cp->state == counter_running ? 'R' : cp->state == counter_paused ? 'P' : '?', cp->terminal_due ? 'T' : ' ', cp->range_error == 0 ? ' ' : 'R', cp->range_gated ? 'G' : ' ', make_timestamp(&sp->timestamp), cp->accumulated.tv_sec + .000001 * cp->accumulated.tv_usec, sp->counter_value, sp->counter_rate); buffer.length = strlen(buffer.body); sock_send(n, &buffer); } void cntr_print(COUNTER* cp, FILE* fd) { SAMPLE* sp = cur_sample(cp); fprintf(fd, "Time: %s, Count: %10llu, Delta: %6d, Time: %8.6f, Rate: %8.2f, Ave: %8.2f\r\n", make_timestamp(&sp->timestamp), sp->counter_value, sp->count_delta, sp->time_delta, sp->counter_rate, sp->average_rate); fflush(fd); } /* * Finalise the current sample and move on to the next */ void cntr_sample(COUNTER* cp) { SAMPLE* psp = cur_sample(cp); dprintf(0, "cntr_sample: %4d\r\n" " polls: %4d\r\n" " time: %4s\r\n" " counter: %10llu\r\n" " count64: %10llu\r\n" " c_delta: %d\r\n" " t_delta: %6.3f\r\n" " rate: %10g\n", cp->sample_index, psp->num_polls, make_timestamp(&psp->timestamp), psp->counter_value, psp->count64, psp->count_delta, psp->time_delta, psp->counter_rate); cp->sample_timer = cp->current_time; ++cp->sample_counter; if (++cp->sample_index >= SAMPLE_ARRAY_SZ) cp->sample_index = 0; SAMPLE* sp = cur_sample(cp); *sp = *psp; sp->valid = true; sp->num_polls = 0; sp->sample_counter = cp->sample_counter; sp->poll_counter = cp->poll_counter; } void cntr_report(COUNTER* cp) { dprintf(0, "cntr_report\n"); /* * Set the time for this report */ cp->report_timer = cp->current_time; BUFFER buffer; SAMPLE* sp; sp = &cp->report; char* str = make_timestamp(&sp->timestamp); snprintf(buffer.body, sizeof(buffer.body), "%s (%6.3f), %10llu (%8d), %8.2f (%8.2f,%8.2f,%8.2f)\r\n", str, sp->time_delta, sp->counter_value, sp->count_delta, sp->counter_rate, sp->minimum_rate, sp->average_rate, sp->maximum_rate); buffer.length = strlen(buffer.body); //fputs(buffer.body, stdout); sock_report(&buffer, 1); snprintf(buffer.body, sizeof(buffer.body), "REPORT %s %10llu %8.2f (%8.2f,%8.2f,%8.2f)\r\n", str, sp->counter_value, sp->counter_rate, sp->minimum_rate, sp->average_rate, sp->maximum_rate); buffer.length = strlen(buffer.body); //fputs(buffer.body, stdout); sock_report(&buffer, 2); } /** * Initialise the counter * * Initialise all of the control data associated with the logical counter. * * Create a 64-bit physical counter and start it. */ int cntr_init(COUNTER* cp, char* name) { int error = 0; char errBuff[2048]={'\0'}; memset(cp, 0, sizeof(COUNTER)); strncpy(cp->name, name, sizeof(cp->name)); cp->params.poll_period = 1000; /* milliseconds between polls */ cp->params.sample_period = 10; /* polls between sample calcs */ cp->params.report_period = 3; /* samples between reports */ cp->state = counter_stopped; struct timeval now; gettimeofday(&now, NULL); cp->current_time = now; cp->previous_time = now; cp->sample_timer = now; cp->report_timer = now; HCTR_TEST(hdio_ctor(name, &cp->private_data)); SAMPLE* sp = cur_sample(cp); sp->timestamp = now; sp->counter_value = cp->current_count; sp->valid = true; cntr_sample(cp); return 0; Error: hdio_errmsg(errBuff, sizeof(errBuff)); printf("DAQmx Error: %s\n", errBuff); return error; return 0; } /** * Start the logical counter * * Read the value of the physical counter and set the state to running */ int cntr_start(COUNTER *cp) { int error = 0; char errBuff[2048]={'\0'}; struct timeval now; /* start the counter object */ gettimeofday(&now, NULL); cp->current_time = now; cp->start_time = cp->current_time; cp->current_count = cp->params.initial_count; cp->accumulated.tv_sec = 0; cp->accumulated.tv_usec = 0; cp->poll_counter = 0; cp->sample_counter = 0; cp->terminal_due = false; cp->state = counter_running; cp->previous_time = cp->current_time; HCTR_TEST(hdio_read(cp->private_data, &cp->count64)); make_report(cp); return error; Error: cntr_errmsg(errBuff, sizeof(errBuff)); printf("DAQmx Error: %s\n", errBuff); return error; } int cntr_stop(COUNTER *cp) { int error = 0; cp->stop_time = cp->current_time; cp->state = counter_stopped; return error; } int cntr_pause(COUNTER *cp) { if (cp->state == counter_running) cp->state = counter_paused; return 0; } int cntr_resume(COUNTER *cp) { if (cp->state == counter_paused) cp->state = counter_running; return 0; } static void cntr_event(COUNTER *cp, char* event) { BUFFER buffer; sprintf(buffer.body, "EVENT %s %s\r\n", event, make_timestamp(&cp->current_time)); buffer.length = strlen(buffer.body); dprintf(0, "%s", buffer.body); //sock_report(&buffer, 0); sock_report(&buffer, 1); sock_report(&buffer, 2); } static void cntr_range_check(COUNTER* cp, int mode) { PARAMETERS* pp = &cp->params; if (pp->range_check_enable) { if (pp->range_mode == mode) { double test; SAMPLE* sp = cur_sample(cp); if (mode == 1) test = sp->counter_rate; else if (mode == 2) test = sp->counter_rate; else test = cp->report.average_rate; if (pp->range_low > 0 && pp->range_low > test) { if (cp->range_error != 1) cntr_event(cp, "RANGE OUT LOW"); cp->range_error = 1; if (pp->range_gate_enable) cp->range_gated = true; else cp->range_gated = false; } else if (pp->range_high > 0 && pp->range_high < test) { if (cp->range_error != 2) cntr_event(cp, "RANGE OUT HIGH"); cp->range_error = 2; if (pp->range_gate_enable) cp->range_gated = true; else cp->range_gated = false; } else { if (cp->range_error != 0) cntr_event(cp, "RANGE IN"); cp->range_error = 0; cp->range_gated = false; } } } else { /* If range check has been disabled while in error - reset */ if (cp->range_error != 0) cntr_event(cp, "RANGE IN"); cp->range_error = 0; cp->range_gated = false; } } static void cntr_test_term(COUNTER* cp) { PARAMETERS* pp = &cp->params; SAMPLE* sp = cur_sample(cp); SAMPLE* psp = prv_sample(cp, 1); if (!cp->terminal_due) { if (pp->terminal_check_type == 1) { if (pp->direction == COUNT_DOWN) { /* * decremented to or through terminal */ if ((sp->counter_value <= pp->terminal_count && psp->counter_value > pp->terminal_count) || (sp->counter_value > psp->counter_value && psp->counter_value > pp->terminal_count)) { cp->terminal_due = true; } } else { /* * incremented to or through terminal */ if ((sp->counter_value >= pp->terminal_count && psp->counter_value < pp->terminal_count) || (sp->counter_value < psp->counter_value && psp->counter_value > pp->terminal_count)) { cp->terminal_due = true; } } } else if (pp->terminal_check_type == 2) { if (cp->accumulated.tv_sec >= pp->terminal_count) cp->terminal_due = true; } if (cp->terminal_due) { cntr_event(cp, "TERMINAL"); make_report(cp); cntr_stop(cp); } } } /* * poll the physical counter */ int cntr_poll(COUNTER* cp) { char errBuff[2048]={'\0'}; unsigned long long current_count_local; int count_delta_local; int error=0; /* read the value from the hardware counter to a temp */ ++cp->poll_counter; HCTR_TEST(hdio_read(cp->private_data, ¤t_count_local)); dprintf(0, "cntr_poll = %llu @ %s\n", current_count_local, make_timestamp(&cp->current_time)); SAMPLE* sp = cur_sample(cp); SAMPLE* psp = prv_sample(cp, 1); /* calculate the number since last time and save new value */ count_delta_local = current_count_local - cp->count64; cp->count64 = current_count_local; sp->num_polls += 1; /* * If the counter is running and not gated increment the count and runtime */ if (cp->state == counter_running && !(cp->params.range_gate_enable && cp->range_gated)) { if (cp->params.direction == COUNT_DOWN) { cp->current_count -= count_delta_local; } else { cp->current_count += count_delta_local; } /* * Add the time difference to the accumulated time */ cp->accumulated.tv_sec += cp->current_time.tv_sec - sp->timestamp.tv_sec; /* prevent negative tv_usec by borrowing one second in microseconds */ cp->accumulated.tv_usec += 1000000; cp->accumulated.tv_usec += cp->current_time.tv_usec; cp->accumulated.tv_usec -= sp->timestamp.tv_usec; if (cp->accumulated.tv_usec >= 1000000) { /* carry the seconds */ cp->accumulated.tv_sec += cp->accumulated.tv_usec / 1000000; cp->accumulated.tv_usec %= 1000000; } /* pay back the borrowed second */ cp->accumulated.tv_sec -= 1; } /* calculate and check the count-rate between polls */ sp->count_delta = cp->count64 - sp->count64; sp->time_delta = time_diff(&cp->current_time, &sp->timestamp); sp->counter_rate = (double) sp->count_delta / sp->time_delta; cntr_range_check(cp, 2); /* poll range check */ cp->previous_time = cp->current_time; /* save counter values in the sample */ sp->counter_value = cp->current_count; sp->count64 = cp->count64; sp->timestamp = cp->current_time; /* calculate the count-rate for this sample so far */ sp->count_delta = sp->count64 - psp->count64; sp->time_delta = time_diff(&sp->timestamp, &psp->timestamp); sp->counter_rate = (double) sp->count_delta / sp->time_delta; /* test for the occurrence of a terminal event */ cntr_test_term(cp); /* check if it is time to roll the sample */ if (cntr_time_to_next_sample(cp) <= 0) { /* check if it is time to roll the report */ if (cntr_time_to_next_report(cp) <= 0) { make_report(cp); cntr_range_check(cp, 0); /* report range check */ cntr_report(cp); } cntr_range_check(cp, 1); /* sample range check */ cntr_sample(cp); } return error; Error: cntr_errmsg(errBuff, sizeof(errBuff)); printf("DAQmx Error: %s\n", errBuff); return error; } void cntr_term(COUNTER* cp) { if (cp->private_data) { hdio_dtor(&cp->private_data); cp->private_data = NULL; } cp->state = counter_idle; } bool cntr_fatal(int error) { return hdio_failed(error); } void cntr_errmsg(char* buff, int len) { hdio_errmsg(buff, len); } double cntr_time_to_next_report(COUNTER* cp) { uint64 last_report; uint64 timeofday; int timeout; struct timeval now; now = cp->current_time; last_report = 1000 * (uint64) cp->report_timer.tv_sec; last_report += (uint64) cp->report_timer.tv_usec / 1000; timeofday = 1000 * (uint64) now.tv_sec; timeofday += (uint64) now.tv_usec / 1000; timeout = cp->params.poll_period * cp->params.sample_period * cp->params.report_period; if ((last_report / timeout) != (timeofday / timeout)) return 0.0; timeout = timeout - timeofday % timeout; return 0.001 * timeout; } double cntr_time_to_next_sample(COUNTER* cp) { uint64 last_sample; uint64 timeofday; int timeout; struct timeval now; now = cp->current_time; last_sample = 1000 * (uint64) cp->sample_timer.tv_sec; last_sample += (uint64) cp->sample_timer.tv_usec / 1000; timeofday = 1000 * (uint64) now.tv_sec; timeofday += (uint64) now.tv_usec / 1000; timeout = cp->params.poll_period * cp->params.sample_period; if ((last_sample / timeout) != (timeofday / timeout)) return 0.0; timeout = timeout - timeofday % timeout; return 0.001 * timeout; }