int main(int argc, char** argv) {
	init(argc, argv, "JointUpdateNode");
	NodeHandle nh;

	// get the filenames
	nh.param(string("joint_offsets_filename_suffix"), jointOffsetsFilename,
			string("calibration_joint_offsets.xacro"));
	nh.param(string("camera_transform_filename_suffix"),
			cameraTransformFilename,
			string("calibration_camera_transform.xacro"));
	nh.param(string("urdf_filename_suffix"), urdfFilename,
			string("robot_model_calibrated.xml"));
	nh.param(string("marker_transforms_filename_suffix"),
			markerTransformsFilename,
			string("calibration_marker_transformations.xacro"));
	nh.param(string("camera_intrnsics_filename"), cameraIntrnsicsFilename,
			string("nao_bottom_640x480.yaml"));

	ModelLoader modelLoader;
	modelLoader.initializeFromRos();

	urdf::Model model;
	modelLoader.getUrdfModel(model);

	// get the robot name and prepend to the filenames
	string filePrefix = model.getName();
	jointOffsetsFilename = filePrefix + "_" + jointOffsetsFilename;
	cameraTransformFilename = filePrefix + "_" + cameraTransformFilename;
	urdfFilename = filePrefix + "_" + urdfFilename;
	markerTransformsFilename = filePrefix + "_" + markerTransformsFilename;

    Subscriber sub = nh.subscribe("/onlineCalibration/calibration_result",
			1, resultCb);
	spin();
	return 0;
}
void resultCb(const calibrationResultConstPtr& msg) {
	ModelLoader modelLoader;
	modelLoader.initializeFromRos();

	urdf::Model model;
	modelLoader.getUrdfModel(model);

	// write the parameter file for the joint offsets
	map<string, double> offsets;
	for (int i = 0; i < msg->jointNames.size(); i++) {
		offsets[msg->jointNames[i]] = msg->jointOffsets[i];
	}

	JointUpdate ju(model);
	ju.writeCalibrationData(offsets, jointOffsetsFilename);

	// write the parameter file for the camera transformation
	tf::Transform transform;
	tf::transformMsgToTF(msg->cameraTransform, transform);

	CameraTransformUpdate ctu(model);
	ctu.writeCalibrationData(transform, cameraTransformFilename);

	// write the new urdf file
	UrdfUpdate urdfUpdate;
	urdfUpdate.readFromRos("/robot_description");
	urdfUpdate.updateJointOffsets(offsets);
	urdfUpdate.updateCameraDeltaTransform(transform);
	urdfUpdate.writeToFile(urdfFilename);


	ROS_INFO("No transformation found from %s to %s!", markerTransformsFilename.c_str());
	// print the marker transformation on the screen and as parameters in a file
	// TODO: move into own class?
	// TODO: also update urdf...
	ofstream file;
	file.open(markerTransformsFilename.c_str());
	file << "<?xml version=\"1.0\"?>\n";
	file << "<robot>\n";
	for (int i = 0; i < msg->chainNames.size(); i++) {
		double rr, rp, ry, tx, ty, tz;
		tf::Transform current;
		tf::transformMsgToTF(msg->endeffectorToMarker[i], current);
		current = current.inverse();
		tf::Matrix3x3(current.getRotation()).getRPY(rr, rp, ry);
		tx = current.getOrigin().getX();
		ty = current.getOrigin().getY();
		tz = current.getOrigin().getZ();
		cout << "chain: " << msg->chainNames[i] << "\n";
		cout << "translation: " << tx << " " << ty << " " << tz << "\n";
		cout << "rotation: " << rr << " " << rp << " " << ry << "\n";
		file << "\t<property name=\"" << msg->chainNames[i] << "_marker_tx"
				<< "\" value=\"" << tx << "\" />\n";
		file << "\t<property name=\"" << msg->chainNames[i] << "_marker_ty"
				<< "\" value=\"" << ty << "\" />\n";
		file << "\t<property name=\"" << msg->chainNames[i] << "_marker_tz"
				<< "\" value=\"" << tz << "\" />\n";
		file << "\t<property name=\"" << msg->chainNames[i] << "_marker_rr"
				<< "\" value=\"" << rr << "\" />\n";
		file << "\t<property name=\"" << msg->chainNames[i] << "_marker_rp"
				<< "\" value=\"" << rp << "\" />\n";
		file << "\t<property name=\"" << msg->chainNames[i] << "_marker_ry"
				<< "\" value=\"" << ry << "\" />\n";
	}
	file << "</robot>\n";
	file.close();

	// write camera intrinsics
	const string cameraName = "nao_camera";
	camera_calibration_parsers::writeCalibrationYml(cameraIntrnsicsFilename,
			cameraName, msg->cameraInfo);
}