scripts/channelClasses/mock_maze/Simple.java

/////////////////////////////////////////////////////////////////
// This script generates (somewhat) realistic MAZE data.
//
// Run with (for example)
//    java -cp build:lib/derby.jar -Dserver.cache.enable=false -Djava.library.path=c frontendClasses/CLI -scriptChannel mock_maze/Simple -reviewSeries -paradigm maze -binH stim -binZ site -display "TiledStack()" -v
/////////////////////////////////////////////////////////////////
package mock_maze;

import java.io.*;
import java.util.*;

import epochClasses.*;
import generalClasses.*;
import recordingClasses.Recording;
import seriesClasses.*;
import seriesClasses.seriesGeneration.Erlang;
import channelClasses.ChannelScript;
import static channelClasses.Channel.*;

/////////////////////////////////////////////////////////////////
/** This script generates (somewhat) realistic MAZE data.
 * The EEG contains various events.
 *
 * <p>The intended effect is:
 * <ol><li>Demonstrate event generation</li>
 * </ol>
 */
public class Simple extends ChannelScript
{
    /** Recording instance to be operated on */
    Recording rec = null;
    /** Time series */
    ArrayList<SeriesAnalog> list = new ArrayList<SeriesAnalog>();
    /** Events.  May be left empty */
    ArrayList<Event> ev = new ArrayList<Event>();

    ////////////////////////////////////////////////////////////////////
    /** Initialize instance by setting its parameters to default values.
     */
    public Simple(Recording rec) {
        this.rec = rec;
    } // Simple


    ////////////////////////////////////////////////////////////////////
    /** Update recording data by performing channel-oriented operations.
     */
    public void update() {
        // Template - used to encapsulate all sampling characteristics
        float x0 = 0.0f;             // in seconds: times start at x0
        float xDelta = 0.004f;       // in seconds: times increment by xDelta
        float duration = 198.2f;     // in seconds: times end at x0+duration
        int nIndexes = Math.round(duration/xDelta);
        SeriesAnalog template = new SeriesAnalog(new SiteSet(), // sites
                                                 x0,            // x0
                                                 xDelta,        // xDelta
                                                 new Units(Unit.s),  // xUnits
                                                 nIndexes,      // # samples
                                                 new Units(Unit.uV), // yUnits
                                                 DataMode.EEG); // DataMode

        // Channels 
        String[] labels = {"Fz", "C3", "Cz", "C4", "Pz", "ECG"};

        // Standard ERP; update event list also
        SeriesAnalog erp = getEegWithErps(template);

        // Generate time series
        for(int site=0; site<labels.length; site++) {    
            // What modality?
            DataMode mode = DataMode.EEG;
            if(labels[site].matches("[eE][oO][gG].*"))       mode=DataMode.EOG;
            else if(labels[site].matches("[eE][cCkK][gG].*"))mode=DataMode.ECG;

            // New series
            SeriesAnalog sum = null;

            if(mode.equals(DataMode.EEG)) {
                // Create sum of noise and ERPs
                float t0 = 0.09f;
                sum = getEegWithAlpha(template, t0);
                sum.add(erp);
            } else if(mode.equals(DataMode.ECG)) {
                sum = getEcg(template);
            }
            sum.setSites(new SiteSet(new Site(labels[site])));
            sum.setMode(mode);
    
            // Append sum to result
            list.add(sum);
        }

        // Add synthetic time series to the currently empty Recording
        replaceAllSeries(rec, list);
        replaceAllEvents(rec, ev);
    } // update


    ////////////////////////////////////////////////////////////////////
    /** Dump summary of this class or object
     * @return String representation of this object
     */
    public String toString() {
        String s = "<<<"+this.getClass().toString()+">>>\n";
        return s;
    } // toString


