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 gnu.io.SerialPort; 038 039import org.openimaj.hardware.serial.SerialDevice; 040 041 042/** 043 * A simple controller for our serially connected electronic turntable. 044 * 045 * Send NNNNNA0 to rotate anticlockwise by NNNNN increments (360/24000th of a degree) 046 * Send NNNNNC0 to rotate clockwise by NNNNN increments (360/24000th of 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 060 * the given port. 061 * 062 * @param port The port 063 * @throws Exception 064 */ 065 public Turntable(String port) throws Exception { 066 turntableDevice = new SerialDevice( port, 9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE ); 067 } 068 069 /** 070 * Get the current absolute angle in degrees (relative to the 071 * position at initialisation) 072 * @return the absolute angle in degrees 073 */ 074 public double getCurrentAngleDegrees() { 075 return currentAngleTicks / TICKS_PER_DEGREE; 076 } 077 078 /** 079 * Get the current absolute angle in radians (relative to the 080 * position at initialisation) 081 * @return the absolute angle in radians 082 */ 083 public double getCurrentAngleRadians() { 084 return currentAngleTicks / TICKS_PER_RADIAN; 085 } 086 087 /** 088 * Rotate the turntable to the given absolute angle in radians 089 * (relative to the position at initialisation). The turntable 090 * will take the shortest path to the requested position. 091 * 092 * @param rads the angle in radians 093 * @throws IOException 094 */ 095 public void rotateToRadians(double rads) throws IOException { 096 rotateToDegrees(rads * 180 / Math.PI); 097 } 098 099 /** 100 * Rotate the turntable to the given absolute angle in degrees 101 * (relative to the position at initialisation). The turntable 102 * will take the shortest path to the requested position. 103 * 104 * @param degrees the angle in degrees 105 * @throws IOException 106 */ 107 public void rotateToDegrees(double degrees) throws IOException { 108 final double current = getCurrentAngleDegrees(); 109 double delta = degrees - current; 110 111 if (delta > 180) 112 delta = 360-delta; 113 if (delta < -180) 114 delta = 360+delta; 115 116 sendCommand((int)Math.rint(delta * TICKS_PER_DEGREE)); 117 } 118 119 /** 120 * Rotate the turntable by the given angle in radians. 121 * Positive angles are clockwise, negative anticlockwise. 122 * 123 * @param rads the angle in radians 124 * @throws IOException 125 */ 126 public void rotateByRadians(double rads) throws IOException { 127 sendCommand((int)Math.rint(rads * TICKS_PER_RADIAN)); 128 } 129 130 /** 131 * Rotate the turntable by the given angle in degrees. 132 * Positive angles are clockwise, negative anticlockwise. 133 * 134 * @param degrees the angle in degrees 135 * @throws IOException 136 */ 137 public void rotateByDegrees(double degrees) throws IOException { 138 sendCommand((int)Math.rint(degrees * TICKS_PER_DEGREE)); 139 } 140 141 protected void sendCommand(int ticks) throws IOException { 142 if (ticks < 0) { 143 sendCommand(Math.abs(ticks), false); 144 } else { 145 sendCommand(ticks, true); 146 } 147 } 148 149 protected void sendCommand(int ticks, boolean cw) throws IOException { 150 final String dir = cw ? "C" : "A"; 151 152 if (cw) 153 currentAngleTicks += ticks; 154 else 155 currentAngleTicks -= ticks; 156 157 if (currentAngleTicks > TICKS_PER_REVOLUTION/2) 158 currentAngleTicks = TICKS_PER_REVOLUTION - currentAngleTicks; 159 if (currentAngleTicks < -TICKS_PER_REVOLUTION/2) 160 currentAngleTicks = TICKS_PER_REVOLUTION + currentAngleTicks; 161 162 try { 163 final String cmd = ticks + dir + "0\n"; 164 turntableDevice.getOutputStream().write(cmd.getBytes("US-ASCII")); 165 } catch (final UnsupportedEncodingException e) { 166 throw new RuntimeException(e); 167 } 168 } 169 170 /** 171 * Close the connection to the turntable. 172 */ 173 public void close() { 174 turntableDevice.close(); 175 } 176 177 /** 178 * Test the turntable 179 * 180 * @param args 181 * @throws Exception 182 */ 183 public static void main( String[] args ) throws Exception { 184 System.out.println("Initializing Turntable"); 185 System.out.println("the command \"r 10\" will rotate the turntable to 10 degrees CW relative to the starting point"); 186 System.out.println("the command \"i -10\" will rotate the turntable to 10 degrees AW relative to the current point"); 187 188 //Turntable t = new Turntable("/dev/tty.usbserial-FTCXE2RA"); 189 final Turntable t = new Turntable("/dev/tty.usbserial"); 190 191 System.out.println("Turntable is ready"); 192 System.out.println("Current absolute angle is " + t.getCurrentAngleDegrees() + " degrees"); 193 194 final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 195 196 String s; 197 while ((s = br.readLine()) != null) { 198 try { 199 final String[] parts = s.split("\\s"); 200 201 if (parts[0].equals("q")) 202 break; 203 204 final double ang = Double.parseDouble(parts[1]); 205 if (parts[0].equals("i")) 206 t.rotateByDegrees(ang); 207 else if (parts[0].equals("r")) 208 t.rotateToDegrees(ang); 209 else 210 throw new Exception(); 211 212 System.out.println("Rotating to absolute angle of " + t.getCurrentAngleDegrees() + " degrees"); 213 } catch (final Throwable throwable) { 214 System.out.println("invalid command"); 215 } 216 } 217 218 System.out.println("Done"); 219 System.exit(0); 220 } 221}