/* (C) 2011 by Dieter Spaar * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ /* The idea behind the RRLP server is as follows: - Every RRLP related response from the MS is sent to the RRLP server - If it is a position, the decoded position is returned - If there is an error, an error message is returned - If assistance data are requested, a set of PDUs with the required data is generated. A set of PDUs is used to not exceed the maximum size for a RRLP message. - If an assitance data ACK is received, the next assitance data PDU is sent (no data are returned if it is the final message). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gps.h" #include "ubx.h" #include "ubx-parse.h" #include "rrlp.h" #define DEBUG 1 #if DEBUG #define printd(x, args ...) printf(x, ## args) #else #define printd(x, args ...) #endif #define DEBUG_GPS 1 #if DEBUG_GPD #define prints(x, args ...) printf(x, ## args) #else #define prints(x, args ...) #endif //#define PDU RRLP_Component /* Convert "Type" defined by -DPDU into "asn_DEF_Type" */ #define ASN_DEF_PDU(t) asn_DEF_ ## t #define DEF_PDU_Type(t) ASN_DEF_PDU(t) #define PDU_Type DEF_PDU_Type(PDU) /* TODO */ #define RRLP_SERV_PORT 7890 #define MAX_RRLP_DATA 256 /* Server cmds */ #define RRLP_CMD_MS_DATA 1 /* data from MS */ #define RRLP_CMD_MS_DATA_SLOW 2 /* data from MS, slow channel */ /* Server response */ #define RRLP_RSP_ASSIST_DATA 1 /* assitance data, send to MS */ #define RRLP_RSP_RRLP_ERROR 2 /* RRLP error */ #define RRLP_RSP_RRLP_POSITION 3 /* RRLP position */ #define RRLP_RSP_ERROR 4 /* something went wrong */ #define SUBSC_ID_NONE 0 #define N_MAX_AD_RECORDS 10 #define N_MAX_AD_RECORDS_SLOW 1 /* maximum number of messages used on slow channel */ enum rrlp_state { RRLP_NONE = 0, RRLP_ASSIST, RRLP_ERROR, RRLP_POSITION, RRLP_WAIT_POSITION }; /* record for the state of a subscriber */ struct rrlp_sub_state { long long unsigned int id; /* subscriber ID from OpenBSC */ time_t time_first; /* time when record was used the first time */ int reference_num; /* RRLP reference number */ enum rrlp_state state; int error; /* error */ /* a large assistance data APDU is split in smaller parts */ int cur_assist_record; /* current assistance data record */ uint8_t assist_data_len[N_MAX_AD_RECORDS]; /* length of a record */ uint8_t assist_data[N_MAX_AD_RECORDS][MAX_RRLP_DATA]; /* record */ /* GPS position */ long latitude; /* */ long longitude; /* */ long altitude; /* in meter */ }; void free_rrlp_subscriber(struct rrlp_sub_state *rrlp_sub_state); /* static allocation, might change that for a large number of queries at once */ #define N_MAX_RRLP_STATES 3000 struct rrlp_sub_state g_rrlp_states[N_MAX_RRLP_STATES]; /* assistance data from the GPS receiver */ struct gps_assist_data g_gps; /* According to TS 23.032 So far only "Ellipsoid point with altitude and uncertainty Ellipsoid" seems to be sent from the MS. */ int decode_pos_estimate(struct rrlp_sub_state *rrlp_sub_state, uint8_t *data, int len) { long latitude; long longitude; long altitude; if(len < 1) { fprintf(stderr, "decode_pos_estimate: len too short (%d)\n", len); return -1; } /* type of shape */ switch((data[0] & 0xF0) >> 4) { case 0x00: printd("Ellipsoid point\n"); break; case 0x01: printd("Ellipsoid point with uncertainty Circle\n"); break; case 0x03: printd("Ellipsoid point with uncertainty Ellipse\n"); break; case 0x05: printd("Polygon\n"); break; case 0x08: printd("Ellipsoid point with altitude\n"); break; case 0x09: printd("Ellipsoid point with altitude and uncertainty Ellipsoid\n"); if(len != 14) { fprintf(stderr, "decode_pos_estimate: invalid len (%d)\n", len); return -1; } /* Deggrees of latitude */ latitude = ((data[1] & 0x7F) << 16) + (data[2] << 8) + data[3]; if(data[1] & 0x80) latitude = -latitude; printd("latitude = %f\n", ((double)latitude * 90.0) / 0x800000L); /* Deggrees of longitude */ longitude = (data[4] << 16) + (data[5] << 8) + data[6]; if(data[4] & 0x80) longitude |= 0xFF000000; printd("longitude = %f\n", ((double)longitude * 360.0) / 0x1000000L); /* Altitude */ altitude = ((data[7] & 0x7F) << 8) + data[8]; if(data[7] & 0x80) altitude = -altitude; printd("altitude = %ld\n", altitude); /* Uncertainty semi-major */ /* Uncertainty semi-minor */ /* Orientation of major axis */ /* Uncertainty Altitude */ /* Confidence */ rrlp_sub_state->latitude = latitude; rrlp_sub_state->longitude = longitude; rrlp_sub_state->altitude = altitude; return 0; break; case 0x0A: printd("Ellipsoid Arc\n"); break; default: printd("Unknown shape type: %d\n", (data[0] & 0xF0) >> 4); break; } fprintf(stderr, "cannot decode shape type (%d)\n", (data[0] & 0xF0) >> 4); return -1; } int decode_rrlp_data(struct rrlp_sub_state *rrlp_sub_state, int max_reply_msgs, uint8_t *data, int len, uint8_t *cmd_reply, uint8_t *reply, int *len_reply) { static asn_TYPE_descriptor_t *pduType = &PDU_Type; void *structure; /* Decoded structure */ asn_codec_ctx_t *opt_codec_ctx = 0; asn_dec_rval_t rval; int rc; structure = 0; rval = uper_decode(opt_codec_ctx, pduType, (void **)&structure, data, len, 0, 0); if(!structure) { if(errno) { fprintf(stderr, "Error: errno = %d\n", errno); return -1; } else { /* EOF */ fprintf(stderr, "EOF\n"); return -1; } } else { /* dump PDU */ asn_fprint(stdout, pduType, structure); long l; PDU_t *pdu = (PDU_t*)structure; printd("\n"); printd("-- referenceNumber = %ld\n", pdu->referenceNumber); rrlp_sub_state->reference_num = pdu->referenceNumber; /* CHOICE */ switch(pdu->component.present) { case RRLP_Component_PR_msrPositionReq: printd("-- msrPositionReq\n"); *cmd_reply = RRLP_RSP_ERROR; strcpy(reply, "APU unsupported: msrPositionReq"); *len_reply = strlen(reply) + 1; break; case RRLP_Component_PR_msrPositionRsp: printd("-- msrPositionRsp\n"); /* "MsrPosition-Rsp": process either "locationInfo" or "locationError" */ /* SEQUENCE with OPTIONAL components */ MsrPosition_Rsp_t *msrPositionRsp = &pdu->component.choice.msrPositionRsp; if(msrPositionRsp->multipleSets) { printd("---- multipleSets\n"); } if(msrPositionRsp->referenceIdentity) { printd("---- referenceIdentity\n"); } if(msrPositionRsp->otd_MeasureInfo) { printd("---- otd_MeasureInfo\n"); } if(msrPositionRsp->locationInfo) { printd("---- locationInfo\n"); printd("------ refFrame = %ld\n", msrPositionRsp->locationInfo->refFrame); if(msrPositionRsp->locationInfo->gpsTOW) { printd("------ gpsTOW = %ld\n", *msrPositionRsp->locationInfo->gpsTOW); /* in ms */ } printd("---- fixType = %ld\n", msrPositionRsp->locationInfo->fixType); printd("---- posEstimate\n"); int n = msrPositionRsp->locationInfo->posEstimate.size; uint8_t *buf = msrPositionRsp->locationInfo->posEstimate.buf; #if 1 /* dump data */ int i; for(i = 0; i < n; i++) printd("%02X ", buf[i]); printd("\n"); #endif /* TODO */ if(decode_pos_estimate(rrlp_sub_state, buf, n) != 0) { fprintf(stderr, "decode_pos_estimate failed\n"); *cmd_reply = RRLP_RSP_ERROR; strcpy(reply, "decode_pos_estimate failed"); *len_reply = strlen(reply) + 1; } else { *cmd_reply = RRLP_RSP_RRLP_POSITION; rrlp_sub_state->state = RRLP_POSITION; reply[0] = rrlp_sub_state->latitude & 0xFF; reply[1] = (rrlp_sub_state->latitude >> 8) & 0xFF; reply[2] = (rrlp_sub_state->latitude >> 16) & 0xFF; reply[3] = (rrlp_sub_state->latitude >> 24) & 0xFF; reply[4] = rrlp_sub_state->longitude & 0xFF; reply[5] = (rrlp_sub_state->longitude >> 8) & 0xFF; reply[6] = (rrlp_sub_state->longitude >> 16) & 0xFF; reply[7] = (rrlp_sub_state->longitude >> 24) & 0xFF; reply[ 8] = rrlp_sub_state->altitude & 0xFF; reply[ 9] = (rrlp_sub_state->altitude >> 8) & 0xFF; reply[10] = (rrlp_sub_state->altitude >> 16) & 0xFF; reply[11] = (rrlp_sub_state->altitude >> 24) & 0xFF; *len_reply = 3 * 4; } /* we got a location, ignore anything else */ goto done; } if(msrPositionRsp->gps_MeasureInfo) { printd("---- gps_MeasureInfo\n"); } if(msrPositionRsp->locationError) { printd("---- locationError\n"); if(asn_INTEGER2long(&msrPositionRsp->locationError->locErrorReason, &l) == 0) { asn_INTEGER_specifics_t *specs = (asn_INTEGER_specifics_t *)asn_DEF_LocErrorReason.specifics; const asn_INTEGER_enum_map_t *el; el = INTEGER_map_value2enum(specs, l); if(el) { printd("---- locErrorReason = %ld (%s)\n", l, el->enum_name); sprintf(reply, "locationError %ld (%s)", l, el->enum_name); } else { printd("---- locErrorReason = %ld\n", l); sprintf(reply, "locationError %ld", l); } } else { printd("---- locErrorReason: decode error\n"); strcpy(reply, "locationError ??"); l = 9999; } if(msrPositionRsp->locationError->additionalAssistanceData) { printd("------ additionalAssistanceData\n"); if(msrPositionRsp->locationError->additionalAssistanceData->gpsAssistanceData) { printd("------ gpsAssistanceData\n"); int n = msrPositionRsp->locationError->additionalAssistanceData->gpsAssistanceData->size; uint8_t *buf = msrPositionRsp->locationError->additionalAssistanceData->gpsAssistanceData->buf; struct rrlp_assist_req ar; #if 1 /* dump data */ int i; for(i = 0; i < n; i++) printd("%02X ", buf[i]); printd("\n"); #endif /* check state */ if(rrlp_sub_state->state == RRLP_ASSIST) { fprintf(stderr, "assistanceData in invalid state\n"); *cmd_reply = RRLP_RSP_ERROR; strcpy(reply, "assistanceData in invalid state"); *len_reply = strlen(reply) + 1; goto done; } if(rrlp_sub_state->state == RRLP_WAIT_POSITION) { fprintf(stderr, "assistanceData already sent\n"); *cmd_reply = RRLP_RSP_ERROR; strcpy(reply, "assistanceData already sent"); *len_reply = strlen(reply) + 1; goto done; } /* decode the requested assistance data */ rc = rrlp_decode_assistance_request(&ar, buf, n); if(rc != 0) { fprintf(stderr, "rrlp_decode_assistance_request failed: %d\n", rc); *cmd_reply = RRLP_RSP_ERROR; sprintf(reply, "rrlp_decode_assistance_request failed: %d", rc); *len_reply = strlen(reply) + 1; } else if(l == LocErrorReason_gpsAssDataMissing) { void *pdus[N_MAX_AD_RECORDS]; int lens[N_MAX_AD_RECORDS]; int i; int error = 0; int ref_num; /* take a new reference number for the assistance data */ ref_num = rrlp_sub_state->reference_num; ref_num++; if(ref_num > 7) /* 0..7 */ ref_num = 7; if(ref_num == 0) /* 0 is special */ ref_num = 1; /* generate PDUs */ rc = rrlp_gps_assist_pdus(ref_num, &g_gps, &ar, pdus, lens, N_MAX_AD_RECORDS); if(rc < 0) { fprintf(stderr, "rrlp_gps_assist_pdus: %d\n", rc); *cmd_reply = RRLP_RSP_ERROR; sprintf(reply, "rrlp_gps_assist_pdus failed: %d", rc); *len_reply = strlen(reply) + 1; goto done; } /* clear messages */ for(i = 0; i < N_MAX_AD_RECORDS; i++) { rrlp_sub_state->assist_data_len[i] = 0; memset(rrlp_sub_state->assist_data[i], 0, MAX_RRLP_DATA); } /* check if maximum number of messages exceeded */ if(rc > max_reply_msgs) { for(i = 0; i < rc; i++) { if(pdus[i]) asn_DEF_PDU.free_struct(&asn_DEF_PDU, pdus[i], 0); } fprintf(stderr, "too many assitance messages: (%d > %d)\n", rc, max_reply_msgs); *cmd_reply = RRLP_RSP_ERROR; sprintf(reply, "too many assitance messages: (%d > %d)\n", rc, max_reply_msgs); *len_reply = strlen(reply) + 1; goto done; } /* copy PDUs to subscriber state */ for(i = 0; i < rc; i++) { printd("%p %d\n", pdus[i], lens[i]); if(pdus[i] == NULL) { fprintf(stderr, "pdus[%d] == NULL\n", i); sprintf(reply, "pdus[%d] == NULL\n", i); error = -1; } #if 0 /* Debug */ { int rv, fd; char filename[50]; sprintf(filename, "pdu%02d.uper", i); fd = open(filename, O_TRUNC | O_CREAT | O_RDWR, S_IREAD | S_IWRITE); if (fd < 0) printf("File error\n"); rv = write(fd, pdus[i], lens[i]); if(rv != lens[i]) printf("Write error\n"); close(fd); } #endif #if 0 /* Debug */ int j; for(j = 0; j < lens[i]; j++) printf("%02X ", ((uint8_t*)pdus[i])[j]); printf("\n"); #endif if(lens[i] > MAX_RRLP_DATA) { fprintf(stderr, "lens[%d] > MAX_RRLP_DATA (%d)\n", i, lens[i]); sprintf(reply, "lens[%d] > MAX_RRLP_DATA (%d)\n", i, lens[i]); error = -2; } if(error == 0) { rrlp_sub_state->assist_data_len[i] = lens[i]; memcpy(rrlp_sub_state->assist_data[i], pdus[i], lens[i]); } if(pdus[i]) free(pdus[i]); } if(error) { *cmd_reply = RRLP_RSP_ERROR; *len_reply = strlen(reply) + 1; goto done; } *cmd_reply = RRLP_RSP_ASSIST_DATA; rrlp_sub_state->state = RRLP_ASSIST; rrlp_sub_state->cur_assist_record = 0; /* send first assistent data block */ i = rrlp_sub_state->cur_assist_record; memcpy(reply, rrlp_sub_state->assist_data[i], rrlp_sub_state->assist_data_len[i]); *len_reply = rrlp_sub_state->assist_data_len[i]; /* advance if not already at the end */ if(rrlp_sub_state->assist_data_len[i]) rrlp_sub_state->cur_assist_record++; /* we got assistance data, ignore anything else */ goto done; } } } rrlp_sub_state->state = RRLP_ERROR; *cmd_reply = RRLP_RSP_RRLP_ERROR; *len_reply = strlen(reply) + 1; /* we got an error, ignore anything else */ goto done; } if(msrPositionRsp->extensionContainer) { printd("---- extensionContainer\n"); } /* no "locationInfo" or "locationError" */ *cmd_reply = RRLP_RSP_ERROR; strcpy(reply, "unexpected MsrPosition-Rsp component"); *len_reply = strlen(reply) + 1; break; case RRLP_Component_PR_assistanceData: printd("-- assistanceData\n"); *cmd_reply = RRLP_RSP_ERROR; strcpy(reply, "APDU unsupported: assistanceData"); *len_reply = strlen(reply) + 1; break; case RRLP_Component_PR_assistanceDataAck: printd("-- assistanceDataAck\n"); /* check state */ if(rrlp_sub_state->state != RRLP_ASSIST) { *cmd_reply = RRLP_RSP_ERROR; fprintf(stderr, "assistanceData Ack in invalid state\n"); strcpy(reply, "assistanceData Ack in invalid state"); *len_reply = strlen(reply) + 1; goto done; } /* send next assitance data reply or done */ *cmd_reply = RRLP_RSP_ASSIST_DATA; int i = rrlp_sub_state->cur_assist_record; if(i < N_MAX_AD_RECORDS) { memcpy(reply, rrlp_sub_state->assist_data[i], rrlp_sub_state->assist_data_len[i]); *len_reply = rrlp_sub_state->assist_data_len[i]; /* advance if not already at the end */ if(rrlp_sub_state->assist_data_len[i]) rrlp_sub_state->cur_assist_record++; else { rrlp_sub_state->state = RRLP_NONE; } } else { *len_reply = 0; rrlp_sub_state->state = RRLP_WAIT_POSITION; } break; case RRLP_Component_PR_protocolError: printd("-- protocolError\n"); ProtocolError_t *protocolError = &pdu->component.choice.protocolError; if(asn_INTEGER2long(&protocolError->errorCause, &l) == 0) { asn_INTEGER_specifics_t *specs = (asn_INTEGER_specifics_t *)asn_DEF_ErrorCodes.specifics; const asn_INTEGER_enum_map_t *el; el = INTEGER_map_value2enum(specs, l); if(el) { printd("---- errorCause = %ld (%s)\n", l, el->enum_name); sprintf(reply, "protocolError %ld (%s)", l, el->enum_name); } else { printd("---- errorCause = %ld\n", l); sprintf(reply, "protocolError %ld", l); } } else { printd("---- errorCause: decode error\n"); strcpy(reply, "protocolError ??"); } rrlp_sub_state->state = RRLP_ERROR; *cmd_reply = RRLP_RSP_RRLP_ERROR; *len_reply = strlen(reply) + 1; break; default: printd("-- unknown %d\n", pdu->component.present); *cmd_reply = RRLP_RSP_ERROR; sprintf(reply, "unknown PDU conponent %d", pdu->component.present); *len_reply = strlen(reply) + 1; break; } done: /* free structure */ asn_DEF_PDU.free_struct(&asn_DEF_PDU, (void*)structure, 0); } return 0; } /***********************************************************************/ static int ubx_parse(struct gps_assist_data *gps, uint8_t *data, int len) { int rv = 0, i; /* Parse each message */ for(i = 0; i < len; ) { rv = ubx_msg_dispatch(ubx_parse_dt, data + i, len - i, gps); if (rv < 0) i++; /* Invalid message: try one byte later */ else i += rv; } return (rv >= 0) ? 0 : rv; } /***********************************************************************/ /* taken from OsmocomBB */ static int serial_init(const char *serial_port, speed_t baudrate) { int rc, serial_fd; struct termios tio; serial_fd = open(serial_port, O_RDWR | O_NOCTTY | O_NDELAY); if (serial_fd < 0) { perror("cannot open serial port"); return serial_fd; } //fcntl(serial_fd, F_SETFL, 0); /* Configure serial interface */ rc = tcgetattr(serial_fd, &tio); if (rc < 0) { close(serial_fd); perror("tcgetattr()"); return rc; } rc = cfsetispeed(&tio, baudrate); if (rc < 0) { close(serial_fd); perror("cfsetispeed()"); return rc; } rc = cfsetospeed(&tio, baudrate); if (rc < 0) { close(serial_fd); perror("cfsetospeed()"); return rc; } tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS); tio.c_cflag |= (CREAD | CLOCAL | CS8); tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); tio.c_iflag |= (INPCK | ISTRIP); tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR); tio.c_oflag &= ~(OPOST | ONLCR); rc = tcsetattr(serial_fd, TCSANOW, &tio); if (rc < 0) { close(serial_fd); perror("tcsetattr()"); return rc; } return serial_fd; } #define GPS_USB 1 /* set to 1 if uBlox GPS receiver is connected to USB */ #if GPS_USB #define GPS_BAUDRATE B115200 #else #define GPS_BAUDRATE B9600 #endif /* USB: switch back to NMEA output "$PUBX,41,3,0003,0002,9600,0*17\r\n" */ static int set_ubx_protocol(int fd) { int len; /* Set Protocol and baudrate (switch to UBX output, still allow NMEA input) */ #if GPS_USB /* if uBlox is connected through USB */ char upx_chg_mode[] = "$PUBX,41,3,0003,0001,115200,0*1C\r\n"; #else /* if uBlox is connected through the serial port */ char upx_chg_mode[] = "$PUBX,41,1,0003,0001,9600,0*16\r\n"; #endif len = write(fd, upx_chg_mode, sizeof(upx_chg_mode) - 1); if(len != sizeof(upx_chg_mode) - 1) { perror("write()"); return len; } /* a short delay */ sleep(1); /* flush the input buffer (there is probably are more elegant solution) */ for(;;) { fd_set readset; struct timeval tv; int rc; int len; uint8_t buf[256]; /* check for data */ FD_ZERO(&readset); FD_SET(fd, &readset); tv.tv_sec = 0; tv.tv_usec = 300 * 1000; rc = select(fd + 1, &readset, NULL, NULL, &tv); if(rc < 0) { if(errno != EINTR) { fprintf(stderr, "select() failed: (%d) %s\n", rc, strerror(errno)); break; } } if(!FD_ISSET(fd, &readset)) break; /* read data */ len = read(fd, buf, sizeof(buf)); if(len <= 0) { fprintf(stderr, "serial read() failed: (%d) %s\n", len, strerror(errno)); break; } } return 0; } static int ubx_get_data(int fd) { int len; char upx_msg1[] = "\xb5\x62\x01\x02\x00\x00\x03\x0a"; /* NAV-POSLLH */ char upx_msg2[] = "\xb5\x62\x0b\x10\x00\x00\x1b\x5c"; /* AID-DATA */ char upx_msg3[] = "\xB5\x62\x06\x01\x03\x00\x01\x20\x01\x2C\x83"; /* CFG-MSG, send NAV-TIMEGPS every second */ len = write(fd, upx_msg1, sizeof(upx_msg1) - 1); if(len != sizeof(upx_msg1) - 1) { perror("write()"); return len; } len = write(fd, upx_msg2, sizeof(upx_msg2) - 1); if(len != sizeof(upx_msg2) - 1) { perror("write()"); return len; } #if 1 len = write(fd, upx_msg3, sizeof(upx_msg3) - 1); if(len != sizeof(upx_msg3) - 1) { perror("write()"); return len; } #endif return 0; } static int ubx_get_time(int fd) { int len; char upx_msg1[] = "\xb5\x62\x01\x20\x00\x00\x21\x64"; /* NAV-TIMEGPS */ //char upx_msg1[] = "\xb5\x62\x01\x22\x00\x00\x23\x6A"; /* NAV-CLOCK */ fd_set readset; struct timeval tv; int rc; uint8_t buf[256]; struct gps_assist_data gps; len = write(fd, upx_msg1, sizeof(upx_msg1) - 1); if(len != sizeof(upx_msg1) - 1) { perror("write()"); return len; } /* wait for reply (does it really take nearly a second ?) */ FD_ZERO(&readset); FD_SET(fd, &readset); tv.tv_sec = 0; tv.tv_usec = 1000 * 1000; rc = select(fd + 1, &readset, NULL, NULL, &tv); if(rc < 0) { if(errno != EINTR) { fprintf(stderr, "select() failed: (%d) %s\n", rc, strerror(errno)); return -1; } } if(!FD_ISSET(fd, &readset)) { fprintf(stderr, "timeout waiting for GPS time\n"); return -2; } /* read data */ len = read(fd, buf, sizeof(buf)); if(len <= 0) { fprintf(stderr, "serial read() failed: (%d) %s\n", len, strerror(errno)); return -1; } /* parse data */ memset(&gps, 0, sizeof(gps)); if(ubx_parse(&gps, buf, len) != 0) { fprintf(stderr, "ubx_parse failed\n"); return -1; } return 0; } /***********************************************************************/ void init_rrlp_states(void) { int i; memset(&g_rrlp_states, 0, sizeof(g_rrlp_states)); for(i = 0; i < N_MAX_RRLP_STATES; i++) { g_rrlp_states[i].id = SUBSC_ID_NONE; g_rrlp_states[i].state = RRLP_NONE; } } /* find record with this ID or get an unused one */ struct rrlp_sub_state *get_rrlp_subscriber(long long unsigned int id) { int i; /* search for this ID */ for(i = 0; i < N_MAX_RRLP_STATES; i++) { if(g_rrlp_states[i].id == id) return &g_rrlp_states[i]; } /* search for an unused record */ for(i = 0; i < N_MAX_RRLP_STATES; i++) { if(g_rrlp_states[i].id == SUBSC_ID_NONE) { g_rrlp_states[i].id = id; g_rrlp_states[i].time_first = time(NULL); return &g_rrlp_states[i]; } } return NULL; } void free_rrlp_subscriber(struct rrlp_sub_state *rrlp_sub_state) { memset(rrlp_sub_state, 0, sizeof(struct rrlp_sub_state)); rrlp_sub_state->id = SUBSC_ID_NONE; rrlp_sub_state->state = RRLP_NONE; } /* find expired records and free them */ #define SUBS_RECORD_LIFETIME 300 /* in seconds */ void free_expired_rrlp_subscribers(void) { int i; /* search for this ID */ for(i = 0; i < N_MAX_RRLP_STATES; i++) { if(g_rrlp_states[i].id != SUBSC_ID_NONE && time(NULL) - g_rrlp_states[i].time_first > SUBS_RECORD_LIFETIME) { free_rrlp_subscriber(&g_rrlp_states[i]); } } } #define GPS_POLL_TIME 100 /* in seconds */ #define MAX(a, b) ((a > b) ? a : b) int rrlp_serv(int fd, int fd_serial) { fd_set readset; struct timeval tv; int rc; int len; uint8_t buf[2 + 1 + 8 + MAX_RRLP_DATA]; /* len, cmd, subscriber ID, data */ uint8_t buf_reply[MAX_RRLP_DATA]; uint8_t buf_serial[1024 * 8]; /* should be large enough for the complete data set */ int len_pkt, offs, len_reply, len_data; uint8_t cmd, cmd_reply; long long unsigned int id; struct sockaddr_in from; int from_len; time_t last_gps_query = 0; time_t last_subs_expiration = time(NULL); struct rrlp_sub_state *rrlp_sub_state; memset(&g_gps, 0, sizeof(g_gps)); init_rrlp_states(); for(;;) { /* new GPS poll ? */ if(time(NULL) - last_gps_query > GPS_POLL_TIME) { printd("GPS poll\n"); ubx_get_data(fd_serial); last_gps_query = time(NULL); } /* any data from RRLP client or GPS Receicer ? */ FD_ZERO(&readset); FD_SET(fd, &readset); if(fd_serial != -1) FD_SET(fd_serial, &readset); tv.tv_sec = 1; tv.tv_usec = 0; /* this creates another UDP socket on Cygwin !? */ #if 0 rc = select(sizeof(readset) * 8, &readset, NULL, NULL, &tv); #else rc = select(MAX(fd, fd_serial) + 1, &readset, NULL, NULL, &tv); #endif if(rc < 0) { if(errno == EINTR) continue; fprintf(stderr, "select() failed: (%d) %s\n", rc, strerror(errno)); return -1; } /* data from RRLP client ? */ if(FD_ISSET(fd, &readset)) { /* read packet */ from_len = sizeof(from); len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&from, &from_len); if(len < 0) { fprintf(stderr, "recvfrom() failed: (%d) %s\n", len, strerror(errno)); return -1; } if(len < 2) { fprintf(stderr, "len < 2: %d\n", len); return -1; } len_pkt = buf[0] + (buf[1] << 8); if(len_pkt < 2 + 1 + 8) { fprintf(stderr, "len_pkt < 2 + 8 + 1: %d\n", len_pkt); return -1; } if(len != len_pkt) { /* TODO: we might have received more than one packet */ fprintf(stderr, "recvfrom: len != len_pkt: %d %d\n", len, len_pkt); return -1; } len_pkt -= 2; offs = 2; #if 0 /* dump packet */ { int i; for(i = 0; i < len_pkt; i++) printd("%02X ", buf[offs + i]); printd("\n"); } #endif cmd = buf[offs + 0]; /* get subscriber ID */ id = buf[offs + 1]; id += ((long long unsigned int)buf[offs + 2] << 8); id += ((long long unsigned int)buf[offs + 3] << 16); id += ((long long unsigned int)buf[offs + 4] << 24); id += ((long long unsigned int)buf[offs + 5] << 32); id += ((long long unsigned int)buf[offs + 6] << 40); id += ((long long unsigned int)buf[offs + 7] << 48); id += ((long long unsigned int)buf[offs + 8] << 56); printd("cmd = 0x%02X ID = %llu\n", cmd, id); len_data = len_pkt - (1 + 8); offs += (1 + 8); #if 1 /* dump data */ { int i; for(i = 0; i < len_data; i++) printd("%02X ", buf[offs + i]); printd("\n"); } #endif /* process packet */ len_reply = 0; cmd_reply = 0x00; /* search subscriber record with this ID or allocate a new one */ rrlp_sub_state = get_rrlp_subscriber(id); if(rrlp_sub_state == NULL) { fprintf(stderr, "cannot get subscriber reccord\n"); cmd_reply = RRLP_RSP_ERROR; strcpy(buf_reply, "cannot get subscriber reccord"); len_reply = strlen(buf_reply) + 1; } else { if(cmd == RRLP_CMD_MS_DATA) { if(decode_rrlp_data(rrlp_sub_state, N_MAX_AD_RECORDS, &buf[offs], len_data, &cmd_reply, buf_reply, &len_reply) != 0) { cmd_reply = RRLP_RSP_ERROR; strcpy(buf_reply, "decode_rrlp_data failed"); len_reply = strlen(buf_reply) + 1; } } else if(cmd == RRLP_CMD_MS_DATA_SLOW) { if(decode_rrlp_data(rrlp_sub_state, N_MAX_AD_RECORDS_SLOW, &buf[offs], len_data, &cmd_reply, buf_reply, &len_reply) != 0) { cmd_reply = RRLP_RSP_ERROR; strcpy(buf_reply, "decode_rrlp_data failed"); len_reply = strlen(buf_reply) + 1; } } else { cmd_reply = RRLP_RSP_ERROR; strcpy(buf_reply, "invalid command"); len_reply = strlen(buf_reply) + 1; } } /* send reply, build packet */ len_pkt = 2 + 1 + len_reply; buf[0] = len_pkt & 0xFF; buf[1] = (len_pkt >> 8) & 0xFF; buf[2] = cmd_reply; /* cmd */ /* data */ memcpy(&buf[3], buf_reply, len_reply); len = sendto(fd, buf, len_pkt, 0, (struct sockaddr*)&from, sizeof(from)); if(len < 0) { fprintf(stderr, "sendto() failed: (%d) %s\n", len, strerror(errno)); return -1; } if(len != len_pkt) { fprintf(stderr, "sendto: len != len_pkt: %d %d\n", len, len_pkt); return -1; } } /* data from the GPS receiver ? */ if(fd_serial != -1 && FD_ISSET(fd_serial, &readset)) { int buf_offset = 0; int total_len = 0; struct gps_assist_data gps; for(;;) { len = read(fd_serial, buf_serial + buf_offset, sizeof(buf_serial) - buf_offset); if(len <= 0) { fprintf(stderr, "serial read() failed: (%d) %s\n", len, strerror(errno)); break; } prints("serial data: %d\n", len); total_len += len; buf_offset += len; if(buf_offset >= sizeof(buf_serial)) { fprintf(stderr, "serial buffer full\n"); break; } /* check for more data */ FD_ZERO(&readset); FD_SET(fd_serial, &readset); tv.tv_sec = 0; tv.tv_usec = 300 * 1000; rc = select(fd_serial + 1, &readset, NULL, NULL, &tv); if(rc < 0) { if(errno != EINTR) { fprintf(stderr, "select() failed: (%d) %s\n", rc, strerror(errno)); break; } } if(!FD_ISSET(fd_serial, &readset)) break; } prints("total_len: %d\n", total_len); #if 0 /* dump hex data */ { int i; for(i = 0; i < total_len; i++) prints("%02X ", buf_serial[i]); prints("\n"); } #endif #if 0 /* dump ASCII data */ { int i; for(i = 0; i < total_len; i++) prints("%c", buf_serial[i]); } #endif memset(&gps, 0, sizeof(gps)); if(ubx_parse(&gps, buf_serial, total_len) != 0) { fprintf(stderr, "ubx_parse failed\n"); } else { if(gps.fields == GPS_FIELD_REFTIME) { g_gps.ref_time = gps.ref_time; } else if (gps.fields != 0) { g_gps = gps; } } #if 0 /* for time testing */ close(fd_serial); fd_serial = -1; #endif } /* expire subscribers ? */ if(time(NULL) - last_subs_expiration > SUBS_RECORD_LIFETIME) { printd("Subscriber expiration\n"); free_expired_rrlp_subscribers(); last_subs_expiration = time(NULL); } #if 0 /* for testing only */ ubx_get_time(fd_serial); #endif } return 0; } static int fd = -1; static int fd_serial = -1; static void signal_handler(int signal) { int rc; fprintf(stdout, "signal %u received\n", signal); switch (signal) { case SIGINT: if(fd != -1) { printd("close\n"); rc = close(fd); if(rc != 0) { fprintf(stderr, "close() failed: (%d) %s\n", rc, strerror(errno)); } } if(fd_serial != -1) { printd("close serial\n"); rc = close(fd_serial); if(rc != 0) { fprintf(stderr, "close() failed: (%d) %s\n", rc, strerror(errno)); } } exit(0); break; default: break; } } int main(int argc, char *argv[]) { int rc; struct sockaddr_in sa; int on; printf("RRLP Server\n"); if(argc != 3) { fprintf(stderr, "usage: rrlp-serv .\n"); return -1; } fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(fd < 0) { fprintf(stderr, "socket() failed: (%d) %s\n", fd, strerror(errno)); return -1; } /* This is for Cygwin/Windows, for Linux take the interface name */ sa.sin_family = AF_INET; sa.sin_port = htons(RRLP_SERV_PORT); sa.sin_addr.s_addr = INADDR_ANY; if(inet_aton(argv[1], &sa.sin_addr) != 1) { fprintf(stderr, "inet_aton() failed: %s\n", strerror(errno)); close(fd); return -1; } if(sa.sin_addr.s_addr == INADDR_NONE) { fprintf(stderr, "Invalid local address\n"); close(fd); return -1; } on = 1; rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if(rc != 0) { fprintf(stderr, "setsockopt() failed: (%d) %s\n", rc, strerror(errno)); } rc = bind(fd, (struct sockaddr *)&sa, sizeof(sa)); if(rc < 0) { fprintf(stderr, "bind() failed: (%d) %s\n", rc, strerror(errno)); close(fd); return -1; } /* serial port for GPS receiver */ fd_serial= serial_init(argv[2], GPS_BAUDRATE); if(fd_serial < 0) { fprintf(stderr, "serial_init failed: (%d) %s\n", fd_serial, strerror(errno)); close(fd); return -1; } set_ubx_protocol(fd_serial); signal(SIGINT, &signal_handler); signal(SIGABRT, &signal_handler); signal(SIGUSR1, &signal_handler); printf("Waiting for incoming data....\n"); rrlp_serv(fd, fd_serial); rc = close(fd); if(rc != 0) { fprintf(stderr, "close() failed: (%d) %s\n", rc, strerror(errno)); } fd = -1; rc = close(fd_serial); if(rc != 0) { fprintf(stderr, "close() serial failed: (%d) %s\n", rc, strerror(errno)); } fd_serial = -1; return 0; }