/* * Abstraction of the counter device. * */ #include "device.h" #include "params.h" #include "sock.h" #include "hware.h" #include #include #include #define HWARE_TEST(functionCall) \ if( hware_failed(error=(functionCall)) ) \ goto Error; \ else /* * get a pointer to the current sample */ static SAMPLE* cur_sample(DEVICE* device) { return &device->sample_array[device->sample_index]; } /* * get a pointer to the num'th previous sample */ static SAMPLE* prv_sample(DEVICE* device, int num) { int idx = (device->sample_index + SAMPLE_ARRAY_SZ - num) % SAMPLE_ARRAY_SZ; return &device->sample_array[idx]; } void make_report(DEVICE* device) { dbg_printf(0, "make_report\n"); int i; SAMPLE* sp = cur_sample(device); device->report = *sp; device->report.average_rate = device->report.counter_rate; device->report.minimum_rate = device->report.counter_rate; device->report.maximum_rate = device->report.counter_rate; for (i = 1; i <= device->params.report_period; ++i) { SAMPLE* psp; psp = prv_sample(device, i); if (psp->valid) { device->report.time_delta = time_diff(&sp->timestamp, &psp->timestamp); device->report.count_delta = sp->count64 - psp->count64; if (device->report.time_delta > 0.0) device->report.average_rate = device->report.count_delta / device->report.time_delta; if (i < device->params.report_period) { device->report.num_polls += psp->num_polls; if (psp->counter_rate < device->report.minimum_rate) device->report.minimum_rate = psp->counter_rate; if (psp->counter_rate > device->report.maximum_rate) device->report.maximum_rate = psp->counter_rate; } } } sp = &device->report; } #if 0 /* * given two samples, compute the count-rate */ static double compute_rate(DEVICE* device, SAMPLE* cur, SAMPLE* prv) { double result = 0.0; uInt32 delta_counter; if (device->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(DEVICE* device, SAMPLE* cur, SAMPLE* prv) { double result = 0.0; if (cur == prv) result = cur->counter_rate; else result = compute_rate(device, cur, prv); return result; } #endif void device_send(DEVICE* device, int n) { SAMPLE* sp = cur_sample(device); 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 device_read(DEVICE* device, int n) { SAMPLE* sp = cur_sample(device); BUFFER buffer; buffer.length = 0; snprintf(buffer.body, sizeof(buffer.body), "READ %c%c%c%c %s %.6f %10llu %8.2f\r\n", device->state == device_idle ? 'I' : device->state == device_stopped ? 'S' : device->state == device_running ? 'R' : device->state == device_paused ? 'P' : '?', device->terminal_due ? 'T' : ' ', device->range_error == 0 ? ' ' : 'R', device->range_gated ? 'G' : ' ', make_timestamp(&sp->timestamp), device->accumulated.tv_sec + .000001 * device->accumulated.tv_usec, sp->counter_value, sp->counter_rate); buffer.length = strlen(buffer.body); sock_send(n, &buffer); } void device_print(DEVICE* device, FILE* fd) { SAMPLE* sp = cur_sample(device); 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 device_sample(DEVICE* device) { SAMPLE* psp = cur_sample(device); dbg_printf(0, "device_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", device->sample_index, psp->num_polls, make_timestamp(&psp->timestamp), psp->counter_value, psp->count64, psp->count_delta, psp->time_delta, psp->counter_rate); device->sample_timer = device->current_time; ++device->sample_counter; if (++device->sample_index >= SAMPLE_ARRAY_SZ) device->sample_index = 0; SAMPLE* sp = cur_sample(device); *sp = *psp; sp->valid = true; sp->num_polls = 0; sp->sample_counter = device->sample_counter; sp->poll_counter = device->poll_counter; } void device_report(DEVICE* device) { dbg_printf(0, "device_report\n"); /* * Set the time for this report */ device->report_timer = device->current_time; BUFFER buffer; SAMPLE* sp; sp = &device->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, device); 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, device); } /** * Initialise the counter * * Initialise all of the control data associated with the logical counter. * * Create a 64-bit physical counter and start it. */ int device_init(DEVICE** cpp, char* name) { int error = 0; char errBuff[2048]={'\0'}; SAMPLE* sp = NULL; DEVICE* device = (DEVICE*) malloc(sizeof(DEVICE)); *cpp = device; memset(device, 0, sizeof(DEVICE)); strncpy(device->name, name, sizeof(device->name)); device->params.poll_period = 1; /* milliseconds between polls */ device->params.sample_period = 1000; /* polls between sample calcs */ device->params.report_period = 3; /* samples between reports */ device->state = device_stopped; struct timeval now; gettimeofday(&now, NULL); device->current_time = now; device->previous_time = now; device->sample_timer = now; device->report_timer = now; HWARE_TEST(hware_ctor(name, &device->private_data)); sp = cur_sample(device); sp->timestamp = now; sp->counter_value = device->current_count; sp->valid = true; device_sample(device); return 0; Error: hware_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 device_start(DEVICE *device) { int error = 0; char errBuff[2048]={'\0'}; struct timeval now; int value; SAMPLE* sp = NULL; SAMPLE* psp = NULL; PARAMETERS* pp = &device->params; /* start the counter object */ gettimeofday(&now, NULL); device->current_time = now; device->start_time = device->current_time; device->current_count = device->params.initial_count; device->accumulated.tv_sec = 0; device->accumulated.tv_usec = 0; device->poll_counter = 0; device->sample_counter = 0; device->terminal_due = false; device->state = device_running; device->previous_time = device->current_time; dbg_printf(0, "Setting input line to %d\n", pp->source); HWARE_TEST(hware_source(device->private_data, pp->source)); device->source = pp->source; HWARE_TEST(hware_read(device->private_data, &device->count64)); sp = cur_sample(device); sp->timestamp = device->current_time; sp->counter_value = device->current_count; psp = prv_sample(device, 1); psp->timestamp = device->current_time; psp->counter_value = device->current_count; make_report(device); if (pp->output_line == 1) value = 1; else if (pp->output_line == 2) value = 0; else value = -1; dbg_printf(0, "Setting output line to %d\n", value); HWARE_TEST(hware_outp(device->private_data, value)); return error; Error: device_errmsg(errBuff, sizeof(errBuff)); printf("DAQmx Error: %s\n", errBuff); return error; } int device_stop(DEVICE *device) { int error = 0; char errBuff[2048]={'\0'}; PARAMETERS* pp = &device->params; int value; device->stop_time = device->current_time; device->state = device_stopped; if (pp->output_line == 1) value = 0; else if (pp->output_line == 2) value = 1; else value = -1; dbg_printf(0, "Setting output line to %d\n", value); HWARE_TEST(hware_outp(device->private_data, value)); dbg_printf(0, "Setting input line to %d\n", pp->source); HWARE_TEST(hware_source(device->private_data, pp->source)); device->source = pp->source; return error; Error: device_errmsg(errBuff, sizeof(errBuff)); printf("DAQmx Error: %s\n", errBuff); return error; } int device_pause(DEVICE *device) { if (device->state == device_running) device->state = device_paused; return 0; } int device_resume(DEVICE *device) { if (device->state == device_paused) device->state = device_running; return 0; } int device_command(void* counter, const char* command) { DEVICE* device = static_cast(counter); if (strncasecmp(command, "pause", 5) == 0) return device_pause(device); else if (strncasecmp(command, "continue", 8) == 0) return device_resume(device); else if (strncasecmp(command, "resume", 6) == 0) return device_resume(device); else if (strncasecmp(command, "start", 6) == 0) return device_start(device); else if (strncasecmp(command, "stop", 4) == 0) return device_stop(device); return 0; } static void device_event(DEVICE *device, const char* event) { BUFFER buffer; sprintf(buffer.body, "EVENT %s %s\r\n", make_timestamp(&device->current_time), event); buffer.length = strlen(buffer.body); dbg_printf(0, "%s", buffer.body); //sock_report(&buffer, 0); sock_report(&buffer, 1, device); sock_report(&buffer, 2, device); } static void device_range_check(DEVICE* device, int mode) { PARAMETERS* pp = &device->params; if (pp->range_check_enable) { if (pp->range_mode == mode) { double test; SAMPLE* sp = cur_sample(device); if (mode == 1) test = sp->counter_rate; else if (mode == 2) test = sp->counter_rate; else test = device->report.average_rate; if (pp->range_low > 0 && pp->range_low > test) { if (device->range_error != 1) device_event(device, "RANGE OUT LOW"); device->range_error = 1; if (pp->range_gate_enable) device->range_gated = true; else device->range_gated = false; } else if (pp->range_high > 0 && pp->range_high < test) { if (device->range_error != 2) device_event(device, "RANGE OUT HIGH"); device->range_error = 2; if (pp->range_gate_enable) device->range_gated = true; else device->range_gated = false; } else { if (device->range_error != 0) device_event(device, "RANGE IN"); device->range_error = 0; device->range_gated = false; } } } else { /* If range check has been disabled while in error - reset */ if (device->range_error != 0) device_event(device, "RANGE IN"); device->range_error = 0; device->range_gated = false; } } static void device_test_term(DEVICE* device) { PARAMETERS* pp = &device->params; SAMPLE* sp = cur_sample(device); SAMPLE* psp = prv_sample(device, 1); if (!device->terminal_due) { if (pp->terminal_check_type == 1) { if (pp->direction == COUNT_DOWN) { /* * decremented to or through terminal */ // TODO FIXME improve wraparound handling 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)) { device->terminal_due = true; } } else { /* * incremented to or through terminal */ // TODO FIXME improve wraparound handling if ( #if 1 sp->counter_value >= pp->terminal_count #else (sp->counter_value >= pp->terminal_count && psp->counter_value < pp->terminal_count) || (sp->counter_value < psp->counter_value && psp->counter_value > pp->terminal_count) #endif ) { device->terminal_due = true; } } } else if (pp->terminal_check_type == 2) { if ((uint64) device->accumulated.tv_sec >= pp->terminal_count) device->terminal_due = true; } if (device->terminal_due) { device_event(device, "TERMINAL"); make_report(device); device_stop(device); } } } /* * poll the physical counter */ int device_poll(DEVICE* device) { PARAMETERS* pp = &device->params; char errBuff[2048]={'\0'}; unsigned long long current_count_local; int count_delta_local; int error=0; SAMPLE* sp = NULL; SAMPLE* psp = NULL; #if 0 dbg_printf(0, "%d:-%s %s %.3f %s %.3f %4d\n", idx, make_timestamp(&device->current_time), make_timestamp(&device->sample_timer), device_time_to_next_sample(device), make_timestamp(&device->report_timer), device_time_to_next_report(device), device->sample_index); #endif /* read the value from the hardware counter to a temp */ ++device->poll_counter; HWARE_TEST(hware_read(device->private_data, ¤t_count_local)); dbg_printf(0, "device_poll = %llu @ %s\n", current_count_local, make_timestamp(&device->current_time)); sp = cur_sample(device); psp = prv_sample(device, 1); /* calculate the number since last time and save new value */ count_delta_local = current_count_local - device->count64; device->count64 = current_count_local; sp->num_polls += 1; /* * If the counter is running and not gated increment the count and runtime */ if (device->state == device_running && !(device->params.range_gate_enable && device->range_gated)) { if (device->params.direction == COUNT_DOWN) { device->current_count -= count_delta_local; } else { device->current_count += count_delta_local; } /* * Add the time difference to the accumulated time */ device->accumulated.tv_sec += device->current_time.tv_sec - sp->timestamp.tv_sec; /* prevent negative tv_usec by borrowing one second in microseconds */ device->accumulated.tv_usec += 1000000; device->accumulated.tv_usec += device->current_time.tv_usec; device->accumulated.tv_usec -= sp->timestamp.tv_usec; if (device->accumulated.tv_usec >= 1000000) { /* carry the seconds */ device->accumulated.tv_sec += device->accumulated.tv_usec / 1000000; device->accumulated.tv_usec %= 1000000; } /* pay back the borrowed second */ device->accumulated.tv_sec -= 1; } /* calculate and check the count-rate between polls */ sp->count_delta = device->count64 - sp->count64; sp->time_delta = time_diff(&device->current_time, &sp->timestamp); sp->counter_rate = (double) sp->count_delta / sp->time_delta; device_range_check(device, 2); /* poll range check */ device->previous_time = device->current_time; /* save counter values in the sample */ sp->counter_value = device->current_count; sp->count64 = device->count64; sp->timestamp = device->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 */ device_test_term(device); /* check if it is time to roll the sample */ if (device_time_to_next_sample(device) <= 0) { /* check if it is time to roll the report */ if (device_time_to_next_report(device) <= 0) { make_report(device); device_range_check(device, 0); /* report range check */ device_report(device); } device_range_check(device, 1); /* sample range check */ device_sample(device); } /* set the source line */ if (device->source != pp->source) { device->source = pp->source; HWARE_TEST(hware_source(device->private_data, device->source)); } /* set the line filter */ if (device->filter != pp->filter) { device->filter = pp->filter; HWARE_TEST(hware_filter(device->private_data, device->filter)); } /* drive the output line */ if (device->output_line != pp->output_line) { int value; device->output_line = pp->output_line; if (pp->output_line == 1) { if (device->state == device_running || device->state == device_paused) value = 1; else value = 0; } else if (pp->output_line == 2) { if (device->state == device_running || device->state == device_paused) value = 0; else value = 1; } else value = -1; HWARE_TEST(hware_outp(device->private_data, value)); } return error; Error: device_errmsg(errBuff, sizeof(errBuff)); printf("DAQmx Error: %s\n", errBuff); return error; } void device_term(DEVICE* device) { if (device->private_data) { hware_dtor(&device->private_data); device->private_data = NULL; } device->state = device_idle; } bool device_fatal(int error) { return hware_failed(error); } void device_errmsg(char* buff, int len) { hware_errmsg(buff, len); } double device_time_to_next_report(DEVICE* device) { uint64 last_report; uint64 timeofday; int timeout; struct timeval now; now = device->current_time; last_report = 1000 * (uint64) device->report_timer.tv_sec; last_report += (uint64) device->report_timer.tv_usec / 1000; timeofday = 1000 * (uint64) now.tv_sec; timeofday += (uint64) now.tv_usec / 1000; timeout = device->params.poll_period * device->params.sample_period * device->params.report_period; if ((last_report / timeout) != (timeofday / timeout)) return 0.0; timeout = timeout - timeofday % timeout; return 0.001 * timeout; } double device_time_to_next_sample(DEVICE* device) { uint64 last_sample; uint64 timeofday; int timeout; struct timeval now; now = device->current_time; last_sample = 1000 * (uint64) device->sample_timer.tv_sec; last_sample += (uint64) device->sample_timer.tv_usec / 1000; timeofday = 1000 * (uint64) now.tv_sec; timeofday += (uint64) now.tv_usec / 1000; timeout = device->params.poll_period * device->params.sample_period; if ((last_sample / timeout) != (timeofday / timeout)) return 0.0; timeout = timeout - timeofday % timeout; return 0.001 * timeout; }