package com.roboclub.robobuggy.nodes.localizers;
import Jama.Matrix;
import com.roboclub.robobuggy.main.Util;
import com.roboclub.robobuggy.messages.EncoderMeasurement;
import com.roboclub.robobuggy.messages.GPSPoseMessage;
import com.roboclub.robobuggy.messages.GpsMeasurement;
import com.roboclub.robobuggy.messages.IMUAngularPositionMessage;
import com.roboclub.robobuggy.messages.SteeringMeasurement;
import com.roboclub.robobuggy.ros.Message;
import com.roboclub.robobuggy.ros.MessageListener;
import com.roboclub.robobuggy.ros.Node;
import com.roboclub.robobuggy.ros.NodeChannel;
import com.roboclub.robobuggy.ros.Publisher;
import com.roboclub.robobuggy.ros.Subscriber;
import java.util.Date;
/**
* This class runs a Node that will build a fused position estimate by trusting all new GPS measurements completely
*
* @author Trevor Decker
*/
public class HighTrustGPSLocalizer implements Node {
private double buggyFrameGpsX;
private double buggyFrameGpsY;
private double buggyFrameRotZ;
private double lastEncoderReading;
private double buggySteeringAngle;
private double oldGPSX = 0.0;
private double oldGPSY = 0.0;
private Publisher posePub;
/**
* Constructor for the High Trust Localizer which will initialize the system to an identity (zero position)
*/
public HighTrustGPSLocalizer() {
//init values
buggyFrameGpsX = 0.0;
buggyFrameGpsY = 0.0;
buggyFrameRotZ = 0.0;
buggySteeringAngle = 0.0;// wheel direction in buggy frame
lastEncoderReading = 0.0;
posePub = new Publisher(NodeChannel.POSE.getMsgPath());
//Read all the steering messages we're getting and adjust our stored steering angle accordingly
new Subscriber("htGpsLoc", NodeChannel.STEERING.getMsgPath(), new MessageListener() {
@Override
public void actionPerformed(String topicName, Message m) {
SteeringMeasurement steerM = (SteeringMeasurement) m;
// TODO Auto-generated method stub
buggySteeringAngle = steerM.getAngle();
}
});
//Initialize subscriber to GPS measurements
new Subscriber("htGpsLoc", NodeChannel.GPS.getMsgPath(), new MessageListener() {
@Override
public void actionPerformed(String topicName, Message m) {
GpsMeasurement newGPSData = (GpsMeasurement) m;
synchronized (this) {
// Get the delta latitude and longitude, use that to figure out how far we've travelled
buggyFrameGpsY = newGPSData.getLatitude();
buggyFrameGpsX = newGPSData.getLongitude();
double dLat = buggyFrameGpsY - oldGPSY;
double dLon = buggyFrameGpsX - oldGPSX;
oldGPSX = buggyFrameGpsX;
oldGPSY = buggyFrameGpsY;
// take the arctangent in order to get the heading (in degrees)
buggyFrameRotZ = Math.toDegrees(Math.atan2(LocalizerUtil.convertLatToMeters(dLat), LocalizerUtil.convertLonToMeters(dLon)));
publishUpdate();
}
}
});
new Subscriber("HighTrustGpsLoc", NodeChannel.IMU_ANG_POS.getMsgPath(), ((topicName, m) -> {
IMUAngularPositionMessage mes = ((IMUAngularPositionMessage) m);
// double y = mes.getRot()[0][1];
// double x = mes.getRot()[0][0];
//
// buggyFrameRotZ = Util.normalizeAngleDeg(-Math.toDegrees(Math.atan2(y, x))+90);
Matrix r = new Matrix(mes.getRot());
double[][] xVec = { { 1 }, { 0 }, { 0 } };
double[][] yVec = { { 0 }, { 1 }, { 0 } };
double x = r.times(new Matrix(xVec)).get(0, 0);
double y = r.times(new Matrix(yVec)).get(0, 0);
buggyFrameRotZ = Util.normalizeAngleDeg(Math.toDegrees(-Math.atan2(y, x)) + 90);
publishUpdate();
}));
//Update our position based on encoder readings
// TODO note that we will probably run into precision errors since the changes are so small
// would be good to batch up the encoder updates until we get a margin that we know can be represented proeprly
new Subscriber("htGpsLoc", NodeChannel.ENCODER.getMsgPath(), new MessageListener() {
@Override
public void actionPerformed(String topicName, Message m) {
EncoderMeasurement measurement = (EncoderMeasurement) m;
// convert the feet from the last message into a delta degree, and update our position
double currentEncoderMeasurement = measurement.getDistance();
double deltaDistance = currentEncoderMeasurement - lastEncoderReading;
// update heading around curve
buggyFrameRotZ += MotionModel.getHeadingChange(deltaDistance, buggySteeringAngle);
// advance by foward in new heading
LocTuple deltaPos = LocalizerUtil.convertMetersToLatLng(deltaDistance, buggyFrameRotZ);
//Update our position estimate
buggyFrameGpsY += deltaPos.getLatitude();
buggyFrameGpsX += deltaPos.getLongitude();
lastEncoderReading = currentEncoderMeasurement;
publishUpdate();
}
});
}
private void publishUpdate() {
posePub.publish(new GPSPoseMessage(new Date(), buggyFrameGpsY, buggyFrameGpsX, buggyFrameRotZ));
}
@Override
public boolean startNode() {
return true;
}
@Override
public boolean shutdown() {
posePub = null;
buggyFrameGpsX = 0.0;
buggyFrameGpsY = 0.0;
return true;
}
@Override
public void setName(String newName) {
}
@Override
public String getName() {
return "High Trust GPS Localizer";
}
}