From 6cce17626efe1cdecef280efb615b0639640aad8 Mon Sep 17 00:00:00 2001
From: Timothy Stack <stack@flux.utah.edu>
Date: Thu, 31 Mar 2005 20:43:24 +0000
Subject: [PATCH] Pass camera local to world offsets through vmcd.

	* event/sched/rpc.cc: Add the camera fixed_x,fixed_y values to the
	emcd.config file.

	* robots/emc/emcd.c: Pickup the cameras fixed_x,fixed_y values
	from the config file.  Add supports for setting the speed of the
	robot.

	* robots/mtp/mtp.h, robots/mtp/mtp.c: Add support for setting
	speed in GOTO packets and constructing vmc-client config packets.

	* robots/mtp/mtp.x: Add a vmc-client config packet that contains
	the cameras origin in world coordinates.

	* robots/mtp/mtp_dump.c: Dump the min/max values for x and y,
	handy for figuring out the bounds of the camera.

	* robots/vmcd/visionTrack.c, robots/vmcd/visionTrack.h: Handle
	cameras that are overlapping on two axes.

	* robots/vmcd/vmc-client.c: Accept a vmc-client config packet that
	tells us where the camera is in world coordinates.  Add an
	orientation flag that specifies how the camera is oriented wrt to
	the world coordinate system.

	* robots/vmcd/vmcd.c: Send vmc-client config packets out.  Fix a
	bug that left unknown tracks in the previous frame, which could
	confuse the matching algorithm causing it to match unknown tracks
	instead of valid known tracks.
---
 event/sched/rpc.cc        |   9 +-
 robots/emc/emcd.c         |  26 ++-
 robots/mtp/mtp.c          |  50 +++++-
 robots/mtp/mtp.h          |   1 +
 robots/mtp/mtp.x          |  14 ++
 robots/vmcd/visionTrack.c | 323 ++++++++++++++------------------------
 robots/vmcd/visionTrack.h |  59 ++++++-
 robots/vmcd/vmc-client.c  |  92 +++++++----
 robots/vmcd/vmcd.c        |  43 +++--
 9 files changed, 346 insertions(+), 271 deletions(-)

diff --git a/event/sched/rpc.cc b/event/sched/rpc.cc
index fefa85be02..65e4fd40f0 100644
--- a/event/sched/rpc.cc
+++ b/event/sched/rpc.cc
@@ -375,14 +375,16 @@ RPC_cameralist(FILE *emcd_config, char *area)
 		tmp = attr.getMember("hostname");
 		port = ((ulxr::Integer)attr.getMember("port")).getInteger();
 		fprintf(emcd_config,
-			"camera %s %s %d %f %f %f %f\n",
+			"camera %s %s %d %f %f %f %f %f %f\n",
 			area,
 			tmp.getString().c_str(),
 			port,
 			((ulxr::Double)attr.getMember("loc_x")).getDouble(),
 			((ulxr::Double)attr.getMember("loc_y")).getDouble(),
 			((ulxr::Double)attr.getMember("width")).getDouble(),
-			((ulxr::Double)attr.getMember("height")).getDouble());
+			((ulxr::Double)attr.getMember("height")).getDouble(),
+			((ulxr::Double)attr.getMember("fixed_x")).getDouble(),
+			((ulxr::Double)attr.getMember("fixed_y")).getDouble());
 	}
 	
 	return 0;
