001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.hardware.turntable; 031 032import java.io.BufferedReader; 033import java.io.IOException; 034import java.io.InputStreamReader; 035import java.io.UnsupportedEncodingException; 036 037import jssc.SerialPort; 038 039import org.openimaj.hardware.serial.SerialDevice; 040 041/** 042 * A simple controller for our serially connected electronic turntable. 043 * 044 * Send NNNNNA0 to rotate anticlockwise by NNNNN increments (360/24000th of a 045 * degree) Send NNNNNC0 to rotate clockwise by NNNNN increments (360/24000th of 046 * a degree) 047 * 048 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 049 */ 050public class Turntable { 051 protected final static int TICKS_PER_REVOLUTION = 24000; 052 protected final static double TICKS_PER_DEGREE = TICKS_PER_REVOLUTION / 360.0; 053 protected final static double TICKS_PER_RADIAN = TICKS_PER_REVOLUTION / (2.0 * Math.PI); 054 055 protected int currentAngleTicks = 0; 056 protected SerialDevice turntableDevice; 057 058 /** 059 * Default constructor. Opens a connection to the turntable on the given 060 * port. 061 * 062 * @param port 063 * The port 064 * @throws Exception 065 */ 066 public Turntable(String port) throws Exception { 067 turntableDevice = new SerialDevice(port, 9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, 068 SerialPort.PARITY_NONE); 069 } 070 071 /** 072 * Get the current absolute angle in degrees (relative to the position at 073 * initialisation) 074 * 075 * @return the absolute angle in degrees 076 */ 077 public double getCurrentAngleDegrees() { 078 return currentAngleTicks / TICKS_PER_DEGREE; 079 } 080 081 /** 082 * Get the current absolute angle in radians (relative to the position at 083 * initialisation) 084 * 085 * @return the absolute angle in radians 086 */ 087 public double getCurrentAngleRadians() { 088 return currentAngleTicks / TICKS_PER_RADIAN; 089 } 090 091 /** 092 * Rotate the turntable to the given absolute angle in radians (relative to 093 * the position at initialisation). The turntable will take the shortest 094 * path to the requested position. 095 * 096 * @param rads 097 * the angle in radians 098 * @throws IOException 099 */ 100 public void rotateToRadians(double rads) throws IOException { 101 rotateToDegrees(rads * 180 / Math.PI); 102 } 103 104 /** 105 * Rotate the turntable to the given absolute angle in degrees (relative to 106 * the position at initialisation). The turntable will take the shortest 107 * path to the requested position. 108 * 109 * @param degrees 110 * the angle in degrees 111 * @throws IOException 112 */ 113 public void rotateToDegrees(double degrees) throws IOException { 114 final double current = getCurrentAngleDegrees(); 115 double delta = degrees - current; 116 117 if (delta > 180) 118 delta = 360 - delta; 119 if (delta < -180) 120 delta = 360 + delta; 121 122 sendCommand((int) Math.rint(delta * TICKS_PER_DEGREE)); 123 } 124 125 /** 126 * Rotate the turntable by the given angle in radians. Positive angles are 127 * clockwise, negative anticlockwise. 128 * 129 * @param rads 130 * the angle in radians 131 * @throws IOException 132 */ 133 public void rotateByRadians(double rads) throws IOException { 134 sendCommand((int) Math.rint(rads * TICKS_PER_RADIAN)); 135 } 136 137 /** 138 * Rotate the turntable by the given angle in degrees. Positive angles are 139 * clockwise, negative anticlockwise. 140 * 141 * @param degrees 142 * the angle in degrees 143 * @throws IOException 144 */ 145 public void rotateByDegrees(double degrees) throws IOException { 146 sendCommand((int) Math.rint(degrees * TICKS_PER_DEGREE)); 147 } 148 149 protected void sendCommand(int ticks) throws IOException { 150 if (ticks < 0) { 151 sendCommand(Math.abs(ticks), false); 152 } else { 153 sendCommand(ticks, true); 154 } 155 } 156 157 protected void sendCommand(int ticks, boolean cw) throws IOException { 158 final String dir = cw ? "C" : "A"; 159 160 if (cw) 161 currentAngleTicks += ticks; 162 else 163 currentAngleTicks -= ticks; 164 165 if (currentAngleTicks > TICKS_PER_REVOLUTION / 2) 166 currentAngleTicks = TICKS_PER_REVOLUTION - currentAngleTicks; 167 if (currentAngleTicks < -TICKS_PER_REVOLUTION / 2) 168 currentAngleTicks = TICKS_PER_REVOLUTION + currentAngleTicks; 169 170 try { 171 final String cmd = ticks + dir + "0\n"; 172 turntableDevice.getOutputStream().write(cmd.getBytes("US-ASCII")); 173 } catch (final UnsupportedEncodingException e) { 174 throw new RuntimeException(e); 175 } 176 } 177 178 /** 179 * Close the connection to the turntable. 180 * 181 * @throws IOException 182 */ 183 public void close() throws IOException { 184 turntableDevice.close(); 185 } 186 187 /** 188 * Test the turntable 189 * 190 * @param args 191 * @throws Exception 192 */ 193 public static void main(String[] args) throws Exception { 194 System.out.println("Initializing Turntable"); 195 System.out 196 .println("the command \"r 10\" will rotate the turntable to 10 degrees CW relative to the starting point"); 197 System.out 198 .println("the command \"i -10\" will rotate the turntable to 10 degrees AW relative to the current point"); 199 200 // Turntable t = new Turntable("/dev/tty.usbserial-FTCXE2RA"); 201 final Turntable t = new Turntable("/dev/tty.usbserial"); 202 203 System.out.println("Turntable is ready"); 204 System.out.println("Current absolute angle is " + t.getCurrentAngleDegrees() + " degrees"); 205 206 final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 207 208 String s; 209 while ((s = br.readLine()) != null) { 210 try { 211 final String[] parts = s.split("\\s"); 212 213 if (parts[0].equals("q")) 214 break; 215 216 final double ang = Double.parseDouble(parts[1]); 217 if (parts[0].equals("i")) 218 t.rotateByDegrees(ang); 219 else if (parts[0].equals("r")) 220 t.rotateToDegrees(ang); 221 else 222 throw new Exception(); 223 224 System.out.println("Rotating to absolute angle of " + t.getCurrentAngleDegrees() + " degrees"); 225 } catch (final Throwable throwable) { 226 System.out.println("invalid command"); 227 } 228 } 229 230 System.out.println("Done"); 231 System.exit(0); 232 } 233}