Propeller Experiment Controller Version 1.5
I completed version 1.5 of the Propeller Experiment Controller software. I added many new features that I found were useful in my own work, or that were needed by colleagues using the Propeller Experiment Controller. I also fixed a few minor bugs. The Propeller Experiment Controller now provides good support for standard date and time formats such as "MM:DD:YYYY" and "HH:MM:SS." It now allows the user to read and write files from the SD card in any method they choose. This can be useful for creating settings files or custom data formats. The Propeller Experiment Controller can now also produce MedPC compatible data output that can be used with existing data analysis programs expecting a MedPC formatted file. Many other small changes decrease program size and increase efficiency.
The next goals for the Propeller Experiment Controller are to create more and better sample experiment programs for principles of behavior laboratories, create a C version of all spin code, and add methods to integrate the Propeller Python Interface with the powerful and free data analysis software package SciPy, and perhaps R as well.
List of Changes
- The code has been streamlined and reduced. Even after the addition of many new features, the object size has decreased slightly from 4660 to 4642 longs!
- The experimental clock now also records a day variable useful in experiments that are designed to run for several days without user intervention. The method exp.Day returns the value of the day.
- During each day, the method exp.DayTime returns the value in milliseconds of the current day. Each day this value resets to zero.
- I added methods to convert a time value in milliseconds to a 24-hour time string in the format "HH:MM:SS."
- A similar method converts a time value in milliseconds to a 12-hour time string in the format "HH:MM:SS xM" where the x is either "A" or "P."
- Strings in the format "HH:MM:SS" or "HH:MM:SS xM" can also be converted to milliseconds using another method.
- I added a sleep method that functions similarly to the pause method. The sleep method is distinct in that it shuts down cogs to decrease power consumption. The sleep method has reduced accuracy compared to pause but can be useful in battery powered applications.
- The methods exp.StartRealRandom and exp.StopRealRandom are no longer needed. When using exp.StartExperiment or one of its variants, a random number seed is quickly generated by a new cog which then shuts down and becomes free for other uses. This random number seed is then used to generate other random numbers during an experiment. The result is that random number generation is much more random than before without requiring the use of an entire cog to generate highly random numbers.
- The frequency generator methods have been changed. Now only two frequencies are generated using the counters of the cog that calls the method. I felt that the previous version of the frequency generation code was large and not particularly useful. If additional frequencies are needed. Experimental_Functions also absorbed the previously separate frequency generator object.
- The PWM methods have changed greatly but function similarly. Now after the PWM cog is launched with the exp.StartPWM method, a PWM signal can be produced on any I/O pin. All 32 I/O pins can be modulated simultaneously! You can now also specify the frequency of the modulation. This makes the PWM method suitable for frequency generation. However, at higher frequencies, the PWM methods are notably less accurate than the frequency generator methods. Finally, I added a servo control method that uses the same basic PWM techniques. This means that a single Propeller can control 32 servos!
- The object EXP_Numbers is no longer needed. The exp.ToStr and exp.ToDec have been reduced in size and completely integrated into Experimental_Functions.
- I added many new methods to communicate with the SD card. In previous versions of Experimental_Functions, the user could only communicate with the SD card using my custom data format. In Experimental_Functions_1.5, the user can now read and write files from the SD card for any purpose. This even makes it possible to save data in a novel format. SD cards can now be mounted manually with the exp.SDMountCard method and unmounted with the exp.SDUnmountCard. The user can now open files for reading, writing, appending or deleting with the exp.SDFileOpen method and close files with the exp.SDFileClose method. Single characters can be written with exp.SDWriteCharacter and strings can be written with exp.SDWriteCharacter. The user can read single characters with exp.SDReadCharacter. I added a method for reading sequences of information from an SD card, exp.SDReadSequence. This method is a little more difficult to use than the other SD card methods, but it greatly simplifies the task of acquiring sequences of data from a file. The exp.SDReadSequence method reads characters from a file until it reaches an end-of-sequence delimiter provided by the user. For example, it may read an entire sentence and only stop when it reaches a period, or it may read a series of numbers and only stop when it reaches a comma. The sequence is then saved as a string in a user provided array.
- The method exp.StopExperiment is no longer needed.
- The methods exp.PrepareSeperateRawDataOuput and exp.PrepareCustomOutputForRawData have been removed. I found the number of prepare data output methods to be cumbersome, so I removed these methods and improved exp.PrepareDataOutputForRawData to provide the same features as the removed methods.
- I added methods to produce MedPC compatible data output. The methods exp.SaveMPCData and exp.SaveRawMPCData provide all the same information the default format but are compatible with scripts that expect data in a MedPC format. The MedPC format is not aesthetically pleasing, but allows existing programs to analyze data collected by the Propeller Experiment Controller.
- I added a few methods to help the user create and manage data arrays. The array methods use some space that Experimental_Functions reserves for processing data to the SD card at the end of a session, so these methods are a useful way to collect large amounts of data without reserving more program space. I have used these methods to record and analyze data during the course of an experimental session. For example, in one fixed interval program I used the array methods to record every response onset during a trial and then sort them into bins for later analysis.
- Previously, Experimental_Functions was limited to saving 1000 instances of a single event type in a spreadsheet. Although many thousands of instances can be recorded, there is a limit to how many events can be processed and saved in a spreadsheet due to the memory size of the Propeller. This limit is now adjustable. The user can change the constant MaximumInstances increase (or decrease) the number of instances of an event that can be saved in a data spreadsheet. If a program is relatively small, free space is available on the Propeller to process extra instances of events. If a program is instead very large, the constant MaximumInstances can be decreased to provide more room for the main program.
- Experimental_Functions now saves a version number in the memory files. Future versions can read this version number to determine how memory files should be processed. This makes updates and backwards compatibility easier to implement.
- The code has been streamlined and reduced. The object size has decreased from 448 longs to 216 longs!
- I found and corrected a very rare bug in the detect/debounce methods that sometimes occurs when wires rub together or an object is on the edge of an infrared beam. It was a very difficult bug to replicate, and so it took many hours to correct. The entire detect/debounce methods were overhauled. It was a lot of work but I am happy to say that the detect/debounce methods are much more efficient now.
- I removed the detect_nodebounce method. Just set the debounce to 0 if you don't want to use any debounce time.
- I removed the methods related to the Parallax Quickstart. They did not seem to be useful and the touchpads are very fickle. You can still use the old code found in version 1.4 if needed.
- Inputs, outputs and manual events all now use the same four state system (off, onset, on, offset). There are a few reasons for this change. In general the change makes outputs and manual events more powerful and easy to use. However, some consideration is needed to update experiment programs. I apologize if any modifications are required, but this update useful and easier in the long term. For an example of the differences, see the changes between the sample experiment programs from version 1.4 to version 1.5.
- The number of unique events in an experiment has been reduced from 203 to 151 to address compatibility issues. Although this appears as if a downgrade, it is a necessary feature. Experimental_Functions and the Propeller Python Interface have also been updated to accommodate this change. If this limitation is too great, future versions may use a two character ID code for events instead of the current one character ID code. A two character ID code would allow for 22801 (151 * 151) unique events.
- I created a general purpose object to keep track of dates. I am not in love with the name "Date.spin" but it seems to describe the purpose adequately. This object is not required by or dependent on Experimental_Functions or Experimental_Event. However, it does work well with Experimental_Functions new methods to convert integer and string time formats. The date object allows users to save a date in the object's variable space. The user can then change or read the entire date, or only change or read the month, day, or year. The user can also add or subtract days from the current date. The date object can determine the day of the week using Zeller's congruence algorithm, report the number of days in the current month, and determine if the current year is a leap year. I have found this object to be useful in experiments that run for many days, as a date can be saved in the data file for each day.
Frequency Generator Object
- I created an additional frequency generator object titled "EXP_FrequencyGenerator" to supplement Experimental_Functions's frequency generator methods. This object is optional, but I found this was useful in uncommon cases where multiple high frequencies needed to be generated. For lower frequencies the PWM methods in Experimental_Functions are usually adequate. The methods in the frequency generator object work identically to the methods found in Experimental_Functions.
- To use the frequency generator object, import the it in the OBJ block of the program, then use the StartFrequencyGenerator method in a new cog. Like the Experimental_Functions methods, the frequency generator uses the counter modules of the cog that launches it. This means you cannot use multiple instances of this object in the same cog, nor can you use Experimental_Functions's frequency generator and this object in the same cog. You can use separate objects for each cog to generate additional frequencies if needed. The basic frequency generator code is very simple and, for the most part, the counters run in the background, so a cog that uses these methods can often simultaneously be used for other tasks.
Propeller Python Interface
- The code has been updated to accommodate some of the changes in Experimental_Functions and Experimental_Event.
- The format of the input settings file has been changed from a text document to a .csv spreadsheet. It is now much easier to use.
- I have NOT yet tested the Propeller Python Interface in detail across multiple computer operating systems. Sorry! I will get to it as soon as possible. In the mean time I did not want this testing to hold up the official release of version 1.5 any longer.