1

I'm working on fixing some GPS units. They are old and have recently had a week count rollover issue so they are reporting to NTP the wrong date. While we are waiting to replace them I'm made a temporary software fix that is 99% working.

I'm taking the serial input from the gps, parsing it, adding a correction value, and then sending it back out on the a pseudo terminal.

NTP won't read the port. If I use my test reading program it will read the data correctly as long as I don't try to open the port as odd parity. This is of course what the gps device is suppose to be.

This is the writing routines setup:

int GPSPortSetup(char *PortName, int *OutputPort)
{
  int gps_fd;
  struct termios line_ctl;
  int OpenFlags;
  int i;
  int master, slave;
  char name[100];

  gps_fd = open (PortName, O_RDWR);

  if (gps_fd == -1)
  {
    perror("Open Failed");
    return -1;
  }
  memset(&line_ctl, 0, sizeof(line_ctl));
  if (tcgetattr (gps_fd, &line_ctl) != 0)
  {
     perror("tcgetattr failed!");
     close(gps_fd);
     exit(-1);
  }

  cfmakeraw(&line_ctl);

  line_ctl.c_iflag = 0;
  line_ctl.c_oflag = 0;
  line_ctl.c_cflag &= ~(CSIZE | CSTOPB);
  line_ctl.c_cflag |= (CS8 | CREAD | PARENB | PARODD);
  line_ctl.c_lflag &= ~(ICANON | ISIG | IEXTEN);

  cfsetspeed(&line_ctl, B9600);

  line_ctl.c_cc[VMIN] = 21;
  line_ctl.c_cc[VTIME] = 5;

  if(tcsetattr(gps_fd, TCSANOW, &line_ctl) !=0)
  {
    perror("tcsetattr failed");
    close(gps_fd);
    exit(-1);
  }

  if(openpty(&master, &slave, &name, &line_ctl, NULL) != 0)
  {
    perror("openpty");
    printf("errno = %i\n", errno);
    exit(-1);
  }

  if(grantpt(master) < 0)
  {
    perror("grantpt");
    exit(-1);
  }
  if(unlockpt(master) < 0)
  {
    perror("unlockpt");
    exit(-1);
  }

  *OutputPort = master;
  printf("%s\n", name);
  return(gps_fd);
}

This is the reading routine port set:

int GPSPortSetup(char Direction, char *PortName)
{
  int gps_fd;
  struct termios line_ctl;
  int OpenFlags;
  int i;

  switch (Direction)
  {
    case GPSREAD:
      OpenFlags = O_RDONLY;
    break;

    case GPSWRITE:
      OpenFlags = O_WRONLY;
    break;

    case GPSRW:
      OpenFlags = O_RDWR;
    break;
  }

printf("%s\n", PortName);
  gps_fd = open (PortName, O_RDWR|O_NOCTTY);

  if (gps_fd == -1)
  {
    perror("Open Failed");
    printf("errno = %i\n", errno);
    printf("EEXIST %i\n", EEXIST);
    printf("EISDIR %i\n", EISDIR);
    printf("EACCES %i\n", EACCES);
    printf("ENAMETOOLONG %i\n", ENAMETOOLONG);
    printf("ENOENT %i\n", ENOENT);
    printf("ENOTDIR %i\n", ENOTDIR);
    printf("ENXIO %i\n", ENXIO);
    printf("ENODEV %i\n", ENODEV);
    printf("EROFS %i\n", EROFS);
    printf("ETXTBSY %i\n", ETXTBSY);
    printf("EFAULT %i\n", EFAULT);
    printf("ELOOP %i\n", ELOOP);
    printf("ENOSPC %i\n", ENOSPC);
    printf("ENOMEM %i\n", ENOMEM);
    printf("EMFILE %i\n", EMFILE);
    printf("ENFILE %i\n", ENFILE);
    exit(-1);
  }
  memset(&line_ctl, 0, sizeof(line_ctl));
  if (tcgetattr (gps_fd, &line_ctl) != 0)
  {
     perror("tcgetattr failed!");
     close(gps_fd);
     exit(-1);
  }

  cfmakeraw(&line_ctl);

  line_ctl.c_iflag = 0;
  line_ctl.c_oflag = 0;
  line_ctl.c_cflag &= ~(CSIZE | CSTOPB);
  line_ctl.c_cflag |= CS8;
  line_ctl.c_cflag |= CREAD;
  line_ctl.c_cflag |= PARENB;
  line_ctl.c_cflag |= PARODD;
  line_ctl.c_lflag &= ~(ICANON | ISIG | IEXTEN);

  cfsetspeed(&line_ctl, B9600);

  line_ctl.c_cc[VMIN] = 21;
  line_ctl.c_cc[VTIME] = 5;

  if(tcsetattr(gps_fd, TCSANOW, &line_ctl) !=0)
  {
    perror("tcsetattr failed");
    close(gps_fd);
    exit(-1);
  }

  return(gps_fd);

}

