/******************************************************************************/ /** Copyright 2005 MBARI */ /******************************************************************************/ /******************************************************************************/ /** Filename : LOBOTRAK.C */ /** Author : Luke Coletti */ /** Project : 600144 */ /** Version : 2.00 */ /** Created : 03/23/05 */ /** Compiler : gcc version 3.0.1 */ /** OS, Box : IRIX Release 6.5 IP27, lepas.mbari.org */ /** Archived : */ /** Summary : Manages the socket connections from the Lantronix UDS10 device */ /** server (134.89.24.49) that the GSM MODEM is attached to. */ /******************************************************************************/ /** Modification History: */ /** 01/29/07 : Added GPS Speed and Heading parsing. */ /******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #define SERV_TIMEOUT 45 //in seconds #define READ_TIMEOUT 7 //in seconds #define TRUE 1 #define FALSE 0 #define OK 0 #define ERROR (-1) #define TIMEOUT (-2) #define DISCONNECT (-3) #define SENDMAIL "/usr/lib/sendmail -t" #define SENDER "coletti@mbari.org" #define RECIPIENT "coletti@mbari.org" typedef unsigned char uchar; int MatchStation(char *id); void UpdateDataFile(char *path, char *msg); void UpdateErrorLogFile(char *emsg); void SendEmail(char *subject, char *message); ssize_t RdData(char *rx_buf, size_t len, ushort tm_out); float GetDOYtime(int *year); ushort DayOfYear(int yy, int mm, int dd); char *CalcTAIPChecksum(char *pszTAIPMsg, uchar *checksum); /*----------------------------------------------------------------------*\ | G P S T I M E C O N V E R S I O N F U N C T I O N S \*----------------------------------------------------------------------*/ uchar rtcSystemTimeToGPS (ushort* pusWeekNum, ulong* pulTimeOfWeek, uchar ucSecond, uchar ucMinute, uchar ucHour, uchar ucDay, uchar ucMonth, ushort usYear); uchar rtcGPStoSystemTime (ushort usWeekNum, ulong ulTimeOfWeek, uchar* pucSecond, uchar* pucMinute, uchar* pucHour, uchar* pucDay, uchar* pucMonth, ushort* pusYear); static time_t timer; static struct tm *timeptr; static struct sockaddr_in addr_clnt; enum LoboStations{L01=0, L02, L03, L04, L05, LastChoice}; static char *stations[] = {"LOBOL001", "LOBOL002", "LOBOL003", "LOBOL004", "LOBOL005"}; static char *data_dirs[] = {"L01", "L02", "L03", "L04", "L05"}; static char data_path_out[100] = "/u/coletti/lobo/outgoing"; /*----------------------------------------------------------------------*\ | G P S T I M E C O N V E R S I O N V A R I A B L E S \*----------------------------------------------------------------------*/ /* * Note: 1980 is short because GPS time starts midnight 1/6/1980. * 2000 IS a leap year. */ const ushort gusDaysInYear[64] = { 361, 365, 365, 365, 366, 365, 365, 365, /* 1980-1987 */ 366, 365, 365, 365, 366, 365, 365, 365, /* 1988-1995 */ 366, 365, 365, 365, 366, 365, 365, 365, /* 1996-2003 */ 366, 365, 365, 365, 366, 365, 365, 365, /* 2004-2011 */ 366, 365, 365, 365, 366, 365, 365, 365, /* 2012-2019 */ 366, 365, 365, 365, 366, 365, 365, 365, /* 2020-2027 */ 366, 365, 365, 365, 366, 365, 365, 365, /* 2028-2035 */ 366, 365, 365, 365, 366, 365, 365, 365 /* 2036-2043 */ }; const uchar gucLeapYear[64] = { 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 }; const char gcDaysInMonth[2][12] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; const int gsDaysOfMonth [12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; int main(int argc, char **argv, char **envp) { char rbuf[1024]; //recv buf char mbuf[250]; //TrimTrac msg buf char pbuf[250]; //path buf char ebuf[250]; //emsg buf char sbuf[250]; //subj buf char lbuf[25]; //location ID buffer char *ptr, *rptr; uchar computed_checksum; int record_checksum, idx; int ret, addr_len; int gotMessage, gotSomething; time_t CurrentTime, Timeout; ssize_t read_ret; if( isatty(0) ){ fprintf(stderr,"This program was meant to run out of inetd\n"); exit(ERROR); } //find out who we're talking to addr_len = sizeof(addr_clnt); getpeername(0, (struct sockaddr *)&addr_clnt, &addr_len); rptr = (char *)rbuf; memset(rbuf, 0, sizeof(rbuf)); time(&CurrentTime); Timeout = CurrentTime + SERV_TIMEOUT; gotMessage = gotSomething = FALSE; while( 1 ){ read_ret = RdData(rptr, (size_t)(((char *)rbuf+sizeof(rbuf))-rptr), READ_TIMEOUT); if( read_ret == ERROR ){ snprintf(ebuf, sizeof(ebuf), "ERROR, main() - RdData() = ERROR, "); UpdateErrorLogFile(ebuf); if( rptr > (char *)rbuf ){ snprintf(sbuf, sizeof(sbuf), "TrimTrac Telemetry ERROR: main() - RdData() = ERROR"); SendEmail(sbuf, rbuf); } return( OK ); } else if( read_ret == TIMEOUT ){ snprintf(ebuf, sizeof(ebuf), "ERROR, main() - RdData() = TIMEOUT, "); UpdateErrorLogFile(ebuf); } else if( read_ret == DISCONNECT ){ if( (gotSomething == TRUE) && (gotMessage == FALSE) ){ snprintf(ebuf, sizeof(ebuf), "ERROR, main() - DISCONNECT with Partial Valid Message, "); UpdateErrorLogFile(ebuf); snprintf(sbuf, sizeof(sbuf), "TrimTrac Telemetry ERROR: main() - DISCONNECT with Incomplete Message"); SendEmail(sbuf, rbuf); return( OK ); } if( rptr > (char *)rbuf ){ snprintf(sbuf, sizeof(sbuf), "TrimTrac Telemetry ERROR: main() - DISCONNECT with Invalid Message"); SendEmail(sbuf, rbuf); } return( OK ); } else{ //we got something, so, let's gather until we can't hold no mo while( read_ret > 0 ){ if( (ptr=memchr(rbuf, '>', sizeof(rbuf)-2)) != NULL ){ //found the message head gotSomething = TRUE; if( memchr(ptr, '<', sizeof(rbuf)-1) != NULL ){ //found the message tail ptr=memccpy(mbuf, ptr, '<', sizeof(mbuf)-1); *ptr = '\0'; gotMessage = TRUE; break; } // end strstr(<) } // end strstr(>) //get some mo! rptr += read_ret; if( (rptr >= ((char *)rbuf+sizeof(rbuf))) ){ snprintf(ebuf, sizeof(ebuf), "ERROR, main() - RxBuffer overrun, "); UpdateErrorLogFile(ebuf); snprintf(sbuf, sizeof(sbuf), "TrimTrac Telemetry ERROR: main() - RxBuffer overrun"); SendEmail(sbuf, rbuf); return( OK ); } read_ret = RdData(rptr, (size_t)(((char *)rbuf+sizeof(rbuf))-rptr), READ_TIMEOUT); } //end inner while }// end read_ret else if(gotMessage == TRUE) // the only way out! break; else time(&CurrentTime); if( CurrentTime > Timeout ){ snprintf(ebuf, sizeof(ebuf), "ERROR, main() - Timeout, "); UpdateErrorLogFile(ebuf); if( rptr > (char *)rbuf ){ snprintf(sbuf, sizeof(sbuf), "TrimTrac Telemetry ERROR: main() - Timeout with Pending Message"); SendEmail(sbuf, rbuf); } return( OK ); } }//end forever while //to get here we must have gotten a correctly formatted message if( (ptr=CalcTAIPChecksum(mbuf, &computed_checksum)) == NULL){ snprintf(ebuf, sizeof(ebuf), "ERROR, main() - Checksum NOT Found, "); UpdateErrorLogFile(ebuf); snprintf(sbuf, sizeof(sbuf), "TrimTrac Telemetry ERROR: main() - Checksum NOT Found"); SendEmail(sbuf, mbuf); return( OK ); } else{ record_checksum = 0xFF00; if( sscanf(ptr,"%2X<", &record_checksum) == 1) record_checksum &= 0x00FF; if( record_checksum != computed_checksum ){ snprintf(ebuf, sizeof(ebuf), "ERROR, main() - Checksum Mismatch, "); UpdateErrorLogFile(ebuf); snprintf(sbuf, sizeof(sbuf), "TrimTrac Telemetry ERROR: main() - Checksum Mismatch"); SendEmail(sbuf, mbuf); return( OK ); } } if( (ptr=strstr(mbuf, "ID=")) == NULL ){ snprintf(ebuf, sizeof(ebuf), "ERROR, main() - Station ID NOT Found, "); UpdateErrorLogFile(ebuf); snprintf(sbuf, sizeof(sbuf), "TrimTrac Telemetry ERROR: main() - Station ID NOT Found"); SendEmail(sbuf, mbuf); return( OK ); } else{ ptr=memccpy(lbuf, ptr+3, ';', sizeof(lbuf)-1); --ptr; *ptr = '\0'; if( (idx = MatchStation(lbuf)) == ERROR ){ snprintf(ebuf, sizeof(ebuf), "ERROR, main() - Station ID Mismatch, "); UpdateErrorLogFile(ebuf); snprintf(sbuf, sizeof(sbuf), "TrimTrac Telemetry ERROR: main() - Station ID Mismatch"); SendEmail(sbuf, mbuf); return( OK ); } } //test if file system "Tepmest" can be mounted! if( (ret = access(data_path_out, F_OK)) != OK ){ snprintf(ebuf, sizeof(ebuf)-1, "ERROR, main() - access() ret = %d - Tempest File System Unavailable", ret); UpdateErrorLogFile(ebuf); snprintf(sbuf, sizeof(sbuf)-1, "TrimTrac Telemetry ERROR: main() - Tempest Mount Access Error"); SendEmail(sbuf, mbuf); return( OK ) ; } if( strstr(mbuf, "RTKP") != NULL ){ //if it's a position message, save the data into the corresponding directory memset(pbuf, '\0', sizeof(pbuf)); snprintf(pbuf, sizeof(pbuf)-1, "%s/%s/", data_path_out, data_dirs[idx]); UpdateDataFile(pbuf, mbuf); } else{ snprintf(sbuf, sizeof(sbuf)-1, "L%02d TrimTrac Telemetry without RTKP Message", idx+1); SendEmail(sbuf, mbuf); } sleep( 15 ); //give the terminal server some time to disconnect return( OK ); } int MatchStation(char *id) { int choice; for (choice=0; (choice < LastChoice); choice++) { if( strcmp(stations[choice], id) == 0 ) return( choice ); } return( ERROR ); } /************************************************************************/ /* Function : UpdateDataFile */ /* Purpose : */ /* Input : None */ /* Outputs : None */ /* Comments : */ /************************************************************************/ void UpdateDataFile(char *path, char *msg) { FILE *datfile; char *ptr; char sbuf[250]; char ebuf[250]; char pbuf[250]; char tbuf[50]; char var[25]; int year, gps_week; int gps_speed, gps_heading; long gps_secs, gps_alt; float t_stamp; double gps_lat, gps_lon; uchar Second, Minute, Hour, Day, Month; ushort Year; time_t e_secs; struct tm t, *tp; tp = &t; t_stamp = GetDOYtime(&year); //local timestamp ptr = strrchr(msg, '>'); //start of message ptr += 14; //start of GPS week number memset(var, '\0', sizeof(var)); strncpy(var, ptr, 4); gps_week = atoi(var); ptr += 4; //start of GPS seconds memset(var, '\0', sizeof(var)); strncpy(var, ptr, 6); gps_secs = atol(var); ptr += 18; //start of GPS lat memset(var, '\0', sizeof(var)); strncpy(var, ptr, 10); gps_lat = strtod(var, NULL); ptr += 10; //start of GPS lon memset(var, '\0', sizeof(var)); strncpy(var, ptr, 11); gps_lon = strtod(var, NULL); ptr += 11; //start of GPS alt memset(var, '\0', sizeof(var)); strncpy(var, ptr, 6); gps_alt = atol(var); ptr += 6; //start of GPS speed memset(var, '\0', sizeof(var)); strncpy(var, ptr, 3); gps_speed = atoi(var); ptr += 3; //start of GPS heading memset(var, '\0', sizeof(var)); strncpy(var, ptr, 3); gps_heading = atoi(var); rtcGPStoSystemTime((ushort)gps_week, (ulong)gps_secs, &Second, &Minute, &Hour, &Day, &Month, &Year); tp->tm_year = (int)Year-1900; tp->tm_mon = (int)Month; tp->tm_mday = (int)Day; tp->tm_hour = (int)Hour; tp->tm_min = (int)Minute; tp->tm_sec = (int)Second; putenv("TZ=GMT0"); tzset(); e_secs = mktime(tp); strftime(tbuf, sizeof(tbuf), "%a %d %b %Y %H:%M:%S", tp); putenv("TZ=PST8PDT"); tzset(); memset(pbuf, '\0', sizeof(pbuf)); strncpy(pbuf, path, sizeof(pbuf)); ptr = strrchr(pbuf, '/'); strcat(ptr+1, "trimtrac.log"); datfile = fopen(pbuf, "a+"); if( datfile == NULL ){ fclose(datfile); snprintf(ebuf, sizeof(ebuf), "ERROR, UpdateDataFile() - Error Opening Datafile"); UpdateErrorLogFile(ebuf); snprintf(sbuf, sizeof(sbuf), "TrimTrac Telemetry ERROR: UpdateDataFile() - Error Opening Datafile"); SendEmail(sbuf, msg); } else{ fprintf(datfile,"%04d.%010.6f, %d, %ld, %lu, %s, %+.7lf, %.7lf, %ld, %d, %d, %s\r\n", year, t_stamp, gps_week, gps_secs, e_secs, tbuf, gps_lat/10000000.0, gps_lon/10000000.0, gps_alt, gps_speed, gps_heading, msg); } memset(pbuf, '\0', sizeof(pbuf)); strncpy(pbuf, path, sizeof(pbuf)); ptr = strrchr(pbuf, '/'); strcat(ptr+1, "lastfix.log"); datfile = fopen(pbuf, "w+"); if( datfile == NULL ){ fclose(datfile); snprintf(ebuf, sizeof(ebuf), "ERROR, UpdateDataFile() - Error Opening Datafile"); UpdateErrorLogFile(ebuf); snprintf(sbuf, sizeof(sbuf), "TrimTrac Telemetry ERROR: UpdateDataFile() - Error Opening Datafile"); SendEmail(sbuf, msg); } else{ fprintf(datfile,"%lu, %+.7lf, %.7lf, %ld, %d, %d\r\n", e_secs, gps_lat/10000000.0, gps_lon/10000000.0, gps_alt, gps_speed, gps_heading); fclose(datfile); } } /************************************************************************/ /* Function : UpdateErrorLogFile */ /* Purpose : keep a usage log of all unsuccessful sessions */ /* Input : None */ /* Outputs : None */ /* Comments : */ /************************************************************************/ void UpdateErrorLogFile(char *emsg) { FILE *logfile; char timebuf[64]; time(&timer); timeptr = localtime(&timer); strftime(timebuf, sizeof(timebuf)-1, "%a %d %b %Y %H:%M:%S", timeptr); logfile = fopen("/u/coletti/lobo/logs/lobotrak.log", "a+"); fprintf(logfile,"%s, %s, %ld, %s, %u\r\n", emsg, timebuf, getpid(), inet_ntoa(addr_clnt.sin_addr), (unsigned)ntohs(addr_clnt.sin_port)); fflush(logfile); fclose(logfile); } void SendEmail(char *subject, char *message) { FILE *out; out = popen(SENDMAIL, "w"); fprintf(out, "From: %s <%s>\n", "LoboTrak", SENDER); fprintf(out, "To: %s\n", RECIPIENT); fprintf(out, "Subject: %s\n", subject); fprintf(out, "\n \n"); fprintf(out, "%s", message); fprintf(out, "\n \n"); pclose(out); } /************************************************************************/ /* Function : RdData */ /* Purpose : */ /* Input : None */ /* Outputs : None */ /* Comments : */ /************************************************************************/ ssize_t RdData(char *rx_buf, size_t len, ushort tm_out) { char ebuf[256]; int sel_ret; fd_set rx_set; struct timeval tmout; ssize_t read_ret; FD_ZERO(&rx_set); FD_SET(0, &rx_set); tmout.tv_sec = tm_out; tmout.tv_usec = 0; do { sel_ret = select(FD_SETSIZE, &rx_set, NULL, NULL, &tmout); } while ( (sel_ret == -1) && (errno == EINTR) ); if( sel_ret == 0 ) return( TIMEOUT ); else if( sel_ret < 0 ){ snprintf(ebuf, sizeof(ebuf)-1, "ERROR, RdDataD() - select() ret = %d - Errno = %s, ", sel_ret, strerror(errno)); UpdateErrorLogFile(ebuf); return( ERROR ); } do { clearerr(stdin); read_ret = read(STDIN_FILENO, rx_buf, len); } while ( ferror(stdin) && (errno == EINTR) ); if( read_ret == 0 ) return( DISCONNECT ); else if( read_ret < 0 ) { snprintf(ebuf, sizeof(ebuf)-1, "ERROR, RdDataD() - Errno = %s, ", strerror(errno)); UpdateErrorLogFile(ebuf); return( ERROR ); } else return( read_ret ); } float GetDOYtime(int *year) { int DOY; float fDOY; time_t timer; struct tm *dt; time(&timer); dt = gmtime(&timer); DOY = DayOfYear(dt->tm_year+1900, dt->tm_mon+1, dt->tm_mday); fDOY = (float) DOY + (float) (dt->tm_hour/24.0) + (float) (dt->tm_min/1440.0) + (float) (dt->tm_sec/86400.0); *year = dt->tm_year+1900; return( fDOY ); } /*****************************************************************************/ /* Name: DayOfYear */ /* Type: Function */ /* Purpose: Day of the year from calendar date */ /* Arguments: */ /* Date : the date */ /* Return value: */ /* the day of the year, Jan 1st = 1, Jan 2nd = 2, etc. */ /*****************************************************************************/ ushort DayOfYear(int yy, int mm, int dd) { int K; /* Check for leap year */ if ((yy % 4 == 0) && ((yy < 1583) || (yy % 100 != 0) || (yy % 400 == 0))) K = 1; else K = 2; return (275 * mm / 9) - K * ((mm + 9) / 12) + (dd - 30); } /************************************************************************/ /* Function : CalcTAIChecksum */ /* Input : pszTAIPMsg, checksum */ /* Returns : Pointer to Checksum Header ;* if found, else NULL */ /* Comments : This function checks for the presense of the TAIP */ /* checksum header. If it exisits it calculates the */ /* checksum and places it in the checksum parameter and */ /* returns a pointer to the checksum header. Otherwise it */ /* returns NULL without changing checksum. */ /************************************************************************/ char *CalcTAIPChecksum(char *pszTAIPMsg, uchar *checksum) { char *pcEnd; char *pc; uchar cs; if( (pcEnd = strstr(pszTAIPMsg, ";*")) != NULL ){ pc = pszTAIPMsg; //start of the checksumed chars. pcEnd += 2; //move to the first non-checksumed char. cs = 0; while( pc < pcEnd ){ cs ^= *pc; pc++; } *checksum = cs; return( pcEnd ); } else return( NULL ); } /*----------------------------------------------------------------------*\ | | Function Name: rtcSystemTimeToGPS() | | Description: | Converts the system time (represented in hh:mm:ss dd/mm/yyyy | format) to the GPS time format (week number and time of week). | | Input Parameters: range | ucSecond - current second (0 .. 59) | ucMinute - current minute (0 .. 59) | ucHour - current hour (0 .. 23) | ucDay - current day of month (1 .. 31) | ucMonth - current month (0 .. 11) | usYear - current year (1980 ... xxxx) | | Output Parameters: | pusWeekNum - a pointer to the week number variable | pulTimeOfWeek - a pointer to the time of week variable (secs) | | Return Value: | RTC_OK if the the conversion was successful; | RTC_ERROR otherwise | \*----------------------------------------------------------------------*/ uchar rtcSystemTimeToGPS (ushort* pusWeekNum, ulong* pulTimeOfWeek, uchar ucSecond, uchar ucMinute, uchar ucHour, uchar ucDay, uchar ucMonth, ushort usYear) { ulong ulTime, ulGpsDay; /* Out-of-bounds check. */ if ((ucSecond >= 60) || (ucMinute >= 60) || (ucHour >= 24) || (ucDay > 31) || (ucDay == 0) || (ucMonth > 12) || (usYear < 1980)) { return ERROR; } /* * Convert the current day to the day from the start of the GPS * time (Jan 6, 1980). */ ulGpsDay = ucDay + gsDaysOfMonth[ucMonth] + (usYear-1980)*365 - 6; /* Leap years - add one for every fourth previous February */ ulGpsDay += (ulong)((12*(usYear-1976) + ucMonth - 2)/48); /* Calculate the time (in seconds) from the start of the GPS time. */ ulTime = (ulong)(ucSecond + 60*(ucMinute + 60*(ucHour + 24*ulGpsDay))); /* Calculate the GPS week number and the GPS time of week. */ *pusWeekNum = (ushort)(ulTime/604800); *pulTimeOfWeek = (ulTime - (ulong)((*pusWeekNum)*604800)); return OK; } /*----------------------------------------------------------------------*\ | | Function Name: rtcGPStoSystemTime() | | Description: | Converts the time in the GPS time format (week number and time | of week) to the system time (represented in hh:mm:ss dd/mm/yyyy | format). | | Input Parameters: | usWeekNum - current week number | ulTimeOfWeek - current time of week variable (secs, 0..604799) | | Output Parameters: | pucSecond - current second (0 .. 59) | pucMinute - current minute (0 .. 59) | pucHour - current hour (0 .. 23) | pucDay - current day of month (1 .. 31) | pucMonth - current month (0 .. 11) | pusYear - current year (1980 ... xxxx) | | Return Value: | RTC_OK if the the conversion was successful; | RTC_ERROR otherwise | \*----------------------------------------------------------------------*/ uchar rtcGPStoSystemTime (ushort usWeekNum, ulong ulTimeOfWeek, uchar* pucSecond, uchar* pucMinute, uchar* pucHour, uchar* pucDay, uchar* pucMonth, ushort* pusYear) { ulong ulTime, ulYears, ulDays, ulDaysThisMonth, ulMonths; /* Out-of-bounds check. */ if (ulTimeOfWeek >= 604800) { return ERROR; } /* Calculate the time (in seconds) from the start of the GPS time. */ ulTime = (ulong)(ulTimeOfWeek + usWeekNum*604800); /* Calculate the number of days from the start of the GPS time. */ ulDays = (ulTime / (24 * 3600)); /* Determine elapsed years since start of GPS */ ulYears = 0; while (1) { if (ulDays < gusDaysInYear[ulYears]) { break; } ulDays -= gusDaysInYear[ulYears]; ulYears++; } /* Determine elapsed months since start of year */ ulMonths = 0; while (1) { if (gucLeapYear[ulYears]) { ulDaysThisMonth = gcDaysInMonth[1][ulMonths]; } else { ulDaysThisMonth = gcDaysInMonth[0][ulMonths]; } if (ulDays < ulDaysThisMonth) { break; } ulDays -= ulDaysThisMonth; ulMonths++; } /* Figure out the time parameters. */ *pucHour = (uchar) ((ulTime % 86400) / 3600); *pucMinute = (uchar) ((ulTime % 3600) / 60); *pucSecond = (uchar) ((ulTime % 60)); *pucMonth = (uchar) (ulMonths); *pucDay = (uchar) (ulDays + 1); *pusYear = (ushort)(ulYears + 1980); return OK; }