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
}