001    /*----------------------------------------------------------------------------*/
002    /* Copyright (c) FIRST 2008-2012. All Rights Reserved.                        */
003    /* Open Source Software - may be modified and shared by FRC teams. The code   */
004    /* must be accompanied by the FIRST BSD license file in the root directory of */
005    /* the project.                                                               */
006    /*----------------------------------------------------------------------------*/
007    
008    package edu.wpi.first.wpilibj;
009    
010    import com.sun.cldc.jna.Pointer;
011    import com.sun.cldc.jna.Structure;
012    import edu.wpi.first.wpilibj.communication.FRCControl;
013    import edu.wpi.first.wpilibj.communication.UsageReporting;
014    
015    /**
016     * @author bradmiller
017     * Handles raw data input from the FRC Kinect Server
018     * when used with a Kinect device connected to the Driver Station.
019     * Each time a value is requested the most recent value is returned.
020     * See Getting Started with Microsoft Kinect for FRC and the Kinect
021     * for Windows SDK API reference for more information
022     *
023     */
024    public class Kinect {
025        
026        private static Kinect m_instance;
027    
028         /**
029         * Gets an instance of the Kinect device
030         *
031         * @return The Kinect.
032         */
033        public static synchronized Kinect getInstance() {
034            if(m_instance == null)
035                m_instance = new Kinect();
036            return m_instance;
037        }
038    
039        /**
040         * A set of 4 coordinates (x,y,z,w) bundled into one object
041         */
042        public class Point4 {
043            public float x, y, z, w;
044    
045            public float getX() { return x; }
046            public float getY() { return y; }
047            public float getZ() { return z; }
048            public float getW() { return w; }
049    
050            public int size(){
051                return 16;
052            }
053        }
054    
055        static class header_t extends Structure {
056    
057            byte[] version = new byte[11];
058            byte players;
059            int flags;
060            float[] floorClipPlane = new float[4];
061            float[] gravityNormalVector = new float[3];
062    
063            final static int size = 44;
064    
065            header_t(Pointer backingMemory) {
066                    useMemory(backingMemory);
067            }
068    
069            public void read() {
070                backingNativeMemory.getBytes(0, version, 0, version.length);
071                players = backingNativeMemory.getByte(11);
072                flags = backingNativeMemory.getInt(12);
073                backingNativeMemory.getFloats(16, floorClipPlane, 0, floorClipPlane.length);
074                backingNativeMemory.getFloats(32, gravityNormalVector, 0, gravityNormalVector.length);
075            }
076    
077            public void write() {
078                backingNativeMemory.setBytes(0, version, 0, version.length);
079                backingNativeMemory.setByte(11, players);
080                backingNativeMemory.setInt(12, flags);
081                backingNativeMemory.setFloats(16, floorClipPlane, 0, floorClipPlane.length);
082                backingNativeMemory.setFloats(32, gravityNormalVector, 0, gravityNormalVector.length);
083            }
084    
085            public int size() {
086                return size;
087            }
088        }
089    
090        static class skeletonExtra_t extends Structure {
091            byte[] trackingState = new byte[20];
092            float[] position = new float[3];
093            int quality;
094            int trackState;
095    
096            final static int size = 40;
097    
098            skeletonExtra_t(Pointer backingMemory) {
099                    useMemory(backingMemory);
100            }
101    
102            public void read() {
103                backingNativeMemory.getBytes(0, trackingState, 0, trackingState.length);
104                backingNativeMemory.getFloats(20, position, 0, position.length);
105                quality = backingNativeMemory.getInt(32);
106                trackState = backingNativeMemory.getInt(36);
107            }
108    
109            public void write() {
110                backingNativeMemory.setBytes(0, trackingState, 0, trackingState.length);
111                backingNativeMemory.setFloats(20, position, 0, position.length);
112                backingNativeMemory.setInt(32, quality);
113                backingNativeMemory.setInt(36, trackState);
114            }
115    
116            public int size() {
117                    return size;
118            }
119        }
120    
121        static class skeleton_t extends Structure {
122            float[] vertices = new float[60];
123    
124            final static int size = 240;
125    
126            skeleton_t(Pointer backingMemory) {
127                    useMemory(backingMemory);
128            }
129    
130            public void read() {
131                backingNativeMemory.getFloats(0, vertices, 0, vertices.length);
132            }
133    
134            public void write() {
135                backingNativeMemory.setFloats(0, vertices, 0, vertices.length);
136            }
137    
138            public int size() {
139                return size;
140            }
141        }
142    
143        class header_block_t extends FRCControl.DynamicControlData {
144            byte size = 45;
145            byte id = kHeaderBlockID;
146            header_t data;
147    
148            {
149                allocateMemory();
150                data = new header_t(
151                        new Pointer(backingNativeMemory.address().toUWord().toPrimitive() + 2,
152                        header_t.size));
153            }
154    
155            public void read() {
156    
157                size = backingNativeMemory.getByte(0);
158                id = backingNativeMemory.getByte(1);
159                data.read();
160            }
161    
162            public void write() {
163                backingNativeMemory.setByte(0, size);
164                backingNativeMemory.setByte(1, id);
165                data.write();
166            }
167    
168            public int size() {
169                return 46;
170            }
171    
172            public void copy(header_block_t dest) {
173                write();
174                Pointer.copyBytes(backingNativeMemory, 0, dest.backingNativeMemory, 0, size());
175                dest.read();
176            }
177        }
178    
179        class skeletonExtra_block_t extends FRCControl.DynamicControlData {
180            byte size = 41;
181            byte id = kSkeletonExtraBlockID;
182            skeletonExtra_t data;
183    
184                    {
185                allocateMemory();
186                data = new skeletonExtra_t(
187                        new Pointer(backingNativeMemory.address().toUWord().toPrimitive() + 2,
188                        skeletonExtra_t.size));
189            }
190    
191            public void read() {
192    
193                size = backingNativeMemory.getByte(0);
194                id = backingNativeMemory.getByte(1);
195                data.read();
196            }
197    
198            public void write() {
199                backingNativeMemory.setByte(0, size);
200                backingNativeMemory.setByte(1, id);
201                data.write();
202            }
203    
204            public int size() {
205                return 42;
206            }
207    
208            public void copy(skeletonExtra_block_t dest) {
209                write();
210                Pointer.copyBytes(backingNativeMemory, 0, dest.backingNativeMemory, 0, size());
211                dest.read();
212            }
213        }
214    
215        class skeleton_block_t extends FRCControl.DynamicControlData {
216            byte size = -15;    //temporary hack for 241
217            byte id = kSkeletonBlockID;
218            skeleton_t data;
219    
220            {
221                allocateMemory();
222                data = new skeleton_t(
223                        new Pointer(backingNativeMemory.address().toUWord().toPrimitive() + 2,
224                        skeleton_t.size));
225            }
226    
227            public void read() {
228                size = backingNativeMemory.getByte(0);
229                id = backingNativeMemory.getByte(1);
230                data.read();
231            }
232    
233            public void write() {
234                backingNativeMemory.setByte(0, size);
235                backingNativeMemory.setByte(1, id);
236                data.write();
237            }
238    
239            public int size() {
240                return 242;
241            }
242    
243            public void copy(skeleton_block_t dest) {
244                write();
245                Pointer.copyBytes(backingNativeMemory, 0, dest.backingNativeMemory, 0, size());
246                dest.read();
247            }
248        }
249    
250        static final byte kHeaderBlockID = 19;
251        static final byte kSkeletonExtraBlockID = 20;
252        static final byte kSkeletonBlockID = 21;
253    
254        header_block_t m_headerData;
255        skeletonExtra_block_t m_skeletonExtraData;
256        skeleton_block_t m_skeletonData;
257        boolean m_headerValid = false;
258        boolean m_skeletonExtraValid = false;
259        boolean m_skeletonValid = false;
260        final Object m_headerDataSemaphore;
261        final Object m_skeletonExtraDataSemaphore;
262        final Object m_skeletonDataSemaphore;
263        int m_recentPacketNumber = 0;
264    
265         /**
266         * Kinect constructor.
267         *
268         * This is only called once on the first call of getInstance()
269         */
270        Kinect() {
271            m_headerData = new header_block_t();
272            m_skeletonExtraData = new skeletonExtra_block_t();
273            m_skeletonData = new skeleton_block_t();
274            m_headerDataSemaphore = new Object();
275            m_skeletonExtraDataSemaphore = new Object();
276            m_skeletonDataSemaphore = new Object();
277    
278            UsageReporting.report(UsageReporting.kResourceType_Kinect, 0);
279        }
280        header_block_t tempHeaderData = new header_block_t();
281        skeletonExtra_block_t tempSkeletonExtraData = new skeletonExtra_block_t();
282        skeleton_block_t tempSkeletonData = new skeleton_block_t();
283    
284    
285         /**
286         * Called by the other Kinect functions to check for the latest data
287         * This function will update the internal data structures
288         * with the most recent Kinect input
289         */
290        void updateData() {
291            int retVal;
292    
293            if (m_recentPacketNumber !=  DriverStation.getInstance().getPacketNumber()){
294                m_recentPacketNumber = DriverStation.getInstance().getPacketNumber();
295                synchronized (m_headerDataSemaphore) {
296                    retVal = FRCControl.getDynamicControlData(kHeaderBlockID, tempHeaderData, tempHeaderData.size(), 5);
297                    if (retVal == 0) {
298                        tempHeaderData.copy(m_headerData);
299                        m_headerValid = true;
300                    }else {
301                        m_headerValid = false;
302                    }
303                }
304    
305                synchronized (m_skeletonExtraDataSemaphore) {
306                    retVal = FRCControl.getDynamicControlData(kSkeletonExtraBlockID, tempSkeletonExtraData, tempSkeletonExtraData.size(), 5);
307                    if (retVal == 0) {
308                        tempSkeletonExtraData.copy(m_skeletonExtraData);
309                        m_skeletonExtraValid = true;
310                    }else {
311                        m_skeletonExtraValid = false;
312                    }
313                }
314    
315                synchronized (m_skeletonDataSemaphore) {
316                    retVal = FRCControl.getDynamicControlData(kSkeletonBlockID, tempSkeletonData, tempSkeletonData.size(), 5);
317                    if (retVal == 0) {
318                        tempSkeletonData.copy(m_skeletonData);
319                        m_skeletonValid = true;
320                    }else {
321                        m_skeletonValid = false;
322                    }
323                }
324            }
325        }
326    
327         /**
328         * Query the number of players detected by the Kinect
329         *
330         * @return The current number of players
331         */
332        public int getNumberOfPlayers() {
333            updateData();
334            if (!m_headerValid){
335                return 0;
336            }
337            synchronized (m_headerDataSemaphore){
338                return (int)m_headerData.data.players;
339            }
340        }
341    
342         /**
343         * Retrieve the FloorClipPlane from the Kinect device
344         *
345         * @return The FloorClipPlane
346         */
347        public Point4 getFloorClipPlane() {
348            updateData();
349            Point4 tempClipPlane = new Point4();
350    
351            if (!m_headerValid){
352                return tempClipPlane;
353            }
354    
355            synchronized(m_headerDataSemaphore){
356                tempClipPlane.x = m_headerData.data.floorClipPlane[0];
357                tempClipPlane.y = m_headerData.data.floorClipPlane[1];
358                tempClipPlane.z = m_headerData.data.floorClipPlane[2];
359                tempClipPlane.w = m_headerData.data.floorClipPlane[3];
360            }
361            return tempClipPlane;
362        }
363    
364         /**
365         * Retrieve the GravityNormal vector from the Kinect device
366         * The w value returned from this method is always 0
367         * @return The GravityNormal vector
368         */
369        public Point4 getGravityNormal() {
370            updateData();
371            Point4 tempGravityNormal = new Point4();
372    
373            if (!m_headerValid){
374                return tempGravityNormal;
375            }
376    
377            synchronized(m_headerDataSemaphore){
378                tempGravityNormal.x = m_headerData.data.gravityNormalVector[0];
379                tempGravityNormal.y = m_headerData.data.gravityNormalVector[1];
380                tempGravityNormal.z = m_headerData.data.gravityNormalVector[2];
381                tempGravityNormal.w = 0;
382            }
383            return tempGravityNormal;
384        }
385    
386         /**
387         * Query the position of the detected skeleton
388         * The w value returned from this method is always 1
389         * @return The position of the skeleton
390         */
391        public Point4 getPosition() {
392            updateData();
393            Point4 tempPosition = new Point4();
394    
395            if (!m_skeletonExtraValid){
396                return tempPosition;
397            }
398    
399            synchronized(m_headerDataSemaphore){
400                tempPosition.x = m_skeletonExtraData.data.position[0];
401                tempPosition.y = m_skeletonExtraData.data.position[1];
402                tempPosition.z = m_skeletonExtraData.data.position[2];
403                tempPosition.w = 1;
404            }
405            return tempPosition;
406        }
407    
408         /**
409         * Retrieve the detected skeleton from the Kinect device
410         *
411         * @return The skeleton
412         */
413        public Skeleton getSkeleton() {
414            updateData();
415            Skeleton tempSkeleton = new Skeleton();
416    
417            if (!m_skeletonValid){
418                return tempSkeleton;
419            }
420    
421            synchronized (m_skeletonDataSemaphore){
422                for(int i=0; i<20; i++){
423                  tempSkeleton.skeleton[i].x = m_skeletonData.data.vertices[i*3];
424                  tempSkeleton.skeleton[i].y = m_skeletonData.data.vertices[i*3+1];
425                  tempSkeleton.skeleton[i].z = m_skeletonData.data.vertices[i*3+2];
426                }
427            }
428            
429            synchronized (m_skeletonExtraDataSemaphore){
430                for(int i=0; i<20; i++){
431                    tempSkeleton.skeleton[i].trackingState = m_skeletonExtraData.data.trackingState[i];
432                }
433                switch(m_skeletonExtraData.data.trackState){
434                    case 0:
435                        tempSkeleton.trackState = Skeleton.tTrackState.kNotTracked;
436                        break;
437                    case 1:
438                        tempSkeleton.trackState = Skeleton.tTrackState.kPositionOnly;
439                        break;
440                    case 2:
441                        tempSkeleton.trackState = Skeleton.tTrackState.kTracked;
442                        break;
443                }
444    
445            }
446            return tempSkeleton;
447        }
448    }