scripts/epochClasses/eyet/Cycle.java

/////////////////////////////////////////////////////////////////
// Standard epoch-definition script for EYET data
/////////////////////////////////////////////////////////////////
package eyet;

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

import epochClasses.*;
import generalClasses.*;
import recordingClasses.Recording;
import seriesClasses.*;
import static epochClasses.EpochSelector.*;

/////////////////////////////////////////////////////////////////

/** Standard epoch-definition script for EC and EO data.
 * This script generates a list of containing just one epoch time; 
 * and for this epoch generates a standard set of attributes.  The 
 * attribute table is a flexible way for attaching all manner of 
 * information to each epoch.
 * This information may be used later when subaveraging epochs.
 *
 * <p>This script also defines the start and duration of epochs through
 * <ul><li><tt>defaultEpochStart</tt>: the offset of epoch beginnings from
 *     the times listed in the the (obligatory) attribute <tt>Time</tt>.
 *     A value of 0.0 is common for EEGs.</li>
 *     <li><tt>defaultEpochDur</tt>: the duration of epochs, in seconds.
 *     A value of 24.3 is common for spectral analysis; 2.43 for time-locking 
 *     to the stimulus cycle.</li>
 *     <li><tt>dt</tt>: the increment between successsive epochs, in dec </li>
 * </ul>
 *
 * <p>Attribute values, and epoch start and duration, are reported 
 * to the calling program via the methods defined in the superclass
 * <tt>EpochScript</tt>
 */
public class Cycle extends EpochScript
{
    private static float defaultEpochStart = 0.0f;
    private static float defaultEpochDur = 2.43f;  // This is ~1 cycle of stim
    private static float dt = defaultEpochDur;
    private float[] times = null;       // t=0 point of epochs, in lieu of evs
    private String[][] th = null;       // column header information
    private Object[][] td = null;       // main results array
    private int iteratorEventN = 0;     // used only by iterator, next()
    private int nAttrib;                // number of attribute columns
    private ArrayList<Event> runtimeEvents = null;  // obtained from Recording

    ////////////////////////////////////////////////////////////////////
    /** This script generates a standard set of attributes:
     * <ul><li><tt>Time</tt>: points within the span of the recording.  In
     *     the case of ERP recordings, these times usually coincide with
     *     significant stimuli.  However they can be offset for stimuli, or
     *     else identify EDA onsets, K-complexes, hypoxic events, etc. <b>This
     *     attribute is obligatory.</b></li>
     *     <li><tt>Stim</tt>: a string label that is commonly used for
     *     identifying epochs.  It is here that an event (which contains 
     *     information like Tone, 1000 Hz) is assigned the label "Bg".</li>
     *     <li><tt>AlphaPow, AlphaPha, AlphaQ</tt>: values that quantify 
     *     the magnitude and phase of alpha.</li>
     *     <li><tt>ScalpSD, ScalpPP</tt>: maximal SD and peak-to-peak range
     *     within each epoch.  Only scalp channels are considered.</li>
     *     <li>And so on …</li>
     * </ul>
     * (Check code for a definitive list.)  It also sets the epoch start
     * offset and epoch duration.
     * @param rec Reference to the data. This is used to retrieve both time 
     *        series and event information.
     */
    public Cycle(Recording rec) {
        super(rec, defaultEpochStart, defaultEpochDur);

        // There are no real events during EYET, so create pseudo-events
        int nEvents = 0;
        try {
            float t0 = rec.getX0();
            float t1 = rec.getX1();
            if(t1-t0-getEpochDur()<0) {
                String errMsg = "Illegal values found: \n";
                errMsg += "Recording duration is "+(t1-t0)+" seconds, ";
                errMsg += "while epoch duration is "+getEpochDur();
                CommonLog.get().severe(errMsg);
                System.exit(2);
            }
            nEvents = (int)((t1-t0-getEpochDur())/dt) + 1;
            // Times will be in the range [t0,t1)
            times = new float[nEvents];
            for(int row=0; row<nEvents; row++)
                times[row] = t0 + (-getEpochStart()) + row*dt;
        } catch(recordingClasses.RecordingException e) {
            System.out.println("In eyet/Cycle.java: "+e.toString());
            System.out.println("This means that the recording contains time"+
                               " series with unequal t0 or lengths!");
            System.exit(2);
        }

        // Generate meta information about attribute table
        String[] names = {
            "Time", "Stim",
            "AlphaPow", "AlphaPha", "AlphaQ", 
            "ScalpSD", "ScalpPP"};
        String[] types = {
            "FLOAT", "VARCHAR6",
            "FLOAT", "FLOAT", "VARCHAR3",
            "FLOAT", "FLOAT"};
        assert names.length==types.length: "Cycle.java: length mismatch";
        nAttrib = names.length;
        th = new String[2][nAttrib];
        for(int i=0; i<nAttrib; i++) {
            th[0][i] = names[i];
            th[1][i] = types[i];
        }

        // Allocate attribute table
        assert (nEvents>=1): "Logic error in eyet/Cycle: nEvents="+nEvents;
        td = new Object[nEvents][nAttrib];

        // Initialize attribute table, one (or a few) columns at a time
        updateTime(0);
        updateStim(1);
        updateSpectralPowerAtStim(2,3,4);
        updateArtif(5,6);
    } // Cycle

