/* A program to read bytes from the Radio Shack ProbeScope and */
/* display the values on a terminal.                           */
/* compile using:                                              */
/*    gcc -o pproto pproto.c                                   */
/* sample usage:                                               */
/*    ./pproto /dev/ttyS0                                      */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <sys/fcntl.h>


/************** Global Variables for ProbeScope ************/
#define PS_BYTES  138
#define SET_LEN   20
int    psfd;   /* file desc. for serial port to the ProbeScope */
struct PS_SMPL {
    char mode[SET_LEN+1];
    char coup[SET_LEN+1];
    char vert[SET_LEN+1];
    char timd[SET_LEN+1];
    char trig[SET_LEN+1];
    char tlvl[SET_LEN+1];
    char  bytes[PS_BYTES];     /* PS settings and samples */
} smpl;

/********************** Forward Declarations ***************/
int ps_open(char *);
int ps_sample();



int main (int argc, char *argv[])
{
    int i;

    /* Open the serial port to the ProbeScope */
    psfd = ps_open(argv[1]);


    /* Get sets of samples and display */
    while (1) {
        ps_sample();

        printf("%s\n", smpl.mode);
        printf("%s\n", smpl.coup);
        printf("%s\n", smpl.vert);
        printf("%s\n", smpl.timd);
        printf("%s\n", smpl.trig);
        printf("%s\n", smpl.tlvl);

        /* Display both settings and sample bytes */
        for (i=0; i<5; i++)
            printf("0x%02x ", smpl.bytes[i]);
        printf("   ... sync and settings\n");

        for (i=5; i<133; i++) { /* sample bytes start at byte 5 */
            if ((i-4)%16)
                printf("0x%02x ", smpl.bytes[i]);
            else
                printf("0x%02x\n", smpl.bytes[i]);
        }

        /* Display duplicate and DVM bytes */
        for (i=133; i<PS_BYTES; i++)
            printf("0x%02x ", smpl.bytes[i]);
        printf("   ... duplicate and DVM bytes\n");
        printf("\n\n\n");
    }
}




/* ps_open:  Open the serial port specified */
/* returns:  The opened file descriptor */
int ps_open(char *serport)
{
    struct  termios    tbuf;
    int     fd_dev;

    if (( fd_dev = open(serport, (O_RDWR | O_NDELAY), 0)) < 0 ) {
        fprintf(stderr,"Unable to open tty port specified\n");
        exit(1);
    }

    tbuf.c_cflag = CS7|CREAD|CLOCAL;
    if (cfsetspeed(&tbuf, B19200)) {
        perror("cfsetspeed");
        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) {
        fprintf(stderr, "pproto: Unable to set device '%s' parameters\n", serport);
        exit(1);
    }
    if (( fd_dev = open(serport, (O_RDWR), 0)) < 0 ) {
        fprintf(stderr, "Unable to open tty port specified\n");
        exit(1);
    }

    return(fd_dev);  /* return the serial port file descriptor */
}


