Package it.hakvoort.nia

Source Code of it.hakvoort.nia.NiaDevice2$NiaDataReader

package it.hakvoort.nia;

import it.hakvoort.usb.UsbDeviceManager;

import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.usb.UsbConfiguration;
import javax.usb.UsbDevice;
import javax.usb.UsbEndpoint;
import javax.usb.UsbException;
import javax.usb.UsbInterface;
import javax.usb.UsbInterfacePolicy;
import javax.usb.UsbPipe;

/**
*
* @author Gido Hakvoort (gido@hakvoort.it)
*
*/
public class NiaDevice2 {
 
  public static final int ATTEMPTS = 5;
 
  public static final short VENDOR = (short) 0x1234;
  public static final short DEVICE = (short) 0x00;
 
  public static final byte ENDPOINT_IN = (byte) 0x81;
  public static final byte ENDPOINT_OUT = (byte) 0x01;
 
  // the size of Nia's internal buffer (this is the maximum number of sample NIA gets behind)
  public static final int INTERNAL_BUFFER_SIZE = 32;
 
  // internal sample rate of Nia in Hz
  public static final int SAMPLE_RATE = 3906;
 
  // preferred sample rate of the NiaDevice.
  // every SAMPLE_RATE/sampleRate clock cycles a new sample is read and send to all listeners
  private int sampleRate = 512;
 
  // listeners waiting for samples
  protected List<NiaListener> listeners = new CopyOnWriteArrayList<NiaListener>();
 
  // the ocz nia usb device
  private UsbDevice device;
 
  // the deviceReader, reading the data and puts data in the queue.
  private NiaDeviceReader deviceReader = new NiaDeviceReader();
 
  // the dataReader, notifying the listeners about new data
  private NiaDataReader dataReader = new NiaDataReader();
 
  // if the device is connected
  private boolean connected = false;
 
  // if the samples are signed
  private boolean signed = true;
 
  // the number of connection attempts
  private int attempt = 0;
 
  // a queue with contains all incoming (and missing) samples
  private ConcurrentLinkedQueue<NiaSample> samples = new ConcurrentLinkedQueue<NiaSample>();
 
  public NiaDevice2() {
    init();
  }
 
  public void init() {
    try {
      UsbDeviceManager manager = UsbDeviceManager.getInstance();
      this.device = manager.getDevice(NiaDevice2.VENDOR, NiaDevice2.DEVICE);
    } catch (UsbException e) {
      e.printStackTrace();
    }
  }
 
  /*
   * Start the NiaDecive, first a connection is being made after which the read and data threads are started.
   */
  public void start() {
    while(!connected && attempt < ATTEMPTS) {

      UsbConfiguration usbConfiguration = this.device.getUsbConfiguration((byte) 1);
      UsbInterface usbInterface = usbConfiguration.getUsbInterface((byte) 0);
     
      try {
        usbInterface.claim(new UsbInterfacePolicy() {
         
          public boolean forceClaim(UsbInterface arg0) {
            return true;
          }
        });
       
        connected = true;
        continue;
      } catch (UsbException e) {
       
      }

      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {}
    }
   
    if(connected) {
      System.out.println(String.format("Connected to NIA device."));

      dataReader.start();
      deviceReader.start();
    } else {
      System.err.println(String.format("Could not connect to NIA device. Stopped after %s attemps", attempt));
    }
  }
 
  /*
   * Stop the NiaDecive from further processing new data
   */
  public void stop() {
    connected = false;
  }
 
  public void setSampleRate(int sampleRate) {
    this.sampleRate = sampleRate;
  }
 
  public int getSampleRate() {
    return this.sampleRate;
  }
 
  public boolean isConnected() {
    return this.connected;
  }
 
  public boolean getSigned() {
    return signed;
  }
 
  public void setSigned(boolean signed) {
    this.signed = signed;
  }
 
  public void addListener(NiaListener listener) {
    listeners.add(listener);
  }
 
  public void removeListener(NiaListener listener) {
    listeners.remove(listener);
  }
 
  public void removeAllListeners() {
    listeners.clear();
  }
 
  public List<NiaListener> getListeners() {
    return listeners;
  }
 
  /**
   * Send a sample to all listeners
   */
  public void fireReceivedSample(NiaSample sample) {
    for(NiaListener listener : listeners) {
      listener.receivedSample(sample);
    }
  }
 
  /**
   * NiaDeviceReader, reads data from the OCZ NIA and stores it in a temporary buffer.
   * The buffer will be converted into multiple NiaSamples and all samples are placed in a Queue for further processing.
   */
  private class NiaDeviceReader extends Thread {
   
    // the buffer for incoming data
    private byte buffer[] = new byte[55];
   
    // the offset of the incomming samples
    private int offset = 0;
   
    // the sample counter
    private int counter = 0;
   
    // the mis counter, keep track of how many samples the reader is behind
    private int pMiscount = 0;
   
