Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 package com.github.rosjava.rosjava_extras.hokuyo.scip20;
00018
00019 import com.google.common.base.Preconditions;
00020
00021 import com.github.rosjava.rosjava_extras.hokuyo.LaserScan;
00022 import com.github.rosjava.rosjava_extras.hokuyo.LaserScanListener;
00023 import com.github.rosjava.rosjava_extras.hokuyo.LaserScannerConfiguration;
00024 import com.github.rosjava.rosjava_extras.hokuyo.LaserScannerDevice;
00025
00026
00027 import org.apache.commons.logging.Log;
00028 import org.apache.commons.logging.LogFactory;
00029 import org.ros.exception.RosRuntimeException;
00030 import org.ros.message.Time;
00031 import org.ros.time.RemoteUptimeClock;
00032 import org.ros.time.TimeProvider;
00033
00034 import java.io.BufferedInputStream;
00035 import java.io.BufferedOutputStream;
00036 import java.io.BufferedReader;
00037 import java.io.BufferedWriter;
00038 import java.io.IOException;
00039 import java.io.InputStream;
00040 import java.io.InputStreamReader;
00041 import java.io.OutputStream;
00042 import java.io.OutputStreamWriter;
00043 import java.nio.charset.Charset;
00044 import java.util.concurrent.Callable;
00045
00049 public class Device implements LaserScannerDevice {
00050
00051 private static final boolean DEBUG = false;
00052 private static final Log log = LogFactory.getLog(Device.class);
00053
00054 private static final int STREAM_BUFFER_SIZE = 8192;
00055 private static final String EXPECTED_SENSOR_DIAGNOSTIC = "Sensor works well.";
00056 private static final double DRIFT_SENSITIVITY = 0.3;
00057 private static final double ERROR_REDUCTION_COEFFICIENT_SENSITIVITY = 0.3;
00058 private static final double LATENCY_FILTER_THRESHOLD = 1.05;
00059 private static final int LATENCY_FILTER_SAMPLE_SIZE = 10;
00060 private static final int CALIBRATION_SAMPLE_SIZE = 20;
00061 private static final int CALIBRATION_SAMPLING_DELAY_MILLIS = 500;
00062
00063 private final BufferedInputStream bufferedInputStream;
00064 private final BufferedReader reader;
00065 private final BufferedWriter writer;
00066 private final LaserScannerConfiguration configuration;
00067 private final RemoteUptimeClock remoteUptimeClock;
00068
00079 public Device(InputStream inputStream, OutputStream outputStream, TimeProvider epochTimeProvider) {
00080 bufferedInputStream = new BufferedInputStream(inputStream, STREAM_BUFFER_SIZE);
00081 reader =
00082 new BufferedReader(new InputStreamReader(bufferedInputStream, Charset.forName("US-ASCII")));
00083 writer =
00084 new BufferedWriter(new OutputStreamWriter(new BufferedOutputStream(outputStream,
00085 STREAM_BUFFER_SIZE), Charset.forName("US-ASCII")));
00086 remoteUptimeClock =
00087 RemoteUptimeClock.newDefault(epochTimeProvider, new Callable<Double>() {
00088 @Override
00089 public Double call() throws Exception {
00090 return (double) queryUptime();
00091 }
00092 }, DRIFT_SENSITIVITY, ERROR_REDUCTION_COEFFICIENT_SENSITIVITY, LATENCY_FILTER_SAMPLE_SIZE,
00093 LATENCY_FILTER_THRESHOLD);
00094 init();
00095 configuration = queryConfiguration();
00096 }
00097
00107 private void init() {
00108 reset();
00109 String sensorDiagnostic = queryState().getSensorDiagnostic();
00110 Preconditions.checkState(sensorDiagnostic.equals(EXPECTED_SENSOR_DIAGNOSTIC),
00111 "Sensor diagnostic check failed: \"" + sensorDiagnostic + "\"");
00112 waitUntilReady();
00113 remoteUptimeClock.calibrate(CALIBRATION_SAMPLE_SIZE, CALIBRATION_SAMPLING_DELAY_MILLIS);
00114 }
00115
00116 private void waitUntilReady() {
00117 boolean ready = false;
00118 while (!ready) {
00119 ready = true;
00120 write("MD0000076800001");
00121 try {
00122 checkMdmsStatus();
00123 } catch (MdmsException e) {
00124 if (DEBUG) {
00125 log.info("Sensor not ready.", e);
00126 }
00127 ready = false;
00128 }
00129 checkTerminator();
00130 }
00131 Preconditions.checkState(read().equals("MD0000076800000"));
00132 checkMdmsStatus();
00133 while (true) {
00134 String line = read();
00135 if (line.length() == 0) {
00136 break;
00137 }
00138 verifyChecksum(line);
00139 }
00140 }
00141
00142 @Override
00143 public LaserScannerConfiguration getConfiguration() {
00144 return configuration;
00145 }
00146
00147 private void write(String command) {
00148 Preconditions.checkArgument(!command.endsWith("\n"));
00149 try {
00150 writer.write(command + "\n");
00151 writer.flush();
00152 if (DEBUG) {
00153 log.info("Wrote: " + command);
00154 }
00155 } catch (IOException e) {
00156 throw new RosRuntimeException(e);
00157 }
00158 String echo = read();
00159 Preconditions.checkState(echo.equals(command),
00160 String.format("Echo does not match command: \"%s\" != \"%s\"", echo, command));
00161 }
00162
00163 private void checkStatus() {
00164 String statusAndChecksum = read();
00165 String status = verifyChecksum(statusAndChecksum);
00166 Preconditions.checkState(status.equals("00"));
00167 }
00168
00169 private void checkMdmsStatus() {
00170 String statusAndChecksum = read();
00171 String status = verifyChecksum(statusAndChecksum);
00172
00173
00174 if (status.equals("00") || status.equals("99")) {
00175 return;
00176 }
00177 throw new MdmsException(status);
00178 }
00179
00180 private void checkTmStatus() {
00181 String statusAndChecksum = read();
00182 String status = verifyChecksum(statusAndChecksum);
00183 if (!(status.equals("01") || status.equals("04"))) {
00184 return;
00185 }
00186 throw new TmException(status);
00187 }
00188
00189 private String read() {
00190 String line;
00191 try {
00192 line = reader.readLine();
00193 } catch (IOException e) {
00194 throw new RosRuntimeException(e);
00195 }
00196 if (DEBUG) {
00197 log.info("Read: " + line);
00198 }
00199 return line;
00200 }
00201
00202 private String verifyChecksum(String buffer) {
00203 Preconditions.checkArgument(buffer.length() > 0, "Empty buffer supplied to verifyChecksum().");
00204 String data = buffer.substring(0, buffer.length() - 1);
00205 char checksum = buffer.charAt(buffer.length() - 1);
00206 int sum = 0;
00207 for (int i = 0; i < data.length(); i++) {
00208 sum += data.charAt(i);
00209 }
00210 if ((sum & 63) + 0x30 == checksum) {
00211 return data;
00212 }
00213 throw new ChecksumException();
00214 }
00215
00216 private void reset() {
00217
00218 write("TM2");
00219 checkTmStatus();
00220 checkTerminator();
00221
00222
00223 write("RS");
00224 checkStatus();
00225 checkTerminator();
00226
00227
00228 write("SCIP2.0");
00229 try {
00230 checkStatus();
00231 } catch (IllegalStateException e) {
00232
00233 if (DEBUG) {
00234 log.error("Switch to SCIP 2.0 failed.", e);
00235 }
00236 }
00237 checkTerminator();
00238
00239
00240 write("RS");
00241 checkStatus();
00242 checkTerminator();
00243 }
00244
00245 private void checkTerminator() {
00246 Preconditions.checkState(read().length() == 0);
00247 }
00248
00252 private long readTimestamp() {
00253 return Decoder.decodeValue(verifyChecksum(read()));
00254 }
00255
00256 @Override
00257 public void startScanning(final LaserScanListener listener) {
00258
00259 new Thread() {
00260 @Override
00261 public void run() {
00262 while (true) {
00263 String command = "MD00000768000%02d";
00264 write(String.format(command, 99));
00265 checkMdmsStatus();
00266 checkTerminator();
00267 String scansRemaining = "99";
00268 while (!scansRemaining.equals("00")) {
00269 String commandEcho = read();
00270 scansRemaining = commandEcho.substring(commandEcho.length() - 2);
00271 checkMdmsStatus();
00272 long timestamp = readTimestamp();
00273 StringBuilder data = new StringBuilder();
00274 boolean checksumOk = true;
00275 while (true) {
00276 String line = read();
00277 if (line.length() == 0) {
00278 if (checksumOk) {
00279 try {
00280 Time time = new Time(remoteUptimeClock.toLocalUptime(timestamp));
00281 int[] ranges = Decoder.decodeValues(data.toString(), 3);
00282 listener.onNewLaserScan(new LaserScan(time, ranges));
00283 } catch (IllegalArgumentException e) {
00284 log.error("Failed to decode scan data.", e);
00285 break;
00286 }
00287 }
00288 break;
00289 }
00290 try {
00291 data.append(verifyChecksum(line));
00292 } catch (ChecksumException e) {
00293
00294
00295
00296
00297 checksumOk = false;
00298 log.error("Invalid checksum.", e);
00299 }
00300 }
00301 }
00302 remoteUptimeClock.update();
00303 }
00304 }
00305 }.start();
00306 }
00307
00308 private String readAndStripSemicolon() {
00309 String buffer = read();
00310 Preconditions.checkState(buffer.charAt(buffer.length() - 2) == ';');
00311 return buffer.substring(0, buffer.length() - 2) + buffer.charAt(buffer.length() - 1);
00312 }
00313
00314 private LaserScannerConfiguration queryConfiguration() {
00315 Configuration.Builder builder = new Configuration.Builder();
00316 write("PP");
00317 checkStatus();
00318 builder.parseModel(verifyChecksum(readAndStripSemicolon()));
00319 builder.parseMinimumMeasurement(verifyChecksum(readAndStripSemicolon()));
00320 builder.parseMaximumMeasurement(verifyChecksum(readAndStripSemicolon()));
00321 builder.parseTotalSteps(verifyChecksum(readAndStripSemicolon()));
00322 builder.parseFirstStep(verifyChecksum(readAndStripSemicolon()));
00323 builder.parseLastStep(verifyChecksum(readAndStripSemicolon()));
00324 builder.parseFrontStep(verifyChecksum(readAndStripSemicolon()));
00325 builder.parseStandardMotorSpeed(verifyChecksum(readAndStripSemicolon()));
00326 checkTerminator();
00327 return builder.build();
00328 }
00329
00330 private State queryState() {
00331 State.Builder builder = new State.Builder();
00332 write("II");
00333 checkStatus();
00334 builder.parseModel(verifyChecksum(readAndStripSemicolon()));
00335 builder.parseLaserIlluminationState(verifyChecksum(readAndStripSemicolon()));
00336 builder.parseMotorSpeed(verifyChecksum(readAndStripSemicolon()));
00337 builder.parseMeasurementMode(verifyChecksum(readAndStripSemicolon()));
00338 builder.parseBitRate(verifyChecksum(readAndStripSemicolon()));
00339 builder.parseTimeStamp(verifyChecksum(readAndStripSemicolon()));
00340 builder.parseSensorDiagnostic(verifyChecksum(readAndStripSemicolon()));
00341 checkTerminator();
00342 return builder.build();
00343 }
00344
00345 private long queryUptime() {
00346
00347 write("TM0");
00348 checkTmStatus();
00349 checkTerminator();
00350
00351 write("TM1");
00352 checkTmStatus();
00353
00354
00355 long timestamp = readTimestamp();
00356 checkTerminator();
00357
00358 write("TM2");
00359 checkTmStatus();
00360 checkTerminator();
00361 return timestamp;
00362 }
00363
00364 @Override
00365 public void shutdown() {
00366 try {
00367 reader.close();
00368 } catch (IOException e) {
00369
00370 e.printStackTrace();
00371 }
00372 try {
00373 writer.close();
00374 } catch (IOException e) {
00375
00376 e.printStackTrace();
00377 }
00378 }
00379 }