/*
 * Name:        1bitda  (one bit data acquisition)
 *
 * Description: A program to drive the one-bit data acquisition circuit
 *              and to output the data stream in a form usable by a
 *              TCL/TK data display program.
 *
 * Syntax:      1bitda tty_port baud_rate
 *
 * Example:     1bitda /dev/ttyS1 57.6
 *              0,44
 *              1,45
 *              0,45
 *              ..........
 *
 */

#include <stdio.h>
#include <termio.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/signal.h>
#include <linux/serial.h>

#define NREAD  512

int  setup_port(char *, char *);
void display_smpl(unsigned char *, int, char);
void logic_analysis(int, int, char);

static int cnt_100th_sec;


main (int argc, char *argv[])
{
    char    portname[FILENAME_MAX];  /* serial port as a string */
    int     fd_serial;               /* file descriptor to serial port */
    char    mode;                    /* output mode:raw,count,time  -r,-c,-t */
    int     count;                   /* count of bytes read */
    unsigned char inbuf[NREAD];      /* the array of bytes read */

    /* Verify that we have the right number of arguments */
    if (argc != 3) {
        printf("Usage: %s serial_port baud_rate\n", argv[0]);
        exit(1);
    }

    /* Open the serial port at 8,N,1.  */
    fd_serial = setup_port(argv[1], argv[2]);


    /* Loop forever reading data bytes */
    while (1) {
        /* There is no background processing, so we use a blocking read */
        count = read(fd_serial, inbuf, NREAD);
        if (count < 0) {
            /* we've encountered an error of some kind.  Just exit for now. */
            exit(0);
        }

        display_smpl(inbuf, count, mode);
    }
}



/******************************************************************
* setup_port()
* Open the serial port specified at the buad rate specified and 
* return the file descriptor.
* Use 8,N,1 and exit(1) on any errors.
******************************************************************/
int  setup_port(
    char *portname,    /* the name of the port */
    char *baud_rate)   /* baud rate of the port */
{
    int              fd_dev;
    struct termios   tbuf;

    if (( fd_dev = open(portname, O_RDWR,0)) < 0 ) {
        printf("Unable to open port '%s'.\n", portname);
        exit(1);
    }

    /* Get the baud rate */
    if (!strncmp(baud_rate, "19.2", 5)) {
        tbuf.c_cflag = CS8|CREAD|B19200|CLOCAL;
        cnt_100th_sec = 154;
    }
    else if (!strncmp(baud_rate, "38.4", 5)) {
        tbuf.c_cflag = CS8|CREAD|B38400|CLOCAL;
        cnt_100th_sec = 307;
    }
    else if (!strncmp(baud_rate, "57.6", 5)) {
        tbuf.c_cflag = CS8|CREAD|B57600|CLOCAL;
        cnt_100th_sec = 461;
    }
    else if (!strncmp(baud_rate, "115.2", 6)) {
        tbuf.c_cflag = CS8|CREAD|B115200|CLOCAL;
        cnt_100th_sec = 922;
    }
    else if (!strncmp(baud_rate, "230.4", 6)) {
        tbuf.c_cflag = CS8|CREAD|B230400|CLOCAL;
        cnt_100th_sec = 1843;
    }
    else {
        printf("Unsupported baud rate of '%s'\n", baud_rate);
        exit(1);
    }

    tbuf.c_iflag = IGNBRK;
    tbuf.c_oflag = 0;
    tbuf.c_lflag = 0;
    tbuf.c_cc[VMIN] = 1; /* character-by-character input */
    tbuf.c_cc[VTIME]= 0; /* no delay waiting for characters */
    if (tcsetattr(fd_dev, TCSANOW, &tbuf) < 0) {
        printf("Unable to set device '%s' parameters\n", portname);
        exit(1);
    }

    return(fd_dev);
}


/******************************************************************
* display_smpl()
* Display the samples in the appropriate mode
******************************************************************/
void display_smpl(
    unsigned char *inbuf,   /* array of input samples */
    int count,              /* count of samples in inbuf */
    char mode)              /* mode of display = r,c,t */
{
    static int value=0;     /* the "current" value */
    static int vcnt=0;      /* the sample count at value */
    unsigned char smpl;     /* the 8 bit sample */
    int i;                  /* loop counter */
    int bitmask;            /* used to detect value of individual bit */


    /* if raw mode, just display the values in hex */
    if (mode == 'r') {
        for (i=0; i<count; i++) {
            printf("%02x\n", ~(inbuf[i]) & 0xff);
        }
        return;
    }


    /* else display as value, comma, count, newline */
    for (i=0; i<count; i++) {
        smpl = ~(inbuf[i]);

        if (smpl == 0xff) {
            if (value == 1) {
                vcnt += 8;
                if (vcnt > cnt_100th_sec) { 
                    /* output something at least 1/10 second */
                    logic_analysis(value, vcnt, mode);
                    vcnt = 0;
                }
            }
            else {
                logic_analysis(value, vcnt, mode);
                value = 1;
                vcnt = 8;
            }
        }
        else if (smpl == 0x00) {
            if (value == 0) {
                vcnt += 8;
                if (vcnt > cnt_100th_sec) { 
                    /* output something at least 1/10 second */
                    logic_analysis(value, vcnt, mode);
                    vcnt = 0;
                }
            }
            else {
                logic_analysis(value, vcnt, mode);
                value = 0;
                vcnt = 8;
            }
        }
        else {
            bitmask = 0x01;
            while (bitmask & 0xff) {
                if ((value && (smpl & bitmask))    /* if both 1 */
                  ||(!value && !(smpl & bitmask))) /* or if both 0 */
                    vcnt++;
                else {
                    logic_analysis(value, vcnt, mode);
                    value = value ^ 1;  /* value just inverts */ 
                    vcnt = 1;
                }
                bitmask = bitmask * 2;
            }
        }
    }
}

/******************************************************************
* logic_analysis()
* Perform state machine evaluation of input
******************************************************************/
void logic_analysis(
    int value,              /* 0 or 1 */
    int count,              /* count of samples with value */
    char mode)              /* mode of output display */
{
    /* This is where we perform all of the "logic analysis" to  */
    /* relate the input stream to the output stream.            */
    /* As the feed to the Tcl/Tk display program, we just print */
    printf("%1d, %d\n", value, count);
    fflush((FILE *)0);
}


