IPState TCFS::MoveAbsFocuser(uint32_t targetTicks) { int delta = targetTicks - currentPosition; if (delta < 0) return MoveRelFocuser(FOCUS_INWARD, (uint32_t)std::abs(delta)); return MoveRelFocuser(FOCUS_OUTWARD, (uint32_t)std::abs(delta)); }
bool ArmPlat::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) { //IDLog( "NewNumber: %s %s %lf %s %d\n", dev, name, values[ 0 ], names[ 0 ], n ); if (dev != nullptr && strcmp(dev, getDeviceName()) == 0) { ///////////////////////////////////////////// // Backlash ///////////////////////////////////////////// if (strcmp(name, BacklashNP.name) == 0) { IUUpdateNumber(&BacklashNP, values, names, n); // Only update backlash value if compensation is enabled if (BacklashCompensationS[BACKLASH_ENABLED].s == ISS_ON) { bool rc = setBacklash(BacklashN[0].value); BacklashNP.s = rc ? IPS_OK : IPS_ALERT; } else { backlash = 0; BacklashNP.s = IPS_OK; } IDSetNumber(&BacklashNP, nullptr); return true; } // if (strcmp(name, SyncNP.name) == 0) // { // bool rc = sync(static_cast<uint32_t>(values[0])); // SyncNP.s = rc ? IPS_OK : IPS_ALERT; // if (rc) // SyncN[0].value = values[0]; // IDSetNumber(&SyncNP, nullptr); // return true; // } ///////////////////////////////////////////// // Relative goto ///////////////////////////////////////////// else if (strcmp(name, FocusRelPosNP.name) == 0) { IUUpdateNumber(&FocusRelPosNP, values, names, n); IDSetNumber(&FocusRelPosNP, nullptr); MoveRelFocuser( FocusMotionS[ 0 ].s == ISS_ON ? FOCUS_INWARD : FOCUS_OUTWARD, (uint32_t)values[ 0 ] ); return true; } ///////////////////////////////////////////// // MaxSpeed ///////////////////////////////////////////// else if (strcmp(name, MaxSpeedNP.name) == 0) { IUUpdateNumber(&MaxSpeedNP, values, names, n); bool rc = setMaxSpeed(MaxSpeedN[0].value); MaxSpeedNP.s = rc ? IPS_OK : IPS_ALERT; IDSetNumber(&MaxSpeedNP, nullptr); return true; } } return INDI::Focuser::ISNewNumber(dev, name, values, names, n); }
bool FocuserInterface::processNumber(const char * dev, const char * name, double values[], char * names[], int n) { // Move focuser based on requested timeout if (!strcmp(name, FocusTimerNP.name)) { FocusDirection dir; int speed; int t; // first we get all the numbers just sent to us IUUpdateNumber(&FocusTimerNP, values, names, n); // Now lets find what we need for this move speed = FocusSpeedN[0].value; if (FocusMotionS[0].s == ISS_ON) dir = FOCUS_INWARD; else dir = FOCUS_OUTWARD; t = FocusTimerN[0].value; lastTimerValue = t; FocusTimerNP.s = MoveFocuser(dir, speed, t); IDSetNumber(&FocusTimerNP, nullptr); return true; } // Set variable focus speed if (!strcmp(name, FocusSpeedNP.name)) { FocusSpeedNP.s = IPS_OK; int current_speed = FocusSpeedN[0].value; IUUpdateNumber(&FocusSpeedNP, values, names, n); if (SetFocuserSpeed(FocusSpeedN[0].value) == false) { FocusSpeedN[0].value = current_speed; FocusSpeedNP.s = IPS_ALERT; } // Update client display IDSetNumber(&FocusSpeedNP, nullptr); return true; } // Update Maximum Position allowed if (!strcmp(name, FocusMaxPosNP.name)) { uint32_t maxTravel = rint(values[0]); if (SetFocuserMaxPosition(maxTravel)) { IUUpdateNumber(&FocusMaxPosNP, values, names, n); FocusAbsPosN[0].max = FocusSyncN[0].max = FocusMaxPosN[0].value; FocusAbsPosN[0].step = FocusSyncN[0].step = FocusMaxPosN[0].value / 50.0; FocusAbsPosN[0].min = FocusSyncN[0].min = 0; FocusRelPosN[0].max = FocusMaxPosN[0].value / 2; FocusRelPosN[0].step = FocusMaxPosN[0].value / 100.0; FocusRelPosN[0].min = 0; IUUpdateMinMax(&FocusAbsPosNP); IUUpdateMinMax(&FocusRelPosNP); IUUpdateMinMax(&FocusSyncNP); FocusMaxPosNP.s = IPS_OK; } else FocusMaxPosNP.s = IPS_ALERT; IDSetNumber(&FocusMaxPosNP, nullptr); return true; } // Sync if (!strcmp(name, FocusSyncNP.name)) { if (SyncFocuser(rint(values[0]))) { FocusSyncN[0].value = FocusAbsPosN[0].value = rint(values[0]); FocusSyncNP.s = IPS_OK; IDSetNumber(&FocusSyncNP, nullptr); IDSetNumber(&FocusAbsPosNP, nullptr); } else { FocusSyncNP.s = IPS_ALERT; IDSetNumber(&FocusSyncNP, nullptr); } return true; } // Update Absolute Focuser Position if (!strcmp(name, FocusAbsPosNP.name)) { int newPos = rint(values[0]); if (newPos < FocusAbsPosN[0].min) { FocusAbsPosNP.s = IPS_ALERT; IDSetNumber(&FocusAbsPosNP, nullptr); DEBUGFDEVICE(dev, Logger::DBG_ERROR, "Requested position out of bound. Focus minimum position is %g", FocusAbsPosN[0].min); return false; } else if (newPos > FocusAbsPosN[0].max) { FocusAbsPosNP.s = IPS_ALERT; IDSetNumber(&FocusAbsPosNP, nullptr); DEBUGFDEVICE(dev, Logger::DBG_ERROR, "Requested position out of bound. Focus maximum position is %g", FocusAbsPosN[0].max); return false; } IPState ret; if ((ret = MoveAbsFocuser(newPos)) == IPS_OK) { FocusAbsPosNP.s = IPS_OK; IUUpdateNumber(&FocusAbsPosNP, values, names, n); DEBUGFDEVICE(dev, Logger::DBG_SESSION, "Focuser moved to position %d", newPos); IDSetNumber(&FocusAbsPosNP, nullptr); return true; } else if (ret == IPS_BUSY) { FocusAbsPosNP.s = IPS_BUSY; DEBUGFDEVICE(dev, Logger::DBG_SESSION, "Focuser is moving to position %d", newPos); IDSetNumber(&FocusAbsPosNP, nullptr); return true; } FocusAbsPosNP.s = IPS_ALERT; DEBUGDEVICE(dev, Logger::DBG_ERROR, "Focuser failed to move to new requested position."); IDSetNumber(&FocusAbsPosNP, nullptr); return false; } // Update Relative focuser steps. This moves the focuser CW/CCW by this number of steps. if (!strcmp(name, FocusRelPosNP.name)) { int newPos = rint(values[0]); if (newPos <= 0) { DEBUGDEVICE(dev, Logger::DBG_ERROR, "Relative ticks value must be greater than zero."); FocusRelPosNP.s = IPS_ALERT; IDSetNumber(&FocusRelPosNP, nullptr); return false; } IPState ret; if (CanAbsMove()) { if (FocusMotionS[0].s == ISS_ON) { if (FocusAbsPosN[0].value - newPos < FocusAbsPosN[0].min) { FocusRelPosNP.s = IPS_ALERT; IDSetNumber(&FocusRelPosNP, nullptr); DEBUGFDEVICE(dev, Logger::DBG_ERROR, "Requested position out of bound. Focus minimum position is %g", FocusAbsPosN[0].min); return false; } } else { if (FocusAbsPosN[0].value + newPos > FocusAbsPosN[0].max) { FocusRelPosNP.s = IPS_ALERT; IDSetNumber(&FocusRelPosNP, nullptr); DEBUGFDEVICE(dev, Logger::DBG_ERROR, "Requested position out of bound. Focus maximum position is %g", FocusAbsPosN[0].max); return false; } } } if ((ret = MoveRelFocuser((FocusMotionS[0].s == ISS_ON ? FOCUS_INWARD : FOCUS_OUTWARD), newPos)) == IPS_OK) { FocusRelPosNP.s = FocusAbsPosNP.s = IPS_OK; IUUpdateNumber(&FocusRelPosNP, values, names, n); IDSetNumber(&FocusRelPosNP, "Focuser moved %d steps %s", newPos, FocusMotionS[0].s == ISS_ON ? "inward" : "outward"); IDSetNumber(&FocusAbsPosNP, nullptr); return true; } else if (ret == IPS_BUSY) { IUUpdateNumber(&FocusRelPosNP, values, names, n); FocusRelPosNP.s = FocusAbsPosNP.s = IPS_BUSY; IDSetNumber(&FocusAbsPosNP, "Focuser is moving %d steps %s...", newPos, FocusMotionS[0].s == ISS_ON ? "inward" : "outward"); IDSetNumber(&FocusAbsPosNP, nullptr); return true; } FocusRelPosNP.s = IPS_ALERT; DEBUGDEVICE(dev, Logger::DBG_ERROR, "Focuser failed to move to new requested position."); IDSetNumber(&FocusRelPosNP, nullptr); return false; } return false; }
bool TCFS::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) { //LOGF_DEBUG("%s %s",__FUNCTION__, me); if (dev != nullptr && strcmp(dev, getDeviceName()) == 0) { char response[TCFS_MAX_CMD] = { 0 }; if (!strcmp(FocusPowerSP.name, name)) { IUUpdateSwitch(&FocusPowerSP, states, names, n); bool sleep = false; ISwitch *sp = IUFindOnSwitch(&FocusPowerSP); // Sleep if (!strcmp(sp->name, "FOCUS_SLEEP")) { dispatch_command(FSLEEP); sleep = true; } // Wake Up else dispatch_command(FWAKUP); if (read_tcfs(response) == false) { IUResetSwitch(&FocusPowerSP); FocusPowerSP.s = IPS_ALERT; IDSetSwitch(&FocusPowerSP, "Error reading TCF-S reply."); return true; } if (sleep) { if (isSimulation()) strncpy(response, "ZZZ", TCFS_MAX_CMD); if (strcmp(response, "ZZZ") == 0) { FocusPowerSP.s = IPS_OK; IDSetSwitch(&FocusPowerSP, "Focuser is set into sleep mode."); FocusAbsPosNP.s = IPS_IDLE; IDSetNumber(&FocusAbsPosNP, nullptr); // if (FocusTemperatureNP) { FocusTemperatureNP.s = IPS_IDLE; IDSetNumber(&FocusTemperatureNP, nullptr); } return true; } else { FocusPowerSP.s = IPS_ALERT; IDSetSwitch(&FocusPowerSP, "Focuser sleep mode operation failed. Response: %s.", response); return true; } } else { if (isSimulation()) strncpy(response, "WAKE", TCFS_MAX_CMD); if (strcmp(response, "WAKE") == 0) { FocusPowerSP.s = IPS_OK; IDSetSwitch(&FocusPowerSP, "Focuser is awake."); FocusAbsPosNP.s = IPS_OK; IDSetNumber(&FocusAbsPosNP, nullptr); // if (FocusTemperatureNP) { FocusTemperatureNP.s = IPS_OK; IDSetNumber(&FocusTemperatureNP, nullptr); } return true; } else { FocusPowerSP.s = IPS_ALERT; IDSetSwitch(&FocusPowerSP, "Focuser wake up operation failed. Response: %s", response); return true; } } } // Do not process any command if focuser is asleep if (isConnected() && FocusPowerSP.sp[0].s == ISS_ON) { ISwitchVectorProperty *svp = getSwitch(name); if (svp) { svp->s = IPS_IDLE; LOG_WARN("Focuser is still in sleep mode. Wake up in order to issue commands."); IDSetSwitch(svp, nullptr); } return true; } if (!strcmp(FocusModeSP.name, name)) { IUUpdateSwitch(&FocusModeSP, states, names, n); FocusModeSP.s = IPS_OK; ISwitch *sp = IUFindOnSwitch(&FocusModeSP); if (!strcmp(sp->name, "Manual")) { if (!isSimulation() && !SetManualMode()) { IUResetSwitch(&FocusModeSP); FocusModeSP.s = IPS_ALERT; IDSetSwitch(&FocusModeSP, "Error switching to manual mode. No reply from TCF-S. Try again."); return true; } } else if (!strcmp(sp->name, "Auto A")) { dispatch_command(FAMODE); read_tcfs(response); if (!isSimulation() && strcmp(response, "A") != 0) { IUResetSwitch(&FocusModeSP); FocusModeSP.s = IPS_ALERT; IDSetSwitch(&FocusModeSP, "Error switching to Auto Mode A. No reply from TCF-S. Try again."); return true; } LOG_INFO("Entered Auto Mode A"); currentMode = MODE_A; } else { dispatch_command(FBMODE); read_tcfs(response); if (!isSimulation() && strcmp(response, "B") != 0) { IUResetSwitch(&FocusModeSP); FocusModeSP.s = IPS_ALERT; IDSetSwitch(&FocusModeSP, "Error switching to Auto Mode B. No reply from TCF-S. Try again."); return true; } LOG_INFO("Entered Auto Mode B"); currentMode = MODE_B; } IDSetSwitch(&FocusModeSP, nullptr); return true; } if (!strcmp(FocusGotoSP.name, name)) { if (FocusModeSP.sp[0].s != ISS_ON) { FocusGotoSP.s = IPS_IDLE; IDSetSwitch(&FocusGotoSP, nullptr); LOG_WARN("The focuser can only be moved in Manual mode."); return false; } IUUpdateSwitch(&FocusGotoSP, states, names, n); FocusGotoSP.s = IPS_BUSY; ISwitch *sp = IUFindOnSwitch(&FocusGotoSP); // Min if (!strcmp(sp->name, "FOCUS_MIN")) { targetTicks = currentPosition; MoveRelFocuser(FOCUS_INWARD, currentPosition); IDSetSwitch(&FocusGotoSP, "Moving focuser to minimum position..."); } // Center else if (!strcmp(sp->name, "FOCUS_CENTER")) { dispatch_command(FCENTR); FocusAbsPosNP.s = FocusRelPosNP.s = IPS_BUSY; IDSetNumber(&FocusAbsPosNP, nullptr); IDSetNumber(&FocusRelPosNP, nullptr); IDSetSwitch(&FocusGotoSP, "Moving focuser to center position %d...", isTCFS3 ? 5000 : 3500); return true; } // Max else if (!strcmp(sp->name, "FOCUS_MAX")) { unsigned int delta = 0; delta = FocusAbsPosN[0].max - currentPosition; MoveRelFocuser(FOCUS_OUTWARD, delta); IDSetSwitch(&FocusGotoSP, "Moving focuser to maximum position %g...", FocusAbsPosN[0].max); } // Home else if (!strcmp(sp->name, "FOCUS_HOME")) { dispatch_command(FHOME); read_tcfs(response); if (isSimulation()) strncpy(response, "DONE", TCFS_MAX_CMD); if (strcmp(response, "DONE") == 0) { IUResetSwitch(&FocusGotoSP); FocusGotoSP.s = IPS_OK; IDSetSwitch(&FocusGotoSP, "Moving focuser to new calculated position based on temperature..."); return true; } else { IUResetSwitch(&FocusGotoSP); FocusGotoSP.s = IPS_ALERT; IDSetSwitch(&FocusGotoSP, "Failed to move focuser to home position!"); return true; } } IDSetSwitch(&FocusGotoSP, nullptr); return true; } // handle quiet mode on/off if (!strcmp(FocusTelemetrySP.name, name)) { IUUpdateSwitch(&FocusTelemetrySP, states, names, n); bool quiet = false; ISwitch *sp = IUFindOnSwitch(&FocusTelemetrySP); // Telemetry off if (!strcmp(sp->name, "FOCUS_TELEMETRY_OFF")) { dispatch_command(FQUIET, 1); quiet = true; } // Telemetry On else dispatch_command(FQUIET, 0); if (read_tcfs(response) == false) { IUResetSwitch(&FocusTelemetrySP); FocusTelemetrySP.s = IPS_ALERT; IDSetSwitch(&FocusTelemetrySP, "Error reading TCF-S reply."); return true; } if (isSimulation()) strncpy(response, "DONE", TCFS_MAX_CMD); if (strcmp(response, "DONE") == 0) { FocusTelemetrySP.s = IPS_OK; IDSetSwitch(&FocusTelemetrySP, quiet ? "Focuser Telemetry is off." : "Focuser Telemetry is on."); // if (FocusTemperatureNP) { FocusTemperatureNP.s = quiet?IPS_IDLE:IPS_OK; IDSetNumber(&FocusTemperatureNP, nullptr); } return true; } else { FocusTelemetrySP.s = IPS_ALERT; IDSetSwitch(&FocusTelemetrySP, "Focuser telemetry mode failed. Response: %s.", response); return true; } } } return INDI::Focuser::ISNewSwitch(dev, name, states, names, n); }
bool FocuserInterface::processNumber(const char *dev, const char *name, double values[], char *names[], int n) { // This is for our device // Now lets see if it's something we process here if (strcmp(name, "FOCUS_TIMER") == 0) { FocusDirection dir; int speed; int t; // first we get all the numbers just sent to us IUUpdateNumber(&FocusTimerNP, values, names, n); // Now lets find what we need for this move speed = FocusSpeedN[0].value; if (FocusMotionS[0].s == ISS_ON) dir = FOCUS_INWARD; else dir = FOCUS_OUTWARD; t = FocusTimerN[0].value; lastTimerValue = t; FocusTimerNP.s = MoveFocuser(dir, speed, t); IDSetNumber(&FocusTimerNP, nullptr); return true; } if (strcmp(name, "FOCUS_SPEED") == 0) { FocusSpeedNP.s = IPS_OK; int current_speed = FocusSpeedN[0].value; IUUpdateNumber(&FocusSpeedNP, values, names, n); if (SetFocuserSpeed(FocusSpeedN[0].value) == false) { FocusSpeedN[0].value = current_speed; FocusSpeedNP.s = IPS_ALERT; } // Update client display IDSetNumber(&FocusSpeedNP, nullptr); return true; } if (strcmp(name, "ABS_FOCUS_POSITION") == 0) { int newPos = (int)values[0]; if (newPos < FocusAbsPosN[0].min) { FocusAbsPosNP.s = IPS_ALERT; IDSetNumber(&FocusAbsPosNP, nullptr); DEBUGFDEVICE(dev, Logger::DBG_ERROR, "Requested position out of bound. Focus minimum position is %g", FocusAbsPosN[0].min); return false; } else if (newPos > FocusAbsPosN[0].max) { FocusAbsPosNP.s = IPS_ALERT; IDSetNumber(&FocusAbsPosNP, nullptr); DEBUGFDEVICE(dev, Logger::DBG_ERROR, "Requested position out of bound. Focus maximum position is %g", FocusAbsPosN[0].max); return false; } IPState ret; if ((ret = MoveAbsFocuser(newPos)) == IPS_OK) { FocusAbsPosNP.s = IPS_OK; IUUpdateNumber(&FocusAbsPosNP, values, names, n); DEBUGFDEVICE(dev, Logger::DBG_SESSION, "Focuser moved to position %d", newPos); IDSetNumber(&FocusAbsPosNP, nullptr); return true; } else if (ret == IPS_BUSY) { FocusAbsPosNP.s = IPS_BUSY; DEBUGFDEVICE(dev, Logger::DBG_SESSION, "Focuser is moving to position %d", newPos); IDSetNumber(&FocusAbsPosNP, nullptr); return true; } FocusAbsPosNP.s = IPS_ALERT; DEBUGDEVICE(dev, Logger::DBG_ERROR, "Focuser failed to move to new requested position."); IDSetNumber(&FocusAbsPosNP, nullptr); return false; } if (strcmp(name, "REL_FOCUS_POSITION") == 0) { int newPos = (int)values[0]; if (newPos <= 0) { DEBUGDEVICE(dev, Logger::DBG_ERROR, "Relative ticks value must be greater than zero."); FocusRelPosNP.s = IPS_ALERT; IDSetNumber(&FocusRelPosNP, nullptr); return false; } IPState ret; if (CanAbsMove()) { if (FocusMotionS[0].s == ISS_ON) { if (FocusAbsPosN[0].value - newPos < FocusAbsPosN[0].min) { FocusRelPosNP.s = IPS_ALERT; IDSetNumber(&FocusRelPosNP, nullptr); DEBUGFDEVICE(dev, Logger::DBG_ERROR, "Requested position out of bound. Focus minimum position is %g", FocusAbsPosN[0].min); return false; } } else { if (FocusAbsPosN[0].value + newPos > FocusAbsPosN[0].max) { FocusRelPosNP.s = IPS_ALERT; IDSetNumber(&FocusRelPosNP, nullptr); DEBUGFDEVICE(dev, Logger::DBG_ERROR, "Requested position out of bound. Focus maximum position is %g", FocusAbsPosN[0].max); return false; } } } if ((ret = MoveRelFocuser((FocusMotionS[0].s == ISS_ON ? FOCUS_INWARD : FOCUS_OUTWARD), newPos)) == IPS_OK) { FocusRelPosNP.s = FocusAbsPosNP.s = IPS_OK; IUUpdateNumber(&FocusRelPosNP, values, names, n); IDSetNumber(&FocusRelPosNP, "Focuser moved %d steps %s", newPos, FocusMotionS[0].s == ISS_ON ? "inward" : "outward"); IDSetNumber(&FocusAbsPosNP, nullptr); return true; } else if (ret == IPS_BUSY) { IUUpdateNumber(&FocusRelPosNP, values, names, n); FocusRelPosNP.s = FocusAbsPosNP.s = IPS_BUSY; IDSetNumber(&FocusAbsPosNP, "Focuser is moving %d steps %s...", newPos, FocusMotionS[0].s == ISS_ON ? "inward" : "outward"); IDSetNumber(&FocusAbsPosNP, nullptr); return true; } FocusRelPosNP.s = IPS_ALERT; DEBUGDEVICE(dev, Logger::DBG_ERROR, "Focuser failed to move to new requested position."); IDSetNumber(&FocusRelPosNP, nullptr); return false; } return false; }
void INDI::Focuser::processButton(const char * button_n, ISState state) { //ignore OFF if (state == ISS_OFF) return; FocusTimerN[0].value = lastTimerValue; IPState rc= IPS_IDLE; // Abort if (!strcmp(button_n, "Abort Focus")) { if (AbortFocuser()) { AbortSP.s = IPS_OK; DEBUG(INDI::Logger::DBG_SESSION, "Focuser aborted."); if (CanAbsMove() && FocusAbsPosNP.s != IPS_IDLE) { FocusAbsPosNP.s = IPS_IDLE; IDSetNumber(&FocusAbsPosNP, NULL); } if (CanRelMove() && FocusRelPosNP.s != IPS_IDLE) { FocusRelPosNP.s = IPS_IDLE; IDSetNumber(&FocusRelPosNP, NULL); } } else { AbortSP.s = IPS_ALERT; DEBUG(INDI::Logger::DBG_ERROR, "Aborting focuser failed."); } IDSetSwitch(&AbortSP, NULL); } // Focus In else if (!strcmp(button_n, "Focus In")) { if (FocusMotionS[FOCUS_INWARD].s != ISS_ON) { FocusMotionS[FOCUS_INWARD].s = ISS_ON; FocusMotionS[FOCUS_OUTWARD].s = ISS_OFF; IDSetSwitch(&FocusMotionSP, NULL); } if (HasVariableSpeed()) { rc = MoveFocuser(FOCUS_INWARD, FocusSpeedN[0].value, FocusTimerN[0].value); FocusTimerNP.s = rc; IDSetNumber(&FocusTimerNP,NULL); } else if (CanRelMove()) { rc=MoveRelFocuser(FOCUS_INWARD, FocusRelPosN[0].value); if (rc == IPS_OK) { FocusRelPosNP.s=IPS_OK; IDSetNumber(&FocusRelPosNP, "Focuser moved %d steps inward", (int) FocusRelPosN[0].value); IDSetNumber(&FocusAbsPosNP, NULL); } else if (rc == IPS_BUSY) { FocusRelPosNP.s=IPS_BUSY; IDSetNumber(&FocusAbsPosNP, "Focuser is moving %d steps inward...", (int) FocusRelPosN[0].value); } } } else if (!strcmp(button_n, "Focus Out")) { if (FocusMotionS[FOCUS_OUTWARD].s != ISS_ON) { FocusMotionS[FOCUS_INWARD].s = ISS_OFF; FocusMotionS[FOCUS_OUTWARD].s = ISS_ON; IDSetSwitch(&FocusMotionSP, NULL); } if (HasVariableSpeed()) { rc = MoveFocuser(FOCUS_OUTWARD, FocusSpeedN[0].value, FocusTimerN[0].value); FocusTimerNP.s = rc; IDSetNumber(&FocusTimerNP,NULL); } else if (CanRelMove()) { rc=MoveRelFocuser(FOCUS_OUTWARD, FocusRelPosN[0].value); if (rc == IPS_OK) { FocusRelPosNP.s=IPS_OK; IDSetNumber(&FocusRelPosNP, "Focuser moved %d steps outward", (int) FocusRelPosN[0].value); IDSetNumber(&FocusAbsPosNP, NULL); } else if (rc == IPS_BUSY) { FocusRelPosNP.s=IPS_BUSY; IDSetNumber(&FocusAbsPosNP, "Focuser is moving %d steps outward...", (int) FocusRelPosN[0].value); } } } }