#include #include #include #include #include #include /* * GNU C library includes getopt_long() */ #ifdef __GNU_LIBRARY__ #include #endif #include #include void MatrixToEuler(VMatrix * mt, double *heading, double *pitch, double *roll); void BuildEulerMatrix(double roll, double pitch, double heading, VMatrix * m); void GenerateWorldToLocalMatrix(WorldCoordinates * w, VMatrix * m); dis_timestamp TimeDoubleToDIS(double time, int reference); void transpose(VMatrix * m, VMatrix * r); static struct itimerval it, ot; #define FEETtoMETERS(x) ((x) * 0.3048) void timer_alarm (int val) { } #define OPT_SITE 1 #define OPT_APPL 2 #define OPT_ENTITY 3 #define OPT_LATITUDE 4 #define OPT_LONGITUDE 5 #define OPT_ALTITUDE 6 #define OPT_ETYPE 7 #ifdef __GNU_LIBRARY__ /* * long command line options */ static struct option long_options[] = { { "site-id", 1, 0, 's' }, { "appl-id", 1, 0, 'a' }, { "entity-id", 1, 0, 'e' }, { "latitude", 1, 0, 'l' }, { "longitude", 1, 0, 'L' }, { "altitude", 1, 0, 'A' }, { "entity-type", 1, 0, 't' }, { "exercise-id", 1, 0, 'E' }, { "force-id", 1, 0, 'f' }, }; #endif int main(int argc, char **argv) { struct sigaction sa, old_sa; struct timeval curtime; int status; pid_t pid; int c; int exercise_id = 1; double unix_time_sec; int option_index = 0; VMatrix XYZtoNED, NEDtoXYZ, trihedral, ABCtoXYZ; double orientation[3]; VPoint velocity = { 0, 0, 0 }; dis_pdu estate, em; dis_entity_state_pdu *esPDU; DISxApplicationInfo * app; WorldCoordinates pos; memset (&estate, 0, sizeof(dis_pdu)); memset (&em, 0, sizeof(dis_pdu)); /* * Fill out PDUs */ esPDU = (dis_entity_state_pdu *) &estate; esPDU->id.sim_id.site_id = 1; esPDU->id.sim_id.application_id = 1; esPDU->id.entity_id = 1; esPDU->force_id = 1; esPDU->hdr.protocol_version; esPDU->hdr.exercise_id = 1; esPDU->hdr.pdu_type = PDUTypeEntityState; esPDU->hdr.protocol_family; esPDU->hdr.time_stamp; esPDU->hdr.length; esPDU->hdr.padding; /* * SA-13 SAM launcher */ esPDU->type.kind = 1; esPDU->type.domain = 1; esPDU->type.country = 222; esPDU->type.category = 4; esPDU->type.subcategory = 21; esPDU->type.specific = 1; /* * process command line arguments */ while (1) { #ifdef __GNU_LIBRARY__ c = getopt_long ( argc, argv, "f:a:s:bc:d:012", long_options, &option_index ); #else c = getopt ( argc, argv, "f:a:s:bc:d:" ); #endif if (c == -1) { break; } switch (c) { case 'f': estate.entity_state.force_id = atoi(optarg); break; case 'a': estate.entity_state.id.sim_id.application_id = atoi(optarg); break; case 's': estate.entity_state.id.sim_id.site_id = atoi(optarg); break; case 'e': estate.entity_state.id.entity_id = atoi(optarg); break; case 'E': exercise_id = atoi(optarg); break; case 'l': pos.latitude = DEGtoRAD(atof(optarg)); break; case 'L': pos.longitude = DEGtoRAD(atof(optarg)); break; case 'A': pos.z = FEETtoMETERS(atof(optarg)); break; case 't': break; } } memset ( &sa, 0, sizeof(sa) ); sa.sa_handler = timer_alarm; #ifdef linux sa.sa_flags = SA_NOMASK; #endif it.it_interval.tv_sec = 3; it.it_interval.tv_usec = 0; it.it_value.tv_sec = 3; it.it_value.tv_usec = 0; if (sigaction( SIGALRM, &sa, &old_sa )) { perror( "sigaction failed" ); exit ( 1 ); } if (setitimer( ITIMER_REAL, &it, &ot )) { perror ( "setitimer failed" ); exit ( 1 ); } app = DISxInitializeApplication( 1, estate.entity_state.id.sim_id.site_id, estate.entity_state.id.sim_id.application_id ); /* * loop until interrupted ... */ while (1) { gettimeofday( &curtime, NULL ); unix_time_sec = (double) curtime.tv_sec + (double) curtime.tv_usec / 1000000.0; esPDU->hdr.time_stamp = TimeDoubleToDIS( unix_time_sec, 1 ); /* esPDU->id already set */ esPDU->force_id = DISForceOpposing; esPDU->art_parm_count = 0; DISWorldCoordinatesToGeocentric( &pos, &esPDU->pos ); BuildEulerMatrix (0.0, 0.0, 0.0, &trihedral); GenerateWorldToLocalMatrix ( &pos, &XYZtoNED ); /* * Derive ECI [Geocentric] heading, pitch, roll */ transpose(&XYZtoNED, &NEDtoXYZ); /* the trihedral is an "ABCtoNED" transformation */ VMatrixMultByRank(&trihedral, &NEDtoXYZ, &ABCtoXYZ, 3); MatrixToEuler(&ABCtoXYZ, &orientation[0], &orientation[1], &orientation[2]); esPDU->vel.x = velocity.x; esPDU->vel.y = velocity.y; esPDU->vel.z = velocity.z; esPDU->orientation.psi = orientation[0]; esPDU->orientation.theta = orientation[1]; esPDU->orientation.phi = orientation[2]; esPDU->appearance = ( DISAppearancePaintUniform | DISAppearanceFirepowerNormal | DISAppearanceHatchClosed | DISAppearanceLandLauncherRaised ); esPDU->dr_parm.algorithm = DISDRMethodRVW; esPDU->dr_parm.linear_acc.x = 0.0f; esPDU->dr_parm.linear_acc.y = 0.0f; esPDU->dr_parm.linear_acc.z = 0.0f; esPDU->dr_parm.angular_vel.x = 0.0f; esPDU->dr_parm.angular_vel.y = 0.0f; esPDU->dr_parm.angular_vel.z = 0.0f; esPDU->marking.charset = DISCharSetASCII; memset(esPDU->marking.marking, 0, sizeof(esPDU->marking.marking)); strcpy(esPDU->marking.marking, "HI THERE"); esPDU->capabilities = 0; esPDU->art_parm = NULL; if (DISxWritePDU(app, (dis_pdu *) esPDU) != 0) { printf ( "error writing PDU\n" ); } else { printf ( "." ); fflush ( stdout ); } pause(); } } /* * Generate a transform matrix to get from geocentric to local NED coordinates */ void GenerateWorldToLocalMatrix(WorldCoordinates * w, VMatrix * m) { dis_world_coordinates gc; VPoint p; VIdentMatrix(m); VRotate(m, ZRotation, -w->longitude); VRotate(m, YRotation, -w->latitude); VRotate(m, YRotation, -DEGtoRAD(90.0)); DISWorldCoordinatesToGeocentric(w, &gc); VTransform((VPoint *) & gc, m, &p); m->m[0][3] = -p.x; m->m[1][3] = -p.y; m->m[2][3] = -p.z; } void BuildEulerMatrix(double roll, double pitch, double heading, VMatrix * m) { register double sinPhi, cosPhi, sinTheta, cosTheta, sinPsi, cosPsi; sinPhi = sin(roll); cosPhi = cos(roll); sinTheta = sin(pitch); cosTheta = cos(pitch); sinPsi = sin(heading); cosPsi = cos(heading); m->m[0][0] = cosTheta * cosPsi; m->m[0][1] = sinPhi * sinTheta * cosPsi - cosPhi * sinPsi; m->m[0][2] = cosPhi * sinTheta * cosPsi + sinPhi * sinPsi; m->m[1][0] = cosTheta * sinPsi; m->m[1][1] = sinPhi * sinTheta * sinPsi + cosPhi * cosPsi; m->m[1][2] = cosPhi * sinTheta * sinPsi - sinPhi * cosPsi; m->m[2][0] = -sinTheta; m->m[2][1] = sinPhi * cosTheta; m->m[2][2] = cosPhi * cosTheta; m->m[0][3] = m->m[1][3] = m->m[2][3] = 0.0; m->m[3][0] = m->m[3][1] = m->m[3][2] = 0.0; m->m[3][3] = 1.0; } /* * Convert a transformation matrix into the equivalent * heading, pitch and roll angles. */ #define EPSILON 1.0e-6 void MatrixToEuler(VMatrix * mt, double *heading, double *pitch, double *roll) { double sin_theta; sin_theta = -mt->m[2][0]; if (fabs(sin_theta) > 1.0 - EPSILON) { /* we have the nose pointing very close to straight up or straight down, set roll to zero and compute the resulting heading */ *heading = atan2(-mt->m[0][1], mt->m[1][1]); if (*heading < 0.0) *heading += 2.0 * M_PI; if (sin_theta > 0.0) *pitch = M_PI / 2.0; else *pitch = -M_PI / 2.0; *roll = 0.0; } else { *heading = atan2(mt->m[1][0], mt->m[0][0]); if (*heading < 0.0) *heading += 2.0 * M_PI; *pitch = asin(sin_theta); *roll = atan2(mt->m[2][1], mt->m[2][2]); } } /* * T i m e D o u b l e T o D I S * * Convert a double in UNIX format (seconds since 1970) to a DIS timestamp. * If reference is 0, the time will be marked relative. * If reference is 1, the time will be marked absolute, i.e. true UTC time. */ dis_timestamp TimeDoubleToDIS(double time, int reference) { unsigned long tmp; dis_timestamp res; tmp = (unsigned long) (fmod(time, 3600.0)); if (tmp > 2147483647L) /* 2^31 - 1 */ res.time = 2147483647L; else res.time = tmp; res.type = reference; return res; } void transpose(VMatrix * m, VMatrix * r) { int i, j; for (i = 0; i < 4; ++i) for (j = 0; j < 4; ++j) r->m[i][j] = m->m[j][i]; }