    ////////////////////////////////////////////////////////////////////
    /** Generate pseudo EEG containing ERPs.  Also update the event list, ev.
     * The event labels must match those in SeriesBinary.buildXXX_NS5Event().
     * The ERP is about +15 uV in amplitude.
     */
    private SeriesAnalog getEegWithErps(SeriesAnalog template) {
        float erpDuration = 1.0f;
        float xDelta = template.getXDelta(); // sampling interval, in seconds
        int nIndexes = Math.round(erpDuration/xDelta);
        if((nIndexes&1)==0) nIndexes++;
        float x0 = -(nIndexes-1)*xDelta/2;  // so wavelet is centred on 0 secs
        SeriesAnalog wavelet = new SeriesAnalog(new SiteSet(), // sites
                                                x0,            // x0
                                                xDelta,        // xDelta
                                                new Units(Unit.s),  // xUnits
                                                nIndexes,      // # samples
                                                new Units(),   // yUnits
                                                DataMode.EEG); // DataMode
        float lnorm = -1.0f;
        SeriesAnalog erp =SeriesAnalog.getModelledErpTimeseries(wavelet,lnorm);

        // Generate multiple impulses, y[], with modulated areas, plus events
        String[] stim = {
            "Init",                                               // initialize
            "Restart",                                            // attempt 1
            "Wrong", "Wrong", "Wrong", "OK", "Wrong", "Wrong", "OK", "Wrong",
            "Wrong", "Wrong", "OK", "OK", "OK", "OK", "OK", "Wrong", "OK",
            "Wrong", "Wrong", "Wrong", "OK", "Wrong", "Wrong", "Wrong","Wrong",
            "Wrong", "Wrong", "Wrong", "Wrong", "OK", "Wrong", "Wrong","Wrong",
            "Wrong", "Wrong", "Wrong", "Wrong", "Wrong", "OK", "OK", "Wrong",
            "OK", "Wrong", "OK", "Wrong", "OK", "OK", "OK", "OK", "Wrong",
            "Wrong", "Wrong", "Wrong", "Wrong", "Wrong", "OK", "Wrong", "OK",
            "OK", "Wrong", "OK", "OK", "OK",
            "Restart",                                            // attempt 2
            "Wrong", "OK", "Wrong", "Wrong", "OK", "Wrong", "Wrong", "Wrong",
            "OK", "OK", "OK", "OK", "OK", "Wrong", "OK", "OK", "Wrong","Wrong",
            "Wrong", "Wrong", "Wrong", "OK", "OK", "Wrong", "OK", "OK", "OK",
            "OK", "OK", "OK", "OK", "Wrong", "OK", "OK", "OK", "OK", "OK","OK",
            "Restart",                                            // attempt 3
            "Wrong", "OK", "OK", "Wrong", "OK", "OK", "OK", "OK", "OK", "OK",
            "OK", "Wrong", "OK", "OK", "Wrong", "OK", "OK", "OK", "OK", "OK",
            "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK",
            "Restart",                                            // attempt 4
            "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "Wrong","OK",
            "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK",
            "OK", "OK", "OK",
            "Restart",                                            // attempt 5
            "OK", "OK", "OK", "OK", "OK", "OK", "Wrong", "OK", "OK", "OK",
            "Wrong", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK","OK",
            "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK",
            "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK",
            "OK", "OK", "OK", "OK", "OK", "OK", "OK",
            "Restart",                                            // attempt 6
            "OK", "OK", "OK", "OK", "OK", "OK", "Wrong", "OK", "OK", "OK","OK",
            "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK",
            "OK", "OK", "OK",
            "Restart",                                            // attempt 7
            "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK",
            "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK",
            "OK", "OK",
            "Restart",                                            // attempt 8
            "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK",
            "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK", "OK",
            "OK", "OK",
            "Done"                                                // complete
        };
        double[] time = {        
            6.018,                                                // initialize
            7.214,                                                // attempt 1
            8.388,   9.142,   9.810,  10.526,  11.184,  11.736,  12.292,  13.012,
            14.218,  14.728,  15.068,  15.658,  16.306,  16.868,  17.290,  17.680,
            18.220,  18.736,  19.136,  19.536,  20.164,  20.922,  21.400,  22.042,
            22.332,  22.784,  23.384,  23.820,  24.824,  25.642,  26.214,  27.056,
            27.480,  28.200,  28.902,  29.186,  30.038,  30.658,  31.566,  32.056,
            32.590,  33.196,  33.808,  34.596,  35.032,  35.572,  35.974,  36.414,
            36.834,  37.252,  37.844,  38.578,  39.010,  39.800,  40.440,  40.872,
            41.628,  42.068,  42.464,  42.890,  43.290,  43.792,  44.322,
            48.664,                                               // attempt 2
            49.456,  50.208,  50.728,  51.298,  51.700,  52.246,  53.462,  53.852,
            54.346,  54.984,  55.318,  55.650,  56.114,  56.782,  57.278,  57.810,
            58.248,  59.522,  60.370,  60.878,  61.740,  62.220,  62.598,  63.338,
            64.056,  64.428,  64.810,  65.566,  65.886,  66.196,  66.622,  67.748,
            68.248,  68.638,  69.698,  70.194,  70.742,  71.342,
            75.684,                                               // attempt 3
            76.174,  76.752,  77.898,  78.454,  78.950,  79.366,  79.636,  79.922,
            80.334,  81.130,  81.594,  81.978,  83.552,  84.116,  85.186,  85.688,
            86.162,  86.482,  87.438,  87.890,  88.174,  88.508,  89.370,  89.666,
            90.410,  90.824,  91.358,  91.654,
            95.994,                                               // attempt 4
            97.844,   98.698,   99.006,   99.576,   99.882,  100.134,  100.394,
            101.252,  101.584,  101.880,  102.740,  103.206,  104.458,  104.926,
            105.238,  106.036,  106.308,  106.974,  107.282,  108.018,  108.300,
            108.572,  109.002,  109.430,  109.884,
            114.224,                                              // attempt 5
            115.310,  116.210,  116.952,  117.702,  117.996,  118.554,  119.136,
            119.684,  120.096,  120.368,  120.772,  121.398,  121.814,  122.402,
            122.816,  123.098,  123.902,  124.184,  124.528,  124.780,  125.838,
            126.128,  126.482,  126.900,  127.688,  128.382,  133.458,  134.198,
            134.456,  135.098,  135.824,  136.358,  136.698,  137.602,  137.858,
            138.342,  138.738,  139.328,  139.828,  140.104,  140.970,  141.578,
            142.028,  142.304,  143.064,  143.342,  143.866,  144.222,  144.732,
            145.064,
            149.402,                                              // attempt 6
            150.342,  150.978,  151.232,  151.610,  151.908,  152.188,  152.618,
            153.282,  153.746,  154.192,  154.604,  154.874,  155.404,  155.858,
            156.290,  156.888,  157.304,  157.908,  158.220,  158.586,  158.864,
            159.164,  159.546,  160.168,  160.456,
            164.802,                                              // attempt 7
            165.166,  165.546,  166.066,  166.432,  166.700,  167.288,  167.776,
            168.592,  168.852,  169.818,  170.216,  170.790,  171.216,  171.916,
            172.346,  172.626,  173.034,  173.300,  173.756,  174.010,  174.510,
            174.868,  175.336,  175.912,
            180.252,                                              // attempt 8
            180.856,  181.294,  181.600,  181.924,  182.272,  182.566,  184.068,
            184.702,  184.996,  185.420,  185.880,  186.176,  186.592,  187.228,
            187.806,  188.258,  188.536,  188.828,  189.318,  189.796,  190.110,
            190.458,  190.860,  191.564,
            192.014                                               // complete
        };
    
        int nEvents = stim.length;
        assert (time.length == nEvents): "In mock_maze/Simple.java: "+
            "stim[] and time[] have mismatching lengths";
    
        float[] y = new float[template.getNIndexes()];
        for(int i=0; i<nEvents; i++) {
            String label = null;
            float scale = 0;
            if(stim[i].equalsIgnoreCase("Init")) {
                label = "Init";
                scale = 0.0f;
            } else if(stim[i].equalsIgnoreCase("Done")) {
                label = "Done";
                scale = 0.0f;
            } else if(stim[i].equalsIgnoreCase("OK")) {
                label = "OK";
                scale = 1.0f;
            } else if(stim[i].equalsIgnoreCase("Restart")) {
                label = "Restart";
                scale = 0.0f;
            } else if(stim[i].equalsIgnoreCase("Wrong")) {
                label = "Wrong";
                scale = 1.5f;
            } else {
                assert false: "In mock_maze/Simple.java: "+
                    "stim '"+stim[i]+"' not recognized";
            }
            int offset = template.getIndex((float)time[i]);
            if(offset>=0 && offset<template.getNIndexes())
                y[offset] = scale;
            ev.add(new Event(time[i], label));
        }

        SeriesAnalog s = new SeriesAnalog(template,y);
        return s.convolveAsym(erp);
    } // getEegWithErps