@@ -459,7 +461,8 @@ RPC_waitforrobots(event_handle_t handle, char *pid, char *eid)
 	info("info: waiting for robots\n");
 	
 	if ((emcd_config = fopen("tbdata/emcd.config", "w")) == NULL) {
-		fprintf(stderr, "Cannot create emcd.config!\n");
+		error("Cannot create emcd.config!\n");
+		return -1;
 	}
 	
 	for (lpc = 0; lpc < locs.size(); lpc++) {
diff --git a/robots/emc/emcd.c b/robots/emc/emcd.c
index 68f3c8ac93..f05155ec8a 100755
--- a/robots/emc/emcd.c
+++ b/robots/emc/emcd.c
@@ -75,7 +75,6 @@ static char *progname;
 static int debug;
 
 static int port = EMC_SERVER_PORT;
-static int command_seq_no = 0;
 
 /* initial data pulled from the config file */
 /* it's somewhat non-intuitive to use lists here -- since we're doing maps,
@@ -202,7 +201,6 @@ static void dump_info(void)
     while ((mup = (struct mtp_update_position *)
 	    robot_list_enum_next_element(e)) != NULL) {
       struct emc_robot_config *erc;
-      struct mtp_packet mp;
       
       erc = robot_list_search(hostname_list, mup->robot_id);
       info("  %s: %.2f %.2f %.2f\n",
@@ -631,11 +629,13 @@ void parse_config_file(char *config_file) {
     }
     else if (strcmp(directive, "camera") == 0) {
       char area[32], hostname[MAXHOSTNAMELEN];
-      float x, y, width, height;
+      float x, y, width, height, fixed_x, fixed_y;
       int port;
 
       if (sscanf(line,
-		 "%16s %32s %" __XSTRING(MAXHOSTNAMELEN) "s %d %f %f %f %f",
+		 "%16s %32s %"
+		 __XSTRING(MAXHOSTNAMELEN)
+		 "s %d %f %f %f %f %f %f",
 		 directive,
 		 area,
 		 hostname,
@@ -643,7 +643,9 @@ void parse_config_file(char *config_file) {
 		 &x,
 		 &y,
 		 &width,
-		 &height) != 8) {
+		 &height,
+		 &fixed_x,
+		 &fixed_y) != 10) {
 	fprintf(stderr,
 		"error:%d: syntax error in config file - %s\n",
 		line_no,
@@ -663,6 +665,8 @@ void parse_config_file(char *config_file) {
 	cc[cc_size].y = y;
 	cc[cc_size].width = width;
 	cc[cc_size].height = height;
+	cc[cc_size].fixed_x = fixed_x;
+	cc[cc_size].fixed_y = fixed_y;
 	cc_size += 1;
       }
     }
@@ -873,8 +877,8 @@ void ev_callback(event_handle_t handle,
     error("no match for host\n");
   }
   else {
+    float x, y, orientation = 0.0, speed = 0.1;
     char *value, args[BUFSIZ];
-    float x, y, orientation;
     struct mtp_packet mp;
     
     event_notification_get_arguments(handle, notification, args, sizeof(args));
@@ -901,6 +905,13 @@ void ev_callback(event_handle_t handle,
       }
     }
     
+    if (event_arg_get(args, "SPEED", &value) > 0) {
+      if (sscanf(value, "%f", &speed) != 1) {
+	error("SPEED argument in event is not a float: %s\n", value);
+	return;
+      }
+    }
+    
     event_notification_get_int32(handle, notification,
 				 "TOKEN", (int32_t *)&match->token);
 
@@ -922,6 +933,7 @@ void ev_callback(event_handle_t handle,
 			MA_X, x,
 			MA_Y, y,
 			MA_Theta, orientation,
+			MA_Speed, speed,
 			MA_TAG_DONE);
 
 	if (rmc_data.handle != NULL) {
@@ -1559,7 +1571,7 @@ int vmc_callback(elvin_io_handler_t handler,
         double best_pose_delta = POSE_THRESHOLD;
         int robot_id = -1;
 
-        double dx,dy,dtheta,ddist;
+        double dx = 0.0, dy = 0.0, dtheta, ddist;
 
 	struct mtp_packet mp_reply;
 
diff --git a/robots/mtp/mtp.c b/robots/mtp/mtp.c
index 025116d266..e9a5f5cbb3 100755
--- a/robots/mtp/mtp.c
+++ b/robots/mtp/mtp.c
@@ -474,6 +474,10 @@ mtp_error_t mtp_init_packet(struct mtp_packet *mp, mtp_tag_t tag, ...)
 		mp->data.mtp_payload_u.command_goto.position.x =
 		    va_arg(args, double);
 		break;
+	    case MTP_CONFIG_VMC_CLIENT:
+		mp->data.mtp_payload_u.config_vmc_client.fixed_x =
+		    va_arg(args, double);
+		break;
 	    default:
 		assert(0);
 		break;
@@ -493,6 +497,10 @@ mtp_error_t mtp_init_packet(struct mtp_packet *mp, mtp_tag_t tag, ...)
 		mp->data.mtp_payload_u.command_goto.position.y =
 		    va_arg(args, double);
 		break;
+	    case MTP_CONFIG_VMC_CLIENT:
+		mp->data.mtp_payload_u.config_vmc_client.fixed_y =
+		    va_arg(args, double);
+		break;
 	    default:
 		assert(0);
 		break;
@@ -591,6 +599,17 @@ mtp_error_t mtp_init_packet(struct mtp_packet *mp, mtp_tag_t tag, ...)
 		mp->data.mtp_payload_u.contact_report.points[lpc] = cp[lpc];
 	    }
 	    break;
+	case MA_Speed:
+	    switch (mp->data.opcode) {
+	    case MTP_COMMAND_GOTO:
+		mp->data.mtp_payload_u.command_goto.speed =
+		    va_arg(args, double);
+		break;
+	    default:
+		assert(0);
+		break;
+	    }
+	    break;
 	}
 	
 	tag = va_arg(args, mtp_tag_t);
@@ -905,13 +924,28 @@ void mtp_print_packet(FILE *file, struct mtp_packet *mp)
 	    struct mtp_config_vmc *mcv = &mp->data.mtp_payload_u.config_vmc;
 	    
 	    fprintf(file,
-		    "  camera[%d]:\t%s:%d\n",
+		    "  camera[%d]:\t%s:%d %.2f %.2f %.2f %.2f -- %.2f %.2f\n",
 		    lpc,
 		    mcv->cameras.cameras_val[lpc].hostname,
-		    mcv->cameras.cameras_val[lpc].port);
+		    mcv->cameras.cameras_val[lpc].port,
+		    mcv->cameras.cameras_val[lpc].x,
+		    mcv->cameras.cameras_val[lpc].y,
+		    mcv->cameras.cameras_val[lpc].width,
+		    mcv->cameras.cameras_val[lpc].height,
+		    mcv->cameras.cameras_val[lpc].fixed_x,
+		    mcv->cameras.cameras_val[lpc].fixed_y);
 	}
 	break;
     
+    case MTP_CONFIG_VMC_CLIENT:
+	fprintf(file,
+		" opcode:\tconfig-vmc-client\n"
+		"  fixed_x:\t\t%.2f\n"
+		"  fixed_y:\t\t%.2f\n",
+		mp->data.mtp_payload_u.config_vmc_client.fixed_x,
+		mp->data.mtp_payload_u.config_vmc_client.fixed_y);
+	break;
+    
     case MTP_CONFIG_RMC:
 	fprintf(file,
 		" opcode:\tconfig-rmc\n"
@@ -1019,13 +1053,15 @@ void mtp_print_packet(FILE *file, struct mtp_packet *mp)
 		"  x:\t\t%f\n"
 		"  y:\t\t%f\n"
 		"  theta:\t%f\n"
-		"  timestamp:\t%f\n",
+		"  timestamp:\t%f\n"
+		"  speed:\t%.2f\n",
 		mp->data.mtp_payload_u.command_goto.command_id,
 		mp->data.mtp_payload_u.command_goto.robot_id,
 		mp->data.mtp_payload_u.command_goto.position.x,
 		mp->data.mtp_payload_u.command_goto.position.y,
 		mp->data.mtp_payload_u.command_goto.position.theta,
-		mp->data.mtp_payload_u.command_goto.position.timestamp);
+		mp->data.mtp_payload_u.command_goto.position.timestamp,
+		mp->data.mtp_payload_u.command_goto.speed);
 	break;
     
     case MTP_COMMAND_STOP:
@@ -1070,7 +1106,8 @@ void mtp_print_packet(FILE *file, struct mtp_packet *mp)
 		    "  speed:\t\t%f\n"
 		    "  status:\t\t%d\n"
 		    "  user_button:\t\t%d\n"
-		    "  user_led:\t\t%d\n",
+		    "  user_led:\t\t%d\n"
+		    "  stall_contact:\t\t%d\n",
 		    mgt->battery_level,
 		    mgt->battery_voltage,
 		    mgt->battery_misses,
@@ -1096,7 +1133,8 @@ void mtp_print_packet(FILE *file, struct mtp_packet *mp)
 		    mgt->speed,
 		    mgt->status,
 		    mgt->user_button,
-		    mgt->user_led);
+		    mgt->user_led,
+		    mgt->stall_contact);
 	    break;
 	}
 	break;
diff --git a/robots/mtp/mtp.h b/robots/mtp/mtp.h
index f20e5492ca..95d234e148 100755
--- a/robots/mtp/mtp.h
+++ b/robots/mtp/mtp.h
@@ -149,6 +149,7 @@ typedef enum {
     MA_WiggleType,	 /*< (mtp_wiggle_t) */
     MA_ContactPointCount,	 /*< (int) */
     MA_ContactPoints,	 /*< (contact_point *) */
+    MA_Speed,		 /*< (float) */
 
     MA_TAG_MAX
 } mtp_tag_t;