/* ps_sample:  get a set of samples from the ProbeScope */
int ps_sample()
{
    static int  cnt;   /* byte counter (smpl[0] is the sync byte) */


    /* Waiting for a sync byte */
    cnt = 0;                  /* count starts at 0 since byte 0 is sync */
    do {
        read(psfd, &smpl.bytes[cnt], 1);
    } while ((0x7c & smpl.bytes[cnt]) != 0x7c);
    cnt = 1;


    /* Waiting for sample to start */
    do {
        read(psfd, &smpl.bytes[cnt], 1);
    } while ((0x7c & smpl.bytes[cnt]) == 0x7c);
    cnt = 2;  /* just read byte 1, the first smpl byte */


    /* Read the setting and the sample bytes */
    do {
        cnt += read(psfd, &smpl.bytes[cnt], PS_BYTES-cnt);
     } while (cnt < PS_BYTES);


    /* At this point we have all 137 bytes of the sample.  We now */
    /* analyze the first few bytes to get the 'settings' strings. */

    /* A zero bit indicates coupling */
    switch(smpl.bytes[1] & 0x30) {
        case 0x10: strncpy(smpl.coup, "Coupling:  AC ", SET_LEN); break;
        case 0x20: strncpy(smpl.coup, "Coupling:  DC ", SET_LEN); break;
        case 0x00: strncpy(smpl.coup, "Coupling:  GND", SET_LEN); break;
    }

    /* A set bit indicates voltage range */
    switch(smpl.bytes[1] & 0x0c) {
        case 0x00: strncpy(smpl.vert, "Volts FS:  1  ", SET_LEN); break;
        case 0x04: strncpy(smpl.vert, "Volts FS:  10 ", SET_LEN); break;
        case 0x08: strncpy(smpl.vert, "Volts FS:  100", SET_LEN); break;
    }

    /* An integer indicated the time base */
    /* There are 20 samples per horizontal division so the time per division */
    /* is 20 times the sample rate. */
    switch(smpl.bytes[2]) {
        case 0: strncpy(smpl.timd, "Time/smpl: 50 ns ", SET_LEN); break;
        case 1: strncpy(smpl.timd, "Time/smpl: 100 ns", SET_LEN); break;
        case 2: strncpy(smpl.timd, "Time/smpl: 0.5 us", SET_LEN); break;
        case 3: strncpy(smpl.timd, "Time/smpl: 1 us  ", SET_LEN); break;
        case 4: strncpy(smpl.timd, "Time/smpl: 5 us  ", SET_LEN); break;
        case 5: strncpy(smpl.timd, "Time/smpl: 10 us ", SET_LEN); break;
        case 6: strncpy(smpl.timd, "Time/smpl: 50 us ", SET_LEN); break;
        case 7: strncpy(smpl.timd, "Time/smpl: 0.1 ms", SET_LEN); break;
        case 8: strncpy(smpl.timd, "Time/smpl: 0.5 ms", SET_LEN); break;
        case 9: strncpy(smpl.timd, "Time/smpl: 1 ms  ", SET_LEN); break;
    }

    /* A set LSB indicates SINGLE mode */
    switch(smpl.bytes[3] & 0x01) {
        case 0x00: strncpy(smpl.mode, "Mode:      Run   ", SET_LEN); break;
        case 0x01: strncpy(smpl.mode, "Mode:      Single", SET_LEN); break;
    }

    /* A set bit indicates the triggering */
    switch(smpl.bytes[3] & 0x78) {
        case 0x00: strncpy(smpl.trig, "Trigger:   Auto      ", SET_LEN); break;
        case 0x08: strncpy(smpl.trig, "Trigger:   -Ext      ", SET_LEN); break;
        case 0x10: strncpy(smpl.trig, "Trigger:   +Ext      ", SET_LEN); break;
        case 0x20: strncpy(smpl.trig, "Trigger:   -Int      ", SET_LEN); break;
        case 0x40: strncpy(smpl.trig, "Trigger:   +Int      ", SET_LEN); break;
    }

    /* A set bit indicates the trigger level */
    switch(smpl.bytes[4] & 0x01) {
        case 0x00: strncpy(smpl.tlvl, "Trig Lvl:  -0.5 Volts", SET_LEN); break;
        case 0x02: strncpy(smpl.tlvl, "Trig Lvl:  +0.5 Volts", SET_LEN); break;
        case 0x04: strncpy(smpl.tlvl, "Trig Lvl:  +0.3 Volts", SET_LEN); break;
        case 0x08: strncpy(smpl.tlvl, "Trig Lvl:  +0.1 Volts", SET_LEN); break;
        case 0x10: strncpy(smpl.tlvl, "Trig Lvl:  -0.1 Volts", SET_LEN); break;
        case 0x20: strncpy(smpl.tlvl, "Trig Lvl:  -0.3 Volts", SET_LEN); break;
    }

    /* We are done reading the settings.  The next 128 bytes are samples. */
    /* The samples start at byte 5.  Return and let the display the trace.*/
    return(1);                /* completed sample */
}
