/* geiger.c -- * Created: Thu Jan 24 11:54:31 2002 by faith@alephnull.com * Revised: Tue Mar 26 15:12:17 2002 by faith@alephnull.com * Copyright 2002 Rickard E. Faith (faith@alephnull.com) * * Adapatation by Peer Janssen (peer@baden-online.de): * Jul 02 2005 - Use of calibration factor 1 as default value * * More hacking by Tjerk Schuringa (tjerk@polonai.se) * Interface for RKSB-104 Russian geiger counter * Make sure your motherboard's IDC header pinout corresponds to * the 9-pin D connector! * 21 July 2012 * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * $Id$ * */ /* This code collects data from the Aware Electronics RM-60 geiger * counter. It probably works with other geiger counters from Aware * Electronics, but I haven't tried any others (although the output will * not be in uR/hr -- please update the gLog function to compute the * uR/hr correctly for other models). * * The pin out of the 4 wires from the geiger counter are as follows: * * RM-60 RS-232 DB-9 (Male) * Ground SG 5 * Signal RI 9 (goes low when an event occurs) * Positive DTR 4 * Negative RTS 7 * * I did all my initial testing with a breakout box with only these 4 * pins and pin 1 (FG) connected. Then I plugged in the DB-9 connector * to the RM-60 cable and did more tests without the breakout box. I * had the best success with the standard serial port (/dev/ttyS0) and * wasn't able to get things to work with a port on an ISA-bus Cyclades * Cyclom-Y serial card (perhaps because it doesn't put out enough power * to power the device? -- I didn't explore further). */ #define DEFAULT_TTY "/dev/geiger" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <termios.h> #include <fcntl.h> #include <signal.h> #include <time.h> #include <sys/ioctl.h> #include <linux/serial.h> #define GAVERAGE 5 /* Number of minutes to average over */ #define GWAIT 60 /* Number of seconds to wait (for testing) */ typedef struct gData { time_t t; unsigned long count; } gDataT; static int fd; static double click[GAVERAGE]; static int currentClick, maxClick; static gDataT d0, d1; static const char *logfile; static char filename[2048]; static FILE *str; static void gLog(gDataT *d0, gDataT *d1) { double current, average, total; int i; /* double factor = 1000.0 / 1050.0; /* RM-60 */ double factor = 1; /* simple counting of pulses */ /* Compute uR/hr for previous minute. * Note that, for the RM-60, * counts/minute * 1000/1050 = uR/hr * (see http://www.aw-el.com/specs.htm) */ time_t seconds = d1->t - d0->t; current = (double)(d1->count - d0->count) * factor * 60.0 / seconds; /* Maintain table for computing average */ click[currentClick++] = current; if (currentClick > maxClick) maxClick = currentClick; if (currentClick >= GAVERAGE) currentClick = 0; /* Compute average */ for (total = 0, i = 0; i < maxClick; i++) total += click[i]; average = maxClick ? (total / maxClick) : 0.0; str = fopen(logfile, "w"); fprintf((logfile) ? str : stdout, "%.0f", current); fclose(str); } static void gRead(gDataT *data) { struct serial_icounter_struct icount; time(&data->t); ioctl(fd, TIOCGICOUNT, &icount); data->count = icount.rng; } static void gHandler(int sig) { gRead(&d1); gLog(&d0, &d1); d0 = d1; alarm(GWAIT); } static void gOpen(const char *port) { struct termios term; int info; if ((fd = open(port, O_RDWR|O_NOCTTY)) < 0) { perror( __FUNCTION__ ); fprintf( stderr, "Cannot open \"%s\"\n", port ); exit(1); } /* This prevents two daemons from * running simultaneously, but it * otherwise optional. */ if (lockf(fd, F_TLOCK, 0) < 0) { perror(__FUNCTION__); fprintf(stderr, "Cannot lock \"%s\"\n", port); close(fd); exit(1); } if (tcgetattr(fd, &term) < 0) { perror( __FUNCTION__ ); fprintf( stderr, "tcgetattr failed\n" ); exit(1); } /* Raw */ cfmakeraw(&term); term.c_cflag |= HUPCL; term.c_cc[VMIN] = 0; term.c_cc[VTIME] = 0; cfsetospeed(&term, B2400); cfsetispeed(&term, B2400); /* Set */ tcsetattr(fd, TCSANOW, &term); /* Turn on power -- DTR high, RTS low */ info = TIOCM_DTR; ioctl(fd, TIOCMBIS, &info); info = TIOCM_RTS; ioctl(fd, TIOCMBIC, &info); sleep(1); } static void help( void ) { static const char *help_msg[] = { "", "-t <tty> specify tty (default = " DEFAULT_TTY ")", "-d increase debugging level", "-l logfile name in strftime(3) format", "-h give this help", 0 }; const char **p = help_msg; fprintf( stderr, "geiger 1.0\n"); fprintf( stderr, "Copyright 2002 Rik Faith (faith@alephnull.com)\n" ); while (*p) fprintf( stderr, "%s\n", *p++ ); } int main( int argc, char **argv ) { int debug = 0; const char *tty = DEFAULT_TTY; int c; struct sigaction sa; while ((c = getopt( argc, argv, "t:dl:")) != EOF) switch (c) { case 't': tty = optarg; break; case 'd': ++debug; break; case 'l': logfile = optarg; break; default: help(); exit(0); } gOpen(tty); memset(&sa, 0, sizeof(sa)); sa.sa_handler = gHandler; sigaction(SIGALRM, &sa, NULL); gRead(&d0); alarm(GWAIT); for (;;) { /* If we're debugging, we want the ioctl to return with each * change so that we can print something. This would be useful * if we were trying to record the time between events. * However, if we aren't debugging, then we can let the kernel * service the interruption and keep count -- we don't need to * return to user space to have that happen. See the code in * linux/drivers/char/serial.c to see how this works. * * Note that this ioctl can also return EIO and EINTR, so if * you're timing events, be sure to check that an event actually * happened. Note that the delivery of SIGALRM causes the ioctl * to return. */ ioctl(fd, TIOCMIWAIT, debug ? TIOCM_RNG : 0); if (debug) { time_t t; time(&t); printf("%lu\n", t); } } return 0; }