May 232011
 

Well, I completed my first functional program. It is severely limited, but it is able to successfully connect to and access data from a ThinkGear device.

ThinkGear Interface Test v0_1.zip

This program requires you to specify the COM port your ThinkGear is attached to, and assumes that it is at the default baud rate (9600). It seemed a good idea to ensure I could get the data, before I worried about scanning COM ports and baud rates.

The ThinkGearDevice class is my wrapper for the ThinkGear API. The current structure does not handle all the functions I want or provide to much value over the actual API. The main gain is the encapsulation of the connectionID inside the class, and the type safety added through the DataType enumeration.

The newer DLL from NeuroSky offers an automated read loop, however this loop doesn’t offer any notification or callbacks, and renders the value status methods useless. I decided to create my own ReadLoop in Java to check for valid packets once a millisecond and notify any waiting Threads. I’d like to modify this to follow more of an Observer Pattern in the future, but for now I wanted some basic functionality to release.

To ensure that the native resources are freed, the object is currently finalizable, the problem with this is that an unfinalized ThinkGearDevice may hold a connection to an active headset, blocking the device until the finalization queue of the JVM catches up. The best solution I can find is to switch to weak references to dispose of the objects on a faster time table.

I chose to use a loop to display the debug data so that the GUI will not have to change in order to display any new data types. The unfortunate side effect of my choice is that it pulls all values on every loop, rather than only those whose status updated. Since this is debug data, I’ll leave it as is until some the graphical elements take over.

In the next iteration I’m going to focus on introducing weak references, enforcing the max connection count on the Java side and , if there is time, adding support for the observer pattern.

