public Callable<Double> getPositionCallable() {
   if (lastImageNumberStreamIndexer == null) {
     logger.info("Creating lastImageNumberStreamIndexer");
     timeSeriesCollection =
         new ZebraCaptureInputStreamCollection(
             zebra.getNumberOfPointsCapturedPV(), zebra.getEnc1AvalPV());
     lastImageNumberStreamIndexer = new PositionStreamIndexer<Double>(timeSeriesCollection);
   }
   numPosCallableReturned++;
   return lastImageNumberStreamIndexer.getNamedPositionCallable(zSM.getName(), 1);
 }
  @SuppressWarnings("unused")
  @Override
  public void prepareForMove() throws DeviceException, InterruptedException {
    try {
      logger.info("prepare for move");

      boolean operatingContinously = zSM.isOperatingContinously();
      try {
        zebra.pcDisarm();
        // if we want to check it is disarmed we will need to wait >2s as that is the zebra bus
        // update period

        if (operatingContinously) zSM.setOperatingContinuously(false);
        requiredSpeed = (Math.abs(step) / triggerPeriod);

        zSM.asynchronousMoveTo(
            start - (step > 0 ? 1.0 : -1.0) * zSM.distanceToAccToVelocity(requiredSpeed));

        // sources must be set first
        zebra.setPCGateSource(step > 0 ? 0 : 3); // Posn +ve/-ve
        zebra.setPCArmSource(0); // Soft

        zebra.setPCPulseSource(mode); // Position

        // set motor before setting gates and pulse parameters
        zebra.setPCEnc(pcEnc); // enc1
        zebra.setPCTimeUnit(Zebra.PC_TIMEUNIT_SEC); // s

        zebra.setPCGateStart(start);
        zebra.setPCGateNumberOfGates(1);

        zebra.setPCCaptureBitField(pcCaptureBitField);

        zebra.setPCPulseDelay(0.);
        pcPulseDelayRBV = zebra.getPCPulseDelayRBV();

        switch (mode) {
          case Zebra.PC_MODE_POSITION:
            if (true) throw new IllegalStateException("PC_MODE_POSITION is not yet tested");
            zebra.setPCPulseWidth(Math.abs(step / 2));
            pcPulseWidthRBV = zebra.getPCPulseWidthRBV();
            zebra.setPCPulseStep(Math.abs(step));

            pcPulseStepRBV = zebra.getPCPulseStepRBV();

            double gateWidthPosn =
                pcPulseDelayRBV + pcPulseStepRBV * (getNumberTriggers() - 1) + pcPulseWidthRBV;

            zebra.setPCGateWidth(gateWidthPosn);

            pcGateWidthRBV = zebra.getPCGateWidthRBV();
            pcGateStartRBV = zebra.getPCGateStartRBV();
            requiredSpeed =
                (Math.abs(pcPulseStepRBV) / triggerPeriod) * zSM.getConstantVelocitySpeedFactor();
            break;
          case Zebra.PC_MODE_TIME:
            zebra.setPCTimeUnit(Zebra.PC_TIMEUNIT_MS); // s
            zebra.setPCPulseWidth(.1); // .01ms
            pcPulseWidthRBV = zebra.getPCPulseWidthRBV() / 1000;
            zebra.setPCPulseStep(triggerPeriod * 1000); // in  ms

            pcPulseStepRBV = zebra.getPCPulseStepRBV() / 1000;

            double gateWidthTime =
                pcPulseDelayRBV + pcPulseStepRBV * (getNumberTriggers() - 1) + pcPulseWidthRBV;
            requiredSpeed = (Math.abs(step) / pcPulseStepRBV);
            zebra.setPCGateWidth((gateWidthTime * requiredSpeed));

            pcGateWidthRBV = zebra.getPCGateWidthRBV();
            pcGateStartRBV = zebra.getPCGateStartRBV();
            break;
          default:
            throw new DeviceException("Unacceptable mode " + mode);
        }

        zSM.waitWhileBusy();

      } finally {
        zSM.setOperatingContinuously(operatingContinously);
      }
      int numberTriggers = getNumberTriggers();
      zebra.setPCPulseMax(numberTriggers);
      zebra.pcArm();
      timeSeriesCollection.start(numberTriggers);

    } catch (Exception e) {
      throw new DeviceException("Error arming the zebra", e);
    }
  }