    ////////////////////////////////////////////////////////////////////
    /** Generate pseudo EEG containing alpha
     */
    private SeriesAnalog getEegWithAlpha(SeriesAnalog template, float t0) {
        float lnorm = 2.5f;
        return SeriesAnalog.getModelledEegTimeseries(template,t0,lnorm);
    } // getEegWithAlpha

    ////////////////////////////////////////////////////////////////////
    /** Generate pseudo ECG.
     * The wavelet is a standard ERP, but deliberately truncated to
     * make it more spike-like.
     * The ECG will have R-R intervals of 0.80+var seconds, where 'var'
     * has a gamma distribution, with mean equal to 0.1 seconds.
     */
    private SeriesAnalog getEcg(SeriesAnalog template) {
        float ecgDuration = 0.2f;
        float xDelta = template.getXDelta(); // sampling interval, in seconds
        int nIndexes = Math.round(ecgDuration/xDelta);
        if((nIndexes&1)==0) nIndexes++;
        float x0 = -(nIndexes-1)*xDelta/2;  // so wavelet is centred on 0 secs
        SeriesAnalog wavelet = new SeriesAnalog(new SiteSet(), // sites
                                                x0,            // x0
                                                xDelta,        // xDelta
                                                new Units(Unit.s),  // xUnits
                                                nIndexes,      // # samples
                                                new Units(),   // yUnits
                                                DataMode.ECG); // DataMode
        float lnorm = -5.0f;
        SeriesAnalog erp =SeriesAnalog.getModelledErpTimeseries(wavelet,lnorm);

        Erlang ran = new Erlang(0.1/5, 5, 0);  // scale = mean/5
        ArrayList<Float> timesList = new ArrayList<Float>();
        float t = template.getFirstX()+0.3f;  // time of first ECG event
        float duration = template.getXDelta()*template.getNIndexes();
        while(t<duration) {
            timesList.add(new Float(t));
            t += (float)(0.8+ran.gen());
        }
        float[] times = new float[timesList.size()];
        for(int i=0; i<timesList.size(); i++) times[i] = timesList.get(i);
    
        return SeriesAnalog.getImpulseTimeseries(template,times)
            .mul(xDelta).convolveAsym(erp);
    } // getEcg
}

 


Validate HTML CSS Generated 2011-08-12T10:28:13+1000 Chris Rennie