/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) 2010, Wolfgang Puffitsch <wpuffits@mail.tuwien.ac.at>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cruiser.control;
import joprt.RtThread;
public class SpeedManager implements Runnable {
final SpeedState frontLeftSpeed = new SpeedState();
final SpeedState frontRightSpeed = new SpeedState();
final SpeedState rearLeftSpeed = new SpeedState();
final SpeedState rearRightSpeed = new SpeedState();
private final SpeedState [] speedState = new SpeedState[4];
// speed forecast
private volatile TargetSpeedState targetSpeed = new TargetSpeedState();
private final SpeedState currentSpeed = new SpeedState();
// distance estimation
private long lastNow = 0;
private long distance = 0;
public SpeedManager() {
speedState[0] = frontLeftSpeed;
speedState[1] = frontRightSpeed;
speedState[2] = rearLeftSpeed;
speedState[3] = rearRightSpeed;
}
private void manage(long now) {
// compute average
int sum = 0;
int sqsum = 0;
int cnt = 0;
for (int i = 0; i < speedState.length; i++) { //@WCA loop = 4
if (speedState[i].valid) {
int speed = speedState[i].speed;
sum += speed;
sqsum += speed*speed;
cnt++;
}
}
// not enough sensor values
if (cnt < speedState.length/2) {
// System.err.println("No reliable speed estimate available, brake, cnt="+cnt);
currentSpeed.valid = false;
} else {
int avg = sum/cnt;
// should divide by cnt-1 for deviation, but avoid division by zero
int dev = (sqsum-(sum*sum)/cnt)/cnt;
dev = dev < 0 ? -dev : dev;
// sensor values should have a reasonable deviation
// System.err.println("$ dev: "+dev+" avg: "+avg+" avg**2: "+avg*avg);
if (dev > 4000) {
// System.err.println("Speed estimates too divergent, brake, dev="+dev);
currentSpeed.valid = false;
} else {
currentSpeed.speed = avg;
currentSpeed.valid = true;
}
}
distance += ((long)currentSpeed.speed*1000*1000*1000)/Math.max(1, now-lastNow);
lastNow = now;
}
public int getCurrentSpeed() {
return currentSpeed.speed;
}
public void setTargetSpeed(StampedDistanceMessage msg) {
targetSpeed = new TargetSpeedState(targetSpeed.targetSpeed, msg);
}
public int getTargetSpeed() {
if (!currentSpeed.valid) {
// no reliable estimate, so we should better stop
return 0;
} else {
TargetSpeedState t = targetSpeed;
long dist = distance/1000;
if (t.targetDistance - dist > 0) {
int retval;
long delta = t.targetDistance-t.lastTargetDistance;
delta = delta == 0 ? 1 : delta;
if (t.targetSpeed > t.lastTargetSpeed) { // accelerating
// start to accelerate quickly
long d = 500*(dist-t.lastTargetDistance)/delta;
d += 500;
retval = (int)((t.lastTargetSpeed*(1000-d)+(t.targetSpeed*d))/1000);
} else { // braking
// start to brake without bias
long d = 1000*(dist-t.lastTargetDistance)/delta;
// approach quadratically
d *= d;
d /= 1000;
retval = (int)((t.lastTargetSpeed*(1000-d)+(t.targetSpeed*d))/1000);
// emergency braking if we run out of room for braking
int v = currentSpeed.speed - t.targetSpeed;
if ((t.targetDistance-dist) < (v*v)/190) {
retval = t.targetSpeed;
}
}
return retval;
} else {
return t.targetSpeed;
}
}
}
public long getDistance() {
return distance;
}
public void run() {
lastNow = System.nanoTime();
for (;;) {
long now = System.nanoTime();
manage(now);
RtThread.currentRtThread().waitForNextPeriod();
}
}
}