If I comment out the line that sets PARENB in the reading routine, I see the data. If I don't, perror for the tcsetattr prints "tcsetattr failed: Invalid argument".

The code works if I use actual serial ports. I've tested it but reading the gps in, opening a real serial port, and then connecting that to another real one via a null modem cable. NTP will go live and read the data properly in that case.

I have gone to NTP documentation to look for a fix. They show one but when I try to implement that fix I get errors as well. The units we use are Trimble Acutime 2000, Acutime Gold, and Palisade. The sites don't have network access yet so I cannot just read a real time server.

I'm Running CentOS 6.5

Acutime 2000 is coming in on port /dev/ttyrp22. The stty -a for it is:

speed 9600 baud; rows 0; columns 0; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 21; time = 5;
parenb parodd cs8 hupcl -cstopb cread clocal -crtscts -cdtrdsr
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff
-iuclc -ixany -imaxbel -iutf8
-opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke

It opens /dev/pts/? The stty -a for the currently open port is:

speed 9600 baud; rows 0; columns 0; line = 0;
intr = <undef>; quit = <undef>; erase = <undef>; kill = <undef>; eof = <undef>;
eol = <undef>; eol2 = <undef>; swtch = <undef>; start = <undef>; stop = <undef>;
susp = <undef>; rprnt = <undef>; werase = <undef>; lnext = <undef>;
flush = <undef>; min = 1; time = 0;
-parenb parodd cs8 -hupcl -cstopb cread clocal -crtscts -cdtrdsr
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff
-iuclc -ixany -imaxbel -iutf8
-opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt
-echoctl -echoke
  • Interesting problem. It seems likely to me that you've set the parity on the pseudo-terminal just fine, but the GPS receivers aren't happy about that for some reason I don't fathom. Can you run `stty -a` or equivalent on the pseudo-terminal to validate that the setting has taken effect? Can you run some other program (a shell perhaps) on the pseudo-terminal to see that it is working and the problem is in the GPS receivers? None of which will be dreadfully helpful if I'm right, of course. I have no experience that can help, I'm sorry to say. – Jonathan Leffler Nov 05 '18 at 16:06
  • So GPS_Correct reads the GPS on /dev/ttyrp22 and then opens a /dev/pty/ to send the corrected output. My diagnostic utility reads the /dev/pty/? port I supply it to read in that port. – Robert Schaefer Nov 05 '18 at 16:34
  • Added the stty output to the original post. – Robert Schaefer Nov 05 '18 at 16:42
  • So the pts is not showing parenb set but I passed that value through the openpty command. Can that be set from a different command? – Robert Schaefer Nov 05 '18 at 16:48
  • you didn't pass the parenb through openpty(). openpty() ignores the return value of tcsetattr() (look at the [source](https://sourceware.org/git/?p=glibc.git;a=blob;f=login/openpty.c;h=941ba039e36e2f6b2303f6380e5a057c2e7bdf7a;hb=6d6ee04622fd77908936250b1f632c2b4388ee78#l128) if you don't believe me). And btw, you don't need grantpt() and unlockpt(); openpty() is calling them itself. –  Nov 05 '18 at 16:56
  • And no, you cannot set `PARENB` on a pseudo-terminal. If I was you, I would go for a `LD_PRELOAD` hack (that will override the read(2) function) in order to push that correction, instead of your pseudo-tty solution. –  Nov 05 '18 at 17:00
  • Thanks I was going to take out the grant and unlock. They were put in because I was getting an Input output error on the open with getpt(). That seemed to fix it but I still needed to test that out. I'll look into how to do the LD_PRELOAD. I haven't played with that before. – Robert Schaefer Nov 05 '18 at 17:09
  • Thanks for updating the question rather than including the information in the comments. That's usually the best way to deal with extra information requested by comments. – Jonathan Leffler Nov 05 '18 at 17:11
  • You are welcome. I actually through them in the comments while I was trying to figure out how to edit the original post. Then I removed them. I was confused because someone had suggested an edit that I didn't realize was there. – Robert Schaefer Nov 05 '18 at 17:13
  • I'm assuming socat would have the same issue. – Robert Schaefer Nov 05 '18 at 17:17

0 Answers0