    ////////////////////////////////////////////////////////////////////
    /** Dump summary of this object, to be used for diagnostics
     * @return String representation of this object
     */
    @Override
    public String toString() {
        String s = super.toString();      // get superclass description
        s += "Increment between epochs: "+dt;
        if(dt<=getEpochDur()) s += " (overlap = "+(getEpochDur()-dt)+")";
        else s += " (gap = "+(dt-getEpochDur())+")";
        return s;
    } // toString

    ////////////////////////////////////////////////////////////////////
    /** This returns the name and datatype of any number of attributes.
     * Each name and datatype characterizes one column of the table
     * of attributes.
     * @return The name and datatype for each of <tt>nCols</tt> columns
     *         of the attribute table.  The array is dimensioned [2][nCol].
     */
    @Override
    public String[][] getAttribNameType() {
        return th;
    } // getAttribNameType

    ////////////////////////////////////////////////////////////////////
    /** The attribute table is returned one row at a time.  This checks 
     * if there are more rows available.
     */
    @Override
    public boolean hasNext() {
        return td!=null && iteratorEventN < td.length;
    } // hasNext

    ////////////////////////////////////////////////////////////////////
    /** Returns the next row of attribute table.  If there are no more
     * rows remaining, then <tt>null</tt> is returned.  Note that is 
     * possible that each element in the returned array is itself
     * <tt>null</tt>, so calling code should be prepared to deal with
     * such cases too.
     */
    @Override
    public Object[] next() {
        return (td!=null && iteratorEventN<td.length)?
            td[iteratorEventN++]: null;
    } // next


    ////////////////////////////////////////////////////////////////////
    //                   private methods                              //
    ////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////
    /** Initializes td[*][colTime] with the event times, in seconds.
     * NOTE: this is obligatory, unlike all other array columns.
     */
    private void updateTime(int colTime) {
        for(int row=0; row<td.length; row++) {
            td[row][colTime] = new Float(times[row]);
        }
    } // updateTime

    ////////////////////////////////////////////////////////////////////
    /** Initializes td[*][colStim].
     * 
     * This version is specific to EC/EO paradigms where the 'stimuli'
     * are pseudo.
     */
    private void updateStim(int colStim) {
        for(int row=0; row<td.length; row++)
            td[row][colStim] = "Pseudo";
    } // updateStim

    ////////////////////////////////////////////////////////////////////
    /** Initializes td[*][colPow] td[*][colPha] and td[*][colPhaseBand],
     * which refer to spectral band power, phase, and a categorical version
     * of phase.  Alpha is quantified for some brief (e.g. 1-sec) period 
     * following the times listed in <tt>float[] times</tt>.
     *
     * This version is specific to 'Pz' and to the alpha band.
     */
    private void updateSpectralPowerAtStim(int colPow, int colPha,
                                           int colPhaseBand) {
        // Which site to look at
        String site = "Pz";
        // Definition of spectral band: binWidth * binN = 10.0 Hz => alpha
        float binWidth = 1.0f;
        int binN = 10;

        // Calculate alpha power and phase at Pz
        ArrayList ss = getSeries();
        int chanN = 0;
        for(chanN=0; chanN<ss.size(); chanN++) {
            Series s = (Series)(ss.get(chanN));
            if(s.getPrimaryLabel().equalsIgnoreCase(site)) break;
        }
        Float[][] alpha = new Float[td.length][2];
        if(chanN<ss.size())
            getEegAtChan((Series)(ss.get(chanN)), binWidth, binN, times, alpha);

        // Update power, phase and categorized phase
        for(int row=0; row<td.length; row++) {
            td[row][colPow] = alpha[row][0];   // power
            td[row][colPha] = alpha[row][1];   // phase 
            if(alpha[row][1] != null) {
                int cat = Math.round(alpha[row][1]/180); if(cat<0) cat+=2;
                td[row][colPhaseBand] = (cat==1)? "Out": "In";
            } else
                td[row][colPhaseBand] = null;
        }
    } // updateSpectralPowerAtStim

    ////////////////////////////////////////////////////////////////////
    /** Initializes td[*][colScalpSd] and td[*][colScalpPP], which refer
     * to the maximal standard deviation and peak-to-peak values, considering
     * all scalp signals.  These values are suitable for threshold-based
     * epoch rejection.
     */
    private void updateArtif(int colScalpSd, int colScalpPP) {
        ArrayList ss = getSeries();

        for(int row=0; row<td.length; row++) {
            // Calculate SD and P-P measures by finding max in EEG chans only
            float maxSd = 0;
            float maxPP = 0;
            float tStart = times[row]+getEpochStart();
            for(int chanN=0; chanN<ss.size(); chanN++) {
                if(ss.get(chanN) instanceof SeriesAnalog) {
                    SeriesAnalog s = (SeriesAnalog)(ss.get(chanN));
                    if(s.getMode()==DataMode.EEG && 
                       s.indexOf(tStart) >= 0 &&
                       s.indexOf(tStart+getEpochDur()) <= s.getNIndexes()) {
                        BasicStats bs = s.segment(tStart,getEpochDur(),
                                                  getEpochStart())
                            .getStats();
                        if(bs.sd > maxSd) maxSd = bs.sd;
                        if(bs.max-bs.min > maxPP) maxPP = bs.max-bs.min;
                    }
                }
            }

            // Update SD and P-P measures
            td[row][colScalpSd] = maxSd;
            td[row][colScalpPP] = maxPP;
        }
    } // updateArtif

}

 


Validate HTML CSS Generated 2011-08-12T10:32:18+1000 Chris Rennie