diff --git a/robots/mtp/mtp.x b/robots/mtp/mtp.x
index 8febc49ad7..a7d57dd7eb 100644
--- a/robots/mtp/mtp.x
+++ b/robots/mtp/mtp.x
@@ -64,6 +64,11 @@ enum mtp_opcode_t {
      */
     MTP_CONFIG_RMC		= 21,
 
+    /**
+     * VMC client configuration packet.
+     */
+    MTP_CONFIG_VMC_CLIENT	= 22,
+
     
     /**
      * Request the current position of a given robot.
@@ -172,6 +177,8 @@ struct camera_config {
     float y;
     float width;
     float height;
+    float fixed_x;
+    float fixed_y;
 };
 
 struct obstacle_config {
@@ -202,6 +209,11 @@ struct mtp_config_vmc {
     camera_config cameras<>;
 };
 
+struct mtp_config_vmc_client {
+    float fixed_x;
+    float fixed_y;
+};
+
 struct mtp_request_position {
     int robot_id;
 };
@@ -234,6 +246,7 @@ struct mtp_command_goto {
     int command_id;
     int robot_id;
     robot_position position;
+    float speed;
 };
 
 struct mtp_command_stop {
@@ -324,6 +337,7 @@ union mtp_payload switch (mtp_opcode_t opcode) {
  case MTP_CONTROL_CLOSE:	mtp_control		close;
  case MTP_CONFIG_RMC:		mtp_config_rmc		config_rmc;
  case MTP_CONFIG_VMC:		mtp_config_vmc		config_vmc;
+ case MTP_CONFIG_VMC_CLIENT:	mtp_config_vmc_client	config_vmc_client;
  case MTP_REQUEST_POSITION:	mtp_request_position	request_position;
  case MTP_REQUEST_ID:		mtp_request_id		request_id;
  case MTP_UPDATE_POSITION:	mtp_update_position	update_position;
diff --git a/robots/vmcd/visionTrack.c b/robots/vmcd/visionTrack.c
index 79b45fddd9..281134f81c 100644
--- a/robots/vmcd/visionTrack.c
+++ b/robots/vmcd/visionTrack.c
@@ -1,3 +1,8 @@
+/*
+ * EMULAB-COPYRIGHT
+ * Copyright (c) 2005 University of Utah and the Flux Group.
+ * All rights reserved.
+ */
 
 #include "config.h"
 
@@ -26,6 +31,7 @@ static float curvy(float x)
  * will continue the search until it reaches the end of the list.
  * @param distance_out Reference to a float where the minimum distance found
  * should be stored.
+ * @return The closest track found to the given track.
  */
 static struct vision_track *vtFindMin(struct vision_track *vt,
 				      struct vision_track *curr,
@@ -46,14 +52,6 @@ static struct vision_track *vtFindMin(struct vision_track *vt,
 
 	distance = hypotf(vt->vt_position.x - curr->vt_position.x,
 			  vt->vt_position.y - curr->vt_position.y);
-#if 0
-	printf("  min %f %f - %f %f = %f\n",
-	       vt->vt_position.x,
-	       vt->vt_position.y,
-	       curr->vt_position.x,
-	       curr->vt_position.y,
-	       distance);
-#endif
 	if (distance < *distance_out) {
 	    retval = curr;
 	    *distance_out = distance;
@@ -61,32 +59,86 @@ static struct vision_track *vtFindMin(struct vision_track *vt,
 	
 	curr = (struct vision_track *)curr->vt_link.ln_Succ;
     }
+
+    assert((retval == NULL) || (*distance_out != FLT_MAX));
+    
+    return retval;
+}
+
+/**
+ * Extract tracks from a frame that are near the given track.
+ *
+ * @param list_out The list of tracks found to be within the distance
+ * tolerance.
+ * @param vt The track to compare other tracks against.
+ * @param curr The track in a list where the search should start.  The function
+ * will continue the search until it reaches the end of the list.
+ * @param tolerance The maximum distance that other tracks have to be within
+ * before they are considered "near."
+ * @return The number of tracks in "list_out."
+ */
+static int vtExtractNear(struct lnMinList *list_out,
+			 struct vision_track *vt,
+			 struct vision_track *curr,
+			 float tolerance)
+{
+    int retval = 0;
     
+    assert(list_out != NULL);
+    assert(vt != NULL);
+    assert(tolerance > 0.0);
+
+    if (curr == NULL)
+	return retval;
+
+    while (curr->vt_link.ln_Succ != NULL) {
+	struct vision_track *vt_next;
+	float distance;
+	
+	vt_next = (struct vision_track *)curr->vt_link.ln_Succ;
+
+	distance = hypotf(vt->vt_position.x - curr->vt_position.x,
+			  vt->vt_position.y - curr->vt_position.y);
+	if (distance <= tolerance) {
+	    lnRemove(&curr->vt_link);
+	    lnAddTail(list_out, &curr->vt_link);
+	    retval += 1;
+	}
+	
+	curr = vt_next;
+    }
+
+    assert(lnEmptyList(list_out) || (retval > 0));
+
     return retval;
 }
 
+/**
+ * Scan a frame for the next set of tracks that are in a different camera.
+ *
+ * @param vt The track in a list where the search should start.
+ * @return The first vision track in the remainder of the list that was
+ * detected by a different camera than the given track or NULL if there are no
+ * more tracks in different cameras.
+ */
 static struct vision_track *vtNextCamera(struct vision_track *vt)
 {
     struct vision_track *retval = NULL;
     struct vmc_client *vc;
 
+    assert(vt != NULL);
+
     vc = vt->vt_client;
-    while (vt->vt_link.ln_Succ != NULL && vt->vt_client == vc) {
+    while ((vt->vt_link.ln_Succ != NULL) && (vt->vt_client == vc)) {
 	vt = (struct vision_track *)vt->vt_link.ln_Succ;
     }
 
-    if (vt->vt_link.ln_Succ != NULL) {
+    if (vt->vt_link.ln_Succ != NULL)
 	retval = vt;
-    }
 
     return retval;
 }
 
-#ifdef RECORD_FRAMES
-static FILE *xdat_file, *ydat_file, *tdat_file;
-static unsigned int frame_count;
-#endif
-
 int vtUpdate(struct lnMinList *now,
 	     struct vmc_client *vc,
 	     struct mtp_packet *mp,
@@ -97,21 +149,8 @@ int vtUpdate(struct lnMinList *now,
     assert(now != NULL);
     assert(mp != NULL);
 
-#ifdef RECORD_FRAMES
-    if (xdat_file == NULL) {
-	xdat_file = fopen("xframes.dat", "w");
-	ydat_file = fopen("yframes.dat", "w");
-	tdat_file = fopen("tframes.dat", "w");
-    }
-#endif
-
     switch (mp->data.opcode) {
     case MTP_CONTROL_ERROR:
-#if 0
-	fprintf(dat_file,
-		"# vc %d -- empty\n",
-		vc->vc_port);
-#endif
 	retval = 1;
 	break;
     case MTP_UPDATE_POSITION:
@@ -129,7 +168,6 @@ int vtUpdate(struct lnMinList *now,
 	    vt->vt_userdata = NULL;
 	    lnAddTail(now, &vt->vt_link);
 
-#if 1
 	    /* Adjust the cameras viewable area based on this track. */
 	    if (mup->position.x < vc->vc_left)
 		vc->vc_left = mup->position.x;
@@ -139,15 +177,7 @@ int vtUpdate(struct lnMinList *now,
 		vc->vc_top = mup->position.y;
 	    if (mup->position.y > vc->vc_bottom)
 		vc->vc_bottom = mup->position.y;
-#endif
 
-#if 0
-	    fprintf(dat_file,
-		    "# vc %d -- %.2f %.2f\n",
-		    vc->vc_port,
-		    mup->position.x,
-		    mup->position.y);
-#endif
 	    retval = (mup->status == MTP_POSITION_STATUS_CYCLE_COMPLETE);
 	}
 	break;
@@ -169,13 +199,13 @@ void vtAgeTracks(struct lnMinList *now,
     assert(old != NULL);
 
     while ((vt = (struct vision_track *)lnRemHead(old)) != NULL) {
-	if (vt->vt_age < 5) {
+	if (vt->vt_age < MAX_TRACK_AGE) {
 	    float distance;
 	    
 	    if ((vtFindMin(vt,
 			   (struct vision_track *)now->lh_Head,
 			   &distance) == NULL) ||
-		(distance > 0.02)) {
+		(distance > MERGE_TOLERANCE)) {
 		vt->vt_age += 1;
 		lnAddHead(now, &vt->vt_link);
 		vt = NULL;
@@ -219,12 +249,6 @@ void vtCoalesce(struct lnMinList *extra,
     assert(vc != NULL);
     assert(vc_len > 0);
 
-    // fprintf(dat_file, "# coalesce -- %d\n", lnEmptyList(now));
-
-#ifdef RECORD_FRAMES
-    frame_count += 1;
-#endif
-    
     vt = (struct vision_track *)now->lh_Head;
     while (vt->vt_link.ln_Succ != NULL) {
 	int in_camera_count = 0, lpc;
@@ -234,15 +258,6 @@ void vtCoalesce(struct lnMinList *extra,
 	 * can coalesce them.
 	 */
 	for (lpc = 0; lpc < vc_len; lpc++) {
-#if 0
-	    printf("vc2 %f %f -- %f %f %f %f\n",
-		   vt->vt_position.x,
-		   vt->vt_position.y,
-		   vc[lpc].vc_left,
-		   vc[lpc].vc_right,
-		   vc[lpc].vc_top,
-		   vc[lpc].vc_bottom);
-#endif
 	    if ((vt->vt_position.x >= vc[lpc].vc_left) &&
 		(vt->vt_position.x <= vc[lpc].vc_right) &&
 		(vt->vt_position.y >= vc[lpc].vc_top) &&
@@ -276,161 +291,45 @@ void vtCoalesce(struct lnMinList *extra,
 	 */
 
 	if (in_camera_count > 1) {
-	    struct vision_track *vt_extra;
-	    float distance;
-
-	    while ((in_camera_count > 1) &&
-		   ((vt_extra = vtFindMin(vt,
-					  (struct vision_track *)
-					  vtNextCamera(vt),
-					  &distance)) != NULL) &&
-		   (distance < 0.35)) {
-		float curcam_dx, nextcam_dx, overlap, curweight, nextweight;
-		struct robot_position rp;
+	    struct lnMinList near;
+	    int rc;
+	    
+	    lnNewList(&near);
+	    if ((rc = vtExtractNear(&near,
+				    vt,
+				    vtNextCamera(vt),
+				    COALESCE_TOLERANCE)) > 0) {
+		struct robot_position rp = vt->vt_position;
+		struct vision_track *vt_near;
 		
-		if (debug > 1) {
-		    info("coalesce %.2f %.2f %.2f %d -- %.2f %.2f %.2f %d\n",
-			 vt->vt_position.x, vt->vt_position.y,
-			 vt->vt_position.theta, vt->vt_client->vc_port,
-			 vt_extra->vt_position.x, vt_extra->vt_position.y,
-			 vt->vt_position.theta, vt_extra->vt_client->vc_port);
+		if (debug > 2) {
+		    printf("start %.2f %.2f\n", vt->vt_position.x,
+			   vt->vt_position.y);
 		}
+		vt_near = (struct vision_track *)near.lh_Head;
+		while (vt_near->vt_link.ln_Succ != NULL) {
+		    if (debug > 2) {
+			printf("merge %.2f %.2f\n", vt_near->vt_position.x,
+			       vt_near->vt_position.y);
+		    }
+		    rp.x += vt_near->vt_position.x;
+		    rp.y += vt_near->vt_position.y;
+		    // rp.theta += vt_near->vt_position.theta;
+		    vt->vt_age = min(vt->vt_age, vt_near->vt_age);
+		    
+		    vt_near = (struct vision_track *)vt_near->vt_link.ln_Succ;
+		}
+		lnAppendList(extra, &near);
 
-		//midpoint = (vt->vt_client->vc_right + 
-		//    vt_extra->vt_client->vc_left) / 2.0f;
-
-		//curcam_dx = midpoint - vt->vt_position.x;
-		//nextcam_dx = vt_extra->vt_position.x - midpoint;
-
-		overlap = vt->vt_client->vc_right -
-		    vt_extra->vt_client->vc_left;
-		curcam_dx = vt->vt_client->vc_right - vt->vt_position.x;
-		nextcam_dx = vt_extra->vt_position.x - 
-		    vt_extra->vt_client->vc_left;
-
-		curweight = curvy(1.0 - curcam_dx / overlap);
-		nextweight = curvy(1.0 - nextcam_dx / overlap);
-
-#if 0
-		info("  cwt %.2f / %.2f -> %.2f ~ %.2f  --  %d\n",
-		     curcam_dx, overlap, curcam_dx / overlap, curweight,
-		     vt->vt_client->vc_port);
-		info("  nwt %.2f / %.2f -> %.2f ~ %.2f  --  %d\n",
-		     nextcam_dx, overlap, nextcam_dx / overlap, nextweight,
-		     vt_extra->vt_client->vc_port);
-#endif
-		
-		rp = vt->vt_position;
-		rp.x = ((curweight * vt->vt_position.x) +
-			(nextweight * vt_extra->vt_position.x)) /
-		    (curweight + nextweight);
-		rp.y = ((curweight * vt->vt_position.y) +
-			(nextweight * vt_extra->vt_position.y)) /
-		    (curweight + nextweight);
-		rp.theta = ((curweight * vt->vt_position.theta) +
-			    (nextweight * vt_extra->vt_position.theta)) /
-		    (curweight + nextweight);
-
-#if 0
-		info("  wa %.2f %.2f -> %.2f  --  %.2f %.2f -> %.2f\n",
-		     vt->vt_position.x,
-		     vt_extra->vt_position.x,
-		     rp.x,
-		     vt->vt_position.y,
-		     vt_extra->vt_position.y,
-		     rp.y);
-#endif
-
-		
-#ifdef RECORD_FRAMES
-		fprintf(xdat_file,
-			"%d %.2f %.2f # coalesce  --  %.2f %.2f -- %d %d\n",
-			frame_count,
-			rp.x,
-			(nextcam_dx > curcam_dx) ?
-			vt_extra->vt_position.x : vt->vt_position.x,
-			vt->vt_position.x,
-			vt_extra->vt_position.x,
-			vt->vt_client->vc_port,
-			vt_extra->vt_client->vc_port);
-		
-		fprintf(ydat_file,
-			"%d %.2f %.2f # coalesce  --  %.2f %.2f -- %d %d\n",
-			frame_count,
-			rp.y,
-			(nextcam_dx > curcam_dx) ?
-			vt_extra->vt_position.y : vt->vt_position.y,
-			vt->vt_position.y,
-			vt_extra->vt_position.y,
-			vt->vt_client->vc_port,
-			vt_extra->vt_client->vc_port);
-		
-		fprintf(tdat_file,
-			"%d %.2f %.2f # coalesce  --  %.2f %.2f -- %d %d\n",
-			frame_count,
-			rp.theta,
-			(nextcam_dx > curcam_dx) ?
-			vt_extra->vt_position.theta : vt->vt_position.theta,
-			vt->vt_position.theta,
-			vt_extra->vt_position.theta,
-			vt->vt_client->vc_port,
-			vt_extra->vt_client->vc_port);
-#endif
-		
+		rp.x /= rc + 1;
+		rp.y /= rc + 1;
+		if (debug > 2) {
+		    printf("final %.2f %.2f\n", rp.x, rp.y);
+		}
+		// rp.theta /= rc;
 		vt->vt_position = rp;
-		vt->vt_age = min(vt->vt_age, vt_extra->vt_age);
-		
-		lnRemove(&vt_extra->vt_link);
-		lnAddHead(extra, &vt_extra->vt_link);
-		
-		in_camera_count -= 1;
-	    }
-
-	    if (in_camera_count > 1) {
-#ifdef RECORD_FRAMES
-		fprintf(xdat_file,
-			"%d %.2f %.2f # 2 cam %d\n",
-			frame_count,
-			vt->vt_position.x,
-			vt->vt_position.x,
-			vt->vt_client->vc_port);
-		fprintf(ydat_file,
-			"%d %.2f %.2f # 2 cam %d\n",
-			frame_count,
-			vt->vt_position.y,
-			vt->vt_position.y,
-			vt->vt_client->vc_port);
-		fprintf(tdat_file,
-			"%d %.2f %.2f # 2 cam %d\n",
-			frame_count,
-			vt->vt_position.theta,
-			vt->vt_position.theta,
-			vt->vt_client->vc_port);
-#endif
 	    }
 	}
-	else {
-#ifdef RECORD_FRAMES
-	    fprintf(xdat_file,
-		    "%d %.2f %.2f # %d\n",
-		    frame_count,
-		    vt->vt_position.x,
-		    vt->vt_position.x,
-		    vt->vt_client->vc_port);
-	    fprintf(ydat_file,
-		    "%d %.2f %.2f # %d\n",
-		    frame_count,
-		    vt->vt_position.y,
-		    vt->vt_position.y,
-		    vt->vt_client->vc_port);
-	    fprintf(tdat_file,
-		    "%d %.2f %.2f # %d\n",
-		    frame_count,
-		    vt->vt_position.theta,
-		    vt->vt_position.theta,
-		    vt->vt_client->vc_port);
-#endif
-	}
 	
 	vt = (struct vision_track *)vt->vt_link.ln_Succ;
     }
@@ -454,15 +353,29 @@ void vtMatch(struct lnMinList *pool,
     while (vt->vt_link.ln_Succ != NULL) {
 	struct vision_track *vt_prev;
 	float distance;
+	void dump_vision_list(struct lnMinList *list);
 	
 	if ((vt_prev = vtFindMin(vt,
 				 (struct vision_track *)prev->lh_Head,
 				 &distance)) != NULL) {
-	    if (distance < 0.30) {
+	    if (distance <= 0.06) {
 		vt->vt_userdata = vt_prev->vt_userdata;
 		lnRemove(&vt_prev->vt_link);
 		lnAddHead(pool, &vt_prev->vt_link);
 	    }
+	    else {
+#if 0
+		info("too far %.2f\n", distance);
+#endif
+	    }
+	}
+	else {
+#if 0
+	    info("no match %.2f %.2f\n", vt->vt_position.x, vt->vt_position.y);
+	    dump_vision_list(now);
+	    info("==\n");
+	    dump_vision_list(prev);
+#endif
 	}
 	
 	vt = (struct vision_track *)vt->vt_link.ln_Succ;
diff --git a/robots/vmcd/visionTrack.h b/robots/vmcd/visionTrack.h
index 3e0aa209ba..a939964a41 100644
--- a/robots/vmcd/visionTrack.h
+++ b/robots/vmcd/visionTrack.h
@@ -1,3 +1,8 @@
+/*
+ * EMULAB-COPYRIGHT
+ * Copyright (c) 2005 University of Utah and the Flux Group.
+ * All rights reserved.
+ */
 
 #ifndef _vision_track_h
 #define _vision_track_h
@@ -7,17 +12,39 @@
 
 #include "vmcd.h"
 
+/**
+ * The maximum distance in meters for which tracks from different cameras will
+ * be considered the same and coalesced.
+ */
+#define COALESCE_TOLERANCE 0.075
+
+/**
+ * The maximum distance in meters for which a track is considered the same from
+ * one frame to the next.
+ */
+#define MERGE_TOLERANCE 0.03
+
+/**
+ * The maximum number of frames that a track can miss before it is considered
+ * lost.
+ */
+#define MAX_TRACK_AGE 5
+
+/**
+ * Structure used to manage fiducials detected by the vision system.
+ */
 struct vision_track {
-    struct lnMinNode vt_link;
-    struct robot_position vt_position;
-    struct vmc_client *vt_client;
-    int vt_age;
-    void *vt_userdata;
+    struct lnMinNode vt_link;		/*< Linked list header. */
+    struct robot_position vt_position;	/*< Fiducial position/orientation. */
+    struct vmc_client *vt_client;	/*< Camera that detected the track. */
+    int vt_age;				/*< Age of the track (lower=younger) */
+    void *vt_userdata;			/*< Data attached to the track. */
 };
 
 /**
  * Update the list of tracks for the current frame with the tracks from a given
- * vmc-client.
+ * vmc-client.  The dimensions of the camera will also be adjusted to ensure
+ * the track's position falls within the bounds.
  *
  * @param now The track list for the current frame, any new tracks will be
  * added to this.
@@ -32,15 +59,33 @@ int vtUpdate(struct lnMinList *now,
 	     struct mtp_packet *mp,
 	     struct lnMinList *pool);
 
+/**
+ * Move tracks from previous frames into the current frame if they are still
+ * young and missing from the current frame.  Tracks that are too old or match
+ * a track in the current frame are returned to the pool.
+ *
+ * @param now The list of tracks in the current frame.
+ * @param old The list of tracks from the previous frame.
+ * @param pool The list to return dead tracks to.
+ */
 void vtAgeTracks(struct lnMinList *now,
 		 struct lnMinList *old,
 		 struct lnMinList *pool);
+
+/**
+ * Copy tracks in the source frame to the destination frame.
+ *
+ * @param dst The list to add new tracks to.
+ * @param src The list of tracks to duplicate.
+ * @param pool The list of free nodes to draw from.
+ */
 void vtCopyTracks(struct lnMinList *dst,
 		  struct lnMinList *src,
 		  struct lnMinList *pool);
 
 /**
- * Coalesce tracks that are in an area where cameras are overlapping.
+ * Coalesce tracks that are in an area where cameras are overlapping.  This
+ * function will produce a frame with the canonical set of tracks.
  *
  * @param extra The list to add any duplicate tracks found in the current
  * frame.
diff --git a/robots/vmcd/vmc-client.c b/robots/vmcd/vmc-client.c
index 59b8c6517e..4a69fd195a 100644
--- a/robots/vmcd/vmc-client.c
+++ b/robots/vmcd/vmc-client.c
@@ -24,6 +24,8 @@
 #include "mtp.h"
 #include <math.h>
 
+#define MAX_CLIENT_COUNT 10
+
 #if defined(HAVE_MEZZANINE)
 #  include "mezz.h"
 #else
@@ -81,13 +83,9 @@ static volatile unsigned int mezz_frame_count = 0;
 
 /**
  * X offset from the real world X == 0 to our local camera X == 0.
- */
-static double x_offset = 0.0;
-
-/**
  * Y offset from the real world Y == 0 to our local camera Y == 0.
  */
-static double y_offset = 0.0;
+static struct robot_position offsets;
 
 static double z_offset = 0.0;
 
@@ -112,6 +110,7 @@ static void usage(void)
             "  -x offset\tx offset from real world x = 0 to our local x = 0\n"
             "  -y offset\ty offset from real world y = 0 to our local y = 0\n"
 	    "  -z offset\tz offset from\n"
+	    "  -o orientation\tcamera orientation\n"
 #if !defined(HAVE_MEZZANINE)
 	    "  -f file\tFile to read simulated positions from.\n"
 #endif
@@ -180,13 +179,17 @@ void radial_trans(struct robot_position *p_inout)
  */
 void local2global_posit_trans(struct robot_position *p_inout)
 {
-    float old_x = p_inout->x;
+    struct robot_position rp;
+    float ct, st;
     
     assert(p_inout != NULL);
-    
-    p_inout->x = p_inout->y + x_offset;
-    p_inout->y = old_x + y_offset;
-    //p_inout->theta -= M_PI_2;
+
+    ct = cosf(offsets.theta);
+    st = sinf(offsets.theta);
+    rp.x = ct * p_inout->x + st * -p_inout->y + offsets.x;
+    rp.y = ct * -p_inout->y + st * -p_inout->x + offsets.y;
+    rp.theta = mtp_theta(p_inout->theta + offsets.theta + M_PI_2);
+    *p_inout = rp;
 }
 
 /**
@@ -341,7 +344,7 @@ int main(int argc, char *argv[])
 
     xdrrec_create(&xdr, 0, 0, NULL, NULL, mem_write);
     
-    while ((c = getopt(argc, argv, "hdp:l:i:f:x:y:z:")) != -1) {
+    while ((c = getopt(argc, argv, "hdp:l:i:f:x:y:z:o:")) != -1) {
         switch (c) {
         case 'h':
             usage();
@@ -373,14 +376,14 @@ int main(int argc, char *argv[])
 #endif
             break;
         case 'x':
-	    if (sscanf(optarg, "%lf", &x_offset) != 1) {
+	    if (sscanf(optarg, "%f", &offsets.x) != 1) {
                 error("error: -x option is not a number: %s\n", optarg);
                 usage();
                 exit(1);
 	    }
             break;
         case 'y':
-	    if (sscanf(optarg, "%lf", &y_offset) != 1) {
+	    if (sscanf(optarg, "%f", &offsets.y) != 1) {
                 error("error: -y option is not a number: %s\n", optarg);
                 usage();
                 exit(1);
@@ -393,6 +396,13 @@ int main(int argc, char *argv[])
                 exit(1);
 	    }
 	    break;
+	case 'o':
+	    if (sscanf(optarg, "%f", &offsets.theta) != 1) {
+                error("error: -o option is not a number: %s\n", optarg);
+                usage();
+                exit(1);
+	    }
+	    break;
         default:
             break;
         }
@@ -499,6 +509,7 @@ int main(int argc, char *argv[])
         errorc("listen");
     }
     else {
+	mtp_handle_t mh[MAX_CLIENT_COUNT];
         fd_set readfds, clientfds;
         int last_mezz_frame = 0;
         
@@ -535,7 +546,13 @@ int main(int argc, char *argv[])
                                      &slen)) == -1) {
                         errorc("accept");
                     }
-                    else {
+                    else if (fd >= MAX_CLIENT_COUNT) {
+			error("too many clients connected\n");
+		    }
+		    else if ((mh[fd] = mtp_create_handle(fd)) == NULL) {
+			error("unable to allocate handle\n");
+		    }
+		    else {
 			if (debug) {
 			    info("vmc-client: connect from %s:%d (fd=%d)\n",
 				 inet_ntoa(peer_sin.sin_addr),
@@ -552,35 +569,44 @@ int main(int argc, char *argv[])
 			}
                         FD_SET(fd, &readfds);
                         FD_SET(fd, &clientfds);
+
+			fd = -1;
                     }
+
+		    if (fd != -1)
+			close(fd);
                 }
                 
                 /*
 		 * We assume that if somebody connected to us tries to write
                  * us some data, they're screwing up; we're just a data source.
                  */
-                for (lpc = 0; lpc < FD_SETSIZE; lpc++) {
+                for (lpc = 0; lpc < MAX_CLIENT_COUNT; lpc++) {
                     if ((lpc != serv_sock) && FD_ISSET(lpc, &rreadyfds)) {
-			if (debug) {
-			    char buffer[512];
-			    int rc;
-
-			    rc = read(lpc, buffer, sizeof(buffer));
-			    if (rc == 0) {
-				info("vmc-client: fd %d disconnected\n", lpc);
-			    }
-			    else {
-				info("vmc-client: fd %d send %d bytes -- "
-				     "disconnecting\n",
-				     lpc,
-				     rc);
+			struct mtp_packet mp;
+			int good = 0;
+			
+			if (((rc = mtp_receive_packet(mh[lpc], &mp)) !=
+			     MTP_PP_SUCCESS) ||
+			    (mp.vers != MTP_VERSION)) {
+			    error("invalid client %d %p\n", rc, mp);
+			}
+			else {
+			    if (mp.data.opcode == MTP_CONFIG_VMC_CLIENT) {
+				offsets.x = mp.data.mtp_payload_u.
+				    config_vmc_client.fixed_x;
+				offsets.y = mp.data.mtp_payload_u.
+				    config_vmc_client.fixed_y;
 			    }
+			    good = 1;
 			}
-			
-                        close(lpc);
-                        FD_CLR(lpc, &readfds);
-                        FD_CLR(lpc, &clientfds);
-                    }
+
+			if (!good) {
+			    close(lpc);
+			    FD_CLR(lpc, &readfds);
+			    FD_CLR(lpc, &clientfds);
+			}
+		    }
                 }
             }
             else if (rc == -1) {
diff --git a/robots/vmcd/vmcd.c b/robots/vmcd/vmcd.c
index 15c0c42185..99ce9f7215 100644
--- a/robots/vmcd/vmcd.c
+++ b/robots/vmcd/vmcd.c
@@ -1,3 +1,10 @@
+/*
+ * EMULAB-COPYRIGHT
+ * Copyright (c) 2005 University of Utah and the Flux Group.
+ * All rights reserved.
+ */
+
+#include "config.h"
 
 #include <math.h>
 #include <float.h>
@@ -178,7 +185,7 @@ static void dump_vision_track(struct vision_track *vt)
 	 ((struct robot_object *)vt->vt_userdata)->ro_name);
 }
 
-static void dump_vision_list(struct lnMinList *list)
+ void dump_vision_list(struct lnMinList *list)
 {
     struct vision_track *vt;
 
@@ -462,6 +469,18 @@ int main(int argc, char *argv[])
 						NULL)) == NULL) {
 	    pfatal("mtp_create_handle");
 	}
+	else {
+	    struct mtp_packet mp;
+
+	    mtp_init_packet(&mp,
+			    MA_Opcode, MTP_CONFIG_VMC_CLIENT,
+			    MA_Role, MTP_ROLE_VMC,
+			    MA_X, vmc_config.cameras.cameras_val[lpc].fixed_x,
+			    MA_Y, vmc_config.cameras.cameras_val[lpc].fixed_y,
+			    MA_TAG_DONE);
+	    if (mtp_send_packet(vc->vc_handle, &mp) != MTP_PP_SUCCESS)
+		pfatal("could not configure vmc-client");
+	}
     }
 
     /*
@@ -511,8 +530,7 @@ int main(int argc, char *argv[])
 #endif
 
 	if (current_client == vmc_client_count) {
-	    struct vision_track *vt, *vt_unknown = NULL;
-	    int unknown_track_count = 0;
+	    struct vision_track *vt;
 	    struct robot_object *ro;
 
 	    /* We've got all of the camera tracks, start processing. */
@@ -523,10 +541,14 @@ int main(int argc, char *argv[])
 		       vmc_clients,
 		       vmc_client_count); // Get rid of duplicates
 
-	    /*
-	     * Match the current frame to the last frame and throw any old
-	     * frames (vt_age > 5) in the pool.
+	    /**
+	     * Remove any unknown tracks before doing the match so that they
+	     * are not candidates for matching against tracks in the current
+	     * frame.
 	     */
+	    vtUnknownTracks(&vt_pool, &last_frame);
+
+	    /* Match the current frame to the last frame. */
 	    vtMatch(&vt_pool, &last_frame, &current_frame);
 
 	    /* Reset the list of known/unknown bots. */
@@ -553,10 +575,6 @@ int main(int argc, char *argv[])
 				    MA_TAG_DONE);
 		    mtp_send_packet(emc_handle, &ump);
 		}
-		else {
-		    unknown_track_count += 1;
-		    vt_unknown = vt;
-		}
 		
 		if (debug > 2) {
 		    printf("track %f %f %f - %d - %p %s\n",
@@ -731,6 +749,11 @@ int main(int argc, char *argv[])
 				 */
 				if ((vt = vtFindWiggle(&wiggle_frame,
 						       &last_frame)) == NULL) {
+				    if (wiggle_bot == NULL) {
+					lnAppendList(&vt_pool, &wiggle_frame);
+					break;
+				    }
+				    
 				    /*
 				     * Couldn't locate the robot, add him to
 				     * the list of "lost" robots which we'll
-- 
GitLab