    // the hit counter, keep track of how many samples are read by the device
    private int pHitcount = 0;
 
    public NiaDeviceReader() {

    }
   
    public void run() {
      UsbConfiguration usbConfiguration = device.getUsbConfiguration((byte) 1);
      UsbInterface usbInterface = usbConfiguration.getUsbInterface((byte) 0);
     
      UsbEndpoint endpoint = usbInterface.getUsbEndpoint(NiaDevice2.ENDPOINT_IN);
      UsbPipe usbPipe = endpoint.getUsbPipe();
     
      try {
        usbPipe.open();
      } catch (UsbException e) {
        e.printStackTrace();
      }
     
      boolean insync = false;
           
      while(connected) {
        try {
          usbPipe.syncSubmit(buffer);
         
          // get the number of samples
          byte nSamples = buffer[54];
 
          // fetch the total number of hits, divided over 2 bytes (litle endian)
          int hitcount = ((buffer[53] & 0xFF) << 8) | (buffer[52] & 0xFF);
 
          // fetch the total number of misses, divided over 2 bytes (litle endian)
          int miscount = ((buffer[51] & 0xFF) << 8) | (buffer[50] & 0xFF);
         
          // calculate delta hitcount, if hitcount is smaller than pHitcount, there was an overflow
          // (use 0x10000 instead of 0xFFFF), you need to take zero in account!
          int dHitcount = (hitcount < pHitcount) ? ((hitcount + 0x10000) - pHitcount) : (hitcount - pHitcount);
         
          // calculate delta miscount, if miscount is smaller than pMiscount, there was an overflow
          // (use 0x10000 instead of 0xFFFF), you need to take zero in account!
          int dMiscount = (miscount < (pMiscount-16)) ? ((miscount + 0x10000) - pMiscount) : (miscount - pMiscount);
         
          // store last miscount
          pMiscount = miscount;
         
          // store last hitcount
          pHitcount = hitcount;
         
          // wait until synchronized with the NIA
          if(nSamples < 16 && dMiscount == 0) {
            insync = true;
          }
         
          // TODO: what if never insync?
          if(!insync) {
            continue;
          }
         
          // update the offset
          offset += dMiscount;
         
          // update cycle counter
          counter += dHitcount;

          // report missing samples
          for(int i=0; i < (offset - (offset%32)); i++) {
           
            // get sample number
            int number = ((counter - offset) - nSamples) + i;
         
            // add missing sample value to the data buffer
            synchronized(samples) {
              NiaSample sample = new NiaSample(number, 0);
              sample.reportMissing();
              samples.add(sample);
               
              try {
                samples.notifyAll();
              } catch(IllegalStateException e) {}             
            }
          }
         
          // update offset, don't fall to far behind
          offset %= INTERNAL_BUFFER_SIZE;
         
          // fetch the samples, each sample is divided over 3 bytes, little endian
          for(int i=0; i < nSamples; i++) {
           
            // get sample number
            int number = ((counter - offset) - nSamples) + i;
           
            // get sample value
            int value = (buffer[i*3] & 0xFF) | ((buffer[i*3+1] & 0xFF) << 8) | ((buffer[i*3+2] & 0xFF) << 16);
           
            // according to the HID information of the device, sample are between -8388608 and 8388607
            // meaning the sample has a sign bit and is in two's complement.
            if(signed && ((value & 0x800000) != 0)) {
              value = ~(value ^ 0x7fffff) + 0x800000;
            }
           
            synchronized(samples) {
              samples.add(new NiaSample(number, value));
             
              try
                samples.notifyAll();
              } catch(IllegalStateException e) {}
            }
          }
        } catch(UsbException e) {
          e.printStackTrace();
        }       
      }
     
      try {
        usbPipe.close();
        usbPipe.getUsbEndpoint().getUsbInterface().release();
      } catch(UsbException e) {
        e.printStackTrace();
      }
 
      System.out.println(String.format("NiaDeviceReader: No longer connected, stopped reading data from the device."));
    }   
  }
 
  /**
   * NiaDataReader, fetches the first NiaSample from the queue and sends it to all listeners.
   */
  private class NiaDataReader extends Thread {

    // number of samples we should wait for sending next sample
    private double interval = 0;
   
    private double nextInterval = 0;
   
    public void run() {
     
      interval = (double) SAMPLE_RATE / (double) sampleRate;
     
      while(connected) {
        synchronized(samples) {
          while(samples.isEmpty()) {
            try {
              samples.wait();
            } catch(InterruptedException e) {
             
            }
          }
         
          while(!samples.isEmpty()) {
            NiaSample sample = samples.poll();
           
            if(sample.number >= nextInterval) {
              fireReceivedSample(sample);             
              nextInterval += interval;
            }
          }
        }   
      }
    }
  }
}
TOP

Related Classes of it.hakvoort.nia.NiaDevice2$NiaDataReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.