Let me know if anyone has some good ideas, otherwise I’ll post up some more code soon!

  20 Responses to “ThinkGear in Java: First Program”

  1. Ey! congratulations because it will be a great project. Im very interesting with you project, Im Computer Science student in Mexico. I would like to talk to you about it but I cant see any email of you… Can you gime any email please?

  2. Hi David

    Your program is excellent! I have been trying to modify it so that it will save the results to a file
    (not just for me but to share with anyone interested).

    Using C# I was able to modify an open source program to do so, but it only saves
    100 lines per second, while it looks like your program would save 512 lines per second.

    Unfortunately I am quite new to Java and have only succeeded in saving one line to a file,
    any advice you could give would be most appreciated!

    Thanks!!

    Katie

    • Hello Katie,

      Since it has been quite a while since this project, I don’t even have a 32-bit JRE installed at the moment! Give me a little while to get things setup for this again, and I’ll set up an example for you based off this Think Gear program, but here is the short of it:

      Have the MainWindow class store a reference to a PrintStream object. Populate it with a FileOutput object in a BufferedOutput object in a PrintStream object. Each time you have text to write, pass it to the PrintStream.print() method.

      For a cleaner implementation, I’d recommend a specialized output object that takes the ThinkGear data and formats it as desired before writing it to an internally referenced PrintStream. This would help keep all the nasty formatting code out of your other classes, and makes everything easier to follow.

      I hope that gives you enough of a start, until I can get an example out there.

      Let me know if you need any more info!

      • Hi David

        Thanks so much!

        My problem was that I had put

        bufferedWriter = new BufferedWriter(new FileWriter(myFile, true))

        without the true, so it kept overwriting. I’ll try and have a more finished version
        done and posted this weekend. If you could have a look at it at some point
        for advice, I would most appreciate it! It will be basically your code, with a little
        bit extra for the file report.

        Many thanks, and will keep you posted.

        Katie

        private void displayValues(int[] values) {

        StringBuilder output = new StringBuilder(“”);

        int i = 0;
        for (DataType type : DataType.values()) {
        //output.append(type.toString());
        //output.append(“=”);
        output.append(values[i++]);
        //output.append(“\n”);
        output.append(“,”);
        }
        debugArea.setText(output.toString());

        //System.out.print(outputFile.toString());
        //String strLine = “”;
        //for(int n = 0; n < 9; n++)
        //{

        Date myTime = new Date();
        String myLine = myTime + "," + output.toString(); // don't need \n because of bufferedWriter.newLine();
        //System.out.print(myLine);

        File myFile=new File("c:" + File.separator + "Mindwave" + File.separator + "Mindwave.txt"); // File("c:/Mindwave/Mindwave.txt")
        BufferedWriter bufferedWriter = null;

        try{
        //Construct the BufferedWriter object
        bufferedWriter = new BufferedWriter(new FileWriter(myFile, true)); // True so that info does not overwrite
        //Start writing to the output stream
        bufferedWriter.append(myLine);
        bufferedWriter.newLine();

        } catch (FileNotFoundException ex) {
        ex.printStackTrace();
        } catch (IOException ex) {
        ex.printStackTrace();
        } finally {
        //Close the BufferedWriter
        try {
        if (bufferedWriter != null) {
        bufferedWriter.flush();
        bufferedWriter.close();
        }
        } catch (IOException ex) {
        ex.printStackTrace();
        }
        }

        • Is that what did it. I wasn’t even thinking of that flag, as it normally wouldn’t be an issue.

          Since you are constructing your Writers inside the displayValues routine, your program is required to re-open the file handle and make new buffers for each line of output, which is what was generating your problem.

          One very big improvement you could make would be to move the instantiation of the Writers to some point outside (perhaps when logging is started) and store it in a private variable near the top of the class. This would free the displayValues routine to execute only the write (bufferedWriter.append(myLine); bufferedWriter.newLine();). The result would be faster, cleaner code, with less opportunity for IO errors.

          I’ll still put together my own approach to this, though. I’ll take my time, now that I know you have a basic solution. I’ll make a version with UI changes and CSV formatting routines in a separate class.

          • Hi David

            I’m so glad you will be working on this, as this is beyond my expertise. My original version was collecting about 300 lines per second (which though not as good as the 512 per second, would still be acceptable). But now that I’ve tried to improve it (I added a “Save to File” and “Stop Saving to File” buttons, and a few other changes) it is runny very slowly and only collecting about 4 lines per second. I tried to follow your advice, but now that I re-read what you wrote, I realize that I completely misunderstood it and will have to try again.
            If you are interested, here are my two versions:

            https://www.dropbox.com/sh/03oupeisfyt515h/AAC57o-F4dVn6USttUApCRnia?dl=0

            There is absolutely no rush, but thank you so much for working on this. I have scoured the internet and and not been able to find any program that downloads Mindwave readings that is easy to use and that downloads at least 200-300 lines per second.

            Best Regards,

            Katie

  3. Hi David

    Great news, now it works! Over 500 lines per second. I ended up adding the output to an Arraylist and then, once the stop button is clicked, saving the info from the Arraylist to a file. Please let me know what you think when you have a chance. This would not have been possible without your wonderful thinkgear wrapper!

    https://www.dropbox.com/sh/03oupeisfyt515h/AAC57o-F4dVn6USttUApCRnia?dl=0

    in the “ThinkGear-Interface-Output-500” folder.

    Best,

    Katie

    • That is definitely a notable improvement! By separating your processing thread from the slower IO process you have avoided having your code check back too late to pick up the next packet. Unfortunately, your code suffers from a major issue that will limit the amount of time for which you can collect data without stopping to purge your memory.

      Java programs run within a Java Virtual machine (JVM) which is allocated a fixed amount of memory at start up. Most JVMs are incapable of swapping memory to hard disk to expand memory availability at the cost of speed, as such when you run out of memory, that’s all she wrote. Worse yet, since the memory amount is fixed, it is generally FAR smaller than your system’s total physical memory.

      I can think of two ways to work around this issue. The first is Non-blocking IO, but it can be more complicated for programmers who are not familiar with it, so I personally favor another approach.

      In my upcoming example I have modified the DataCollector class to also write the data to an area of memory, as you did, but created a separate processing Thread which gathers the contents of that storage and writes it to disk while the other thread is busy trying to keep up with the ThinkGearDevice. This accomplished the same throughput (about 500 packets a second) without limitations to the run time (with the exception of storage too slow to allow the data queue to be emptied at an acceptable rate).

      The storage Class I used is specifically designed for these types of multi –threaded, First-in First-Out (FIFO) operations. It is called “java.util.concurrent.ArrayBlockingQueue”. I provided it a fixed size for the internal array, and used the Non-blocking (doesn’t stop to wait for space) “add” method to place data in the queue. This means if the queue is full the packet will be lost, but the program will still return to service the next packet from the ThinkGearDevice. On the file writing side of things, I used the queue’s “take” method. This method will sit and wait until there is data to get from the queue, effectively pausing the writing thread until there is work for it to do.

      I’m trying to put a user interface on my changes now, and will post them up in the near future, but don’t quit trying! You are learning a lot about Java from this experiment, and nothing sticks with you like information you dig for!

      • Hi David

        You’re right, I did think that the physical memory of most computers would be plenty, thanks for the heads up about the JVM, I had no idea. Your solution sounds perfect, and I’ll try to learn about threading, and even having two threads communicating, which is pretty high end programming.

        “…and nothing sticks with you like information you dig for!” Ha, this has been a very educational project! I’d kind of forgotten how much fun programming can be.

        And thank you for all your help!

        Katie

        • Hi David

          Well I can see how the concurrent.ArrayBlockingQueue could definitely work. I setup a test program whereby the “Producer” puts into the queue: (System.currentTimeMillis() + ” ” + i) with a sleep time of (1). This then gets picked up by the Consumer and saved to a file using this method:

          http://stackoverflow.com/questions/16237546/writing-to-console-and-text-file

          And was able to save over 900 lines per second, more than enough!

          Unfortunately the hard part is getting to actually work with the Mindwave wrapper. I was hoping to copy some of your code to the Producer so that it can pickup the packets directly from the headset, but since the inner workings of your wrapper is way beyond me I’ve gotten stuck. Very much looking forward to seeing how you tackle this issue!

          Bye for now,

          Katie

  4. Hi David

    Success! Now it saves to over 500 lines per second. Way back in February you had recommended exactly what ended up working:

    “One very big improvement you could make would be to move the instantiation of the Writers to some point outside (perhaps when logging is started) and store it in a private variable near the top of the class. This would free the displayValues routine to execute only the write (bufferedWriter.append(myLine); bufferedWriter.newLine();). The result would be faster, cleaner code, with less opportunity for IO errors.”

    At the time I moved the file creation, which helped a lot, but I should have read you posting more carefully and also moved the bufferedWriter. Now that I’ve more carefully followed you advice, it works beautifully.

    Here’s a link to the finished version which I hope to shortly post to an open-source website.

    https://www.dropbox.com/sh/5a5psn1p1pi9q90/AACdrUzjsznSCHDSHuTDNXxBa?dl=0

    Please let me know if there are any changes/improvements that you would recommend, and than you again for your excellent driver and especially all your help!

    Katie

  5. Hi!
    It seems like a very good program and I think I could use it for my Mindwave headset. Can you please tell me how to run it?

  6. How can we connect multiple neurosky to the thinkgear?

  7. Hi David

    Just wanted to let you know I have a second version of the Mindwave Reader 512. The new version saves only the Time, Raw, Attention, Meditation. And it automatically deletes the first and last lines, as they tend to only partially save.

    https://sourceforge.net/projects/mindwavereader512/files/

    As for analyzing the data, that is way over my head, so I hired Nayuki of The Nayuki Project to write the Brainwave Analyzer, which is open source and free and available at:

    http://brainwaves.io/wp/

    Hope everything is going very well with you. And thank you for your wonderful Thinkgear wrapper and your patient help, my projects would not have been possible without your help!

    Katie

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)

This site uses Akismet to reduce spam. Learn how your comment data is processed.