// This file is part of PleoCommand: // Interactively control Pleo with psychobiological parameters // // Copyright (C) 2010 Oliver Hoffmann - Hoffmann_Oliver@gmx.de // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Boston, USA. package pleocmd.pipe.in; import java.io.IOException; import java.util.Random; import pleocmd.Log; import pleocmd.cfg.ConfigDouble; import pleocmd.cfg.ConfigInt; import pleocmd.exc.InputException; import pleocmd.itfc.gui.dgr.DiagramDataSet; import pleocmd.pipe.data.Data; import pleocmd.pipe.data.SingleFloatData; public final class RandomGeneratorInput extends Input { // NO_UCD private static Random rand = new Random(); private final ConfigInt cfgUserData; private final ConfigInt cfgSamplerate; private final ConfigDouble cfgPeakPropbability; private final ConfigDouble cfgMaxAmplitude; private final ConfigDouble cfgMaxPeakLength; private final ConfigDouble cfgMaxGrad0Length; private final ConfigDouble cfgMaxGrad1Length; private final ConfigDouble cfgMaxNoise; private enum PeakPos { NoPeak, Grad0, OnPeak, Grad1 } private long last; private PeakPos peakPos; private double amp; private int peakLen; // in loops private int grad0Len; // in loops private int grad1Len; // in loops private double grad0Inc; private double grad1Inc; private double value; private int step; public RandomGeneratorInput() { addConfig(cfgUserData = new ConfigInt("User-Data", 0)); addConfig(cfgSamplerate = new ConfigInt("Samplerate (in Hz)", 10, 1, 10000)); addConfig(cfgPeakPropbability = new ConfigDouble("Peak Probability", 0, 0, 1, 0.01)); addConfig(cfgMaxAmplitude = new ConfigDouble("Max Amplitude", 1000, 0, Double.MAX_VALUE)); addConfig(cfgMaxPeakLength = new ConfigDouble( "Max Length of Peak (in ms)", 2000, 0, Double.MAX_VALUE)); addConfig(cfgMaxGrad0Length = new ConfigDouble( "Max Length of First Gradient (in ms)", 2000, 0, Double.MAX_VALUE)); addConfig(cfgMaxGrad1Length = new ConfigDouble( "Max Length of Second Gradient (in ms)", 2000, 0, Double.MAX_VALUE)); addConfig(cfgMaxNoise = new ConfigDouble("Max Noise", 300, 0, Double.MAX_VALUE)); constructed(); } @Override protected void init0() throws IOException { last = 0; peakPos = PeakPos.NoPeak; } @Override protected void initVisualize0() { final DiagramDataSet ds = getVisualizeDataSet(0); if (ds != null) ds.setLabel(String.format("Random [0-%s]", cfgMaxAmplitude.getContent())); } @Override public String getOutputDescription() { return SingleFloatData.IDENT; } @Override protected String getShortConfigDescr0() { return String.format("%s@p=%s ~%s", cfgMaxAmplitude.asString(), cfgPeakPropbability.asString(), cfgMaxNoise.asString()); } @Override protected Data readData0() throws InputException, IOException { try { final long next = last + 1000 / cfgSamplerate.getContent(); final long now = System.currentTimeMillis(); if (next > now) { Log.detail("Artificially slow down by %d ms", next - now); Thread.sleep(next - now); } } catch (final InterruptedException e) { Log.detail("Slow down interrupted"); return null; } last = System.currentTimeMillis(); final double d; Log.detail("PeakPos: %s", peakPos.toString()); switch (peakPos) { case NoPeak: d = .0; if (Math.random() < cfgPeakPropbability.getContent()) { peakPos = PeakPos.Grad0; amp = rand11() * cfgMaxAmplitude.getContent(); final double samplesPerMS = cfgSamplerate.getContent() / 1000.0; peakLen = Math.max(1, (int) rand0N(cfgMaxPeakLength.getContent() * samplesPerMS)); grad0Len = Math.max(1, (int) rand0N(cfgMaxGrad0Length.getContent() * samplesPerMS)); grad1Len = Math.max(1, (int) rand0N(cfgMaxGrad1Length.getContent() * samplesPerMS)); grad0Inc = amp / grad0Len; grad1Inc = amp / grad1Len; value = 0; step = 0; if (Log.canLogDetail()) Log.detail("Switching to PeakPos '%s' with amp %f " + "and length %d, %d and %d => " + "peak-inc will be %f and %f", peakPos.toString(), amp, grad0Len, peakLen, grad1Len, grad0Inc, grad1Inc); } break; case Grad0: d = value += grad0Inc; if (++step >= grad0Len) { step = 0; peakPos = PeakPos.OnPeak; Log.detail("Switching to PeakPos '%s'", peakPos.toString()); } break; case OnPeak: d = amp; // assert(value == amp) if (++step >= peakLen) { step = 0; peakPos = PeakPos.Grad1; Log.detail("Switching to PeakPos '%s'", peakPos.toString()); } break; case Grad1: d = value -= grad1Inc; if (++step >= grad1Len) { peakPos = PeakPos.NoPeak; Log.detail("Switching to PeakPos '%s'", peakPos.toString()); } break; default: return null; } final double val = d + rand11() * cfgMaxNoise.getContent(); if (Log.canLogDetail()) Log.detail("Adding noise to %f => %f", d, val); if (isVisualize()) plot(0, val); return new SingleFloatData(val, cfgUserData.getContent(), null); } private static double rand11() { return 1 - 2 * rand.nextDouble(); } private static double rand0N(final double max) { return rand.nextDouble() * max; } public static String help(final HelpKind kind) { switch (kind) { case Name: return "Random Generator"; case Description: return "Generates noise with some random peaks"; case Config1: return "Some integer which is sent as an additional argument in " + "the Data blocks (mostly used for channel number)"; case Config2: return "Number of Data blocks created per second"; case Config3: return "The probability to start a new peak on a Data block " + "(if 0 there are no peaks at all, if 1 a peak is started " + "immediately after the last one)"; case Config4: return "The maximum possible (absolute) value to send during a peak"; case Config5: return "The maximum possible length of one peak in ms (rounded " + "down to the next value possible with the current samplerate)"; case Config6: return "The maximum possible length from the start to the highest point " + "of a peak in ms (rounded down to the next value possible " + "with the current samplerate)"; case Config7: return "The maximum possible length from the highest point to the end " + "of a peak in ms (rounded down to the next value possible " + "with the current samplerate)"; case Config8: return "The maximum possible random noise added to / subtracted from " + "each generated Data block."; default: return null; } } @Override public String isConfigurationSane() { return null; } @Override protected int getVisualizeDataSetCount() { return 1; } }