package org.jactr.modules.pm.motor.command.translators; /* * default logging */ import java.util.ArrayList; import java.util.Collection; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.commonreality.object.IEfferentObject; import org.jactr.core.chunk.IChunk; import org.jactr.core.model.IModel; import org.jactr.core.production.request.ChunkTypeRequest; import org.jactr.core.slot.DefaultMutableSlot; import org.jactr.core.slot.IMutableSlot; import org.jactr.core.slot.ISlot; import org.jactr.modules.pm.motor.IMotorModule; public abstract class AbstractManualTranslator extends AbstractTranslator { static public final String PECK_FITTS_COEFFICIENT = "PeckFittsCoefficient"; static public final String MINIMUM_FITTS_TIME = "MinimumFittsTime"; static public final String MINIMUM_MOVEMENT_TIME = "MinimumMovementTime"; static private final double LOG_2 = Math .log(2); /** * Logger definition */ static final transient Log LOGGER = LogFactory .getLog(AbstractManualTranslator.class); private double _motorBurstTime = Double.NaN; private double _minimumFittsTime = Double.NaN; private double _peckFittsCoeff = Double.NaN; private Collection<ISlot> _recycledSlotContainer = new ArrayList<ISlot>(4); public AbstractManualTranslator() { } /** * translates a set of slot values into a {@link IEfferentObject} that * represents a muscle defined within the pattern. Since many ACT-R movement * commands use multiple slots to define a muscle, this collapses them. (i.e. * translates hand right finger index into right-index). In addition to the * returned {@link IEfferentObject} this method should also ensure that the * slots used to define the muscle are nulled out and the muscle slot is * specified. * * @param request * @param model * @return * @throws IllegalArgumentException */ public IEfferentObject getMuscle(ChunkTypeRequest request, IModel model) throws IllegalArgumentException { /* * special processing to handle finger,hand,device */ IChunk hand = null; IChunk finger = null; IChunk device = null; ISlot muscleSlot = null; _recycledSlotContainer.clear(); _recycledSlotContainer = request.getSlots(_recycledSlotContainer); for (ISlot slot : _recycledSlotContainer) { String slotName = slot.getName(); if (slotName.equalsIgnoreCase("hand")) { hand = (IChunk) slot.getValue(); request.removeSlot(slot); } else if (slotName.equalsIgnoreCase("finger")) { finger = (IChunk) slot.getValue(); request.removeSlot(slot); } else if (slotName.equalsIgnoreCase("device")) { device = (IChunk) slot.getValue(); request.removeSlot(slot); } else if (slotName.equalsIgnoreCase("muscle")) muscleSlot = slot; } String muscleName = null; if (hand != null) { muscleName = hand.getSymbolicChunk().getName(); if (finger != null) muscleName += "-" + finger.getSymbolicChunk().getName(); } else if (device != null) muscleName = device.getSymbolicChunk().getName(); else if (muscleSlot != null) muscleName = "" + muscleSlot.getValue(); IEfferentObject muscle = getMuscle(muscleName, model); if (LOGGER.isDebugEnabled()) LOGGER.debug("muscle named : " + muscleName + " matches to efferent object " + muscle.getIdentifier()); if (muscle == null) { muscleName = null; if (LOGGER.isWarnEnabled()) LOGGER.warn("No muscle found, available : " + getCachedMuscleNames()); } /** * finally we set the muscle slot value since that is what we will actually * be using further down stream in the processing */ if (muscleSlot == null) { muscleSlot = new DefaultMutableSlot("muscle", muscleName); request.addSlot(muscleSlot); } else ((IMutableSlot) muscleSlot).setValue(muscleName); if (LOGGER.isDebugEnabled()) LOGGER.debug("Final movement pattern : " + request); return muscle; } protected double getMotorBurstTime(IMotorModule module) { if (Double.isNaN(_motorBurstTime)) try { _motorBurstTime = Double.parseDouble(module .getParameter(MINIMUM_MOVEMENT_TIME)); } catch (Exception e) { _motorBurstTime = 0.05; } return _motorBurstTime; } protected double getMinimumFittsTime(IMotorModule module) { if (Double.isNaN(_minimumFittsTime)) try { _minimumFittsTime = Double.parseDouble(module .getParameter(MINIMUM_FITTS_TIME)); } catch (Exception e) { _minimumFittsTime = 0.1; } return _minimumFittsTime; } protected double getPeckFittsCoefficient(IMotorModule module) { if (Double.isNaN(_peckFittsCoeff)) try { _peckFittsCoeff = Double.parseDouble(module .getParameter(PECK_FITTS_COEFFICIENT)); } catch (Exception e) { _peckFittsCoeff = 0.075; } return _peckFittsCoeff; } protected double computeFitts(double fittsCoeff, double distance, double width) { return fittsCoeff * Math.log(distance / width + 0.5) / LOG_2; } protected double[] computeRate(double[] origin, double[] target, double duration) { double[] rate = new double[origin.length]; for (int i = 0; i < rate.length; i++) { rate[i] = (target[i] - origin[i]) / duration; if (Double.isNaN(rate[i])) rate[i] = 0; } return rate; } }