#if 0
    INDI
    Copyright (C) 2003 Elwood C. Downey

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#endif

/* simulate a telescope OTA on an INDI interface.
 * it offers a focus control and a filter wheel.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <math.h>
#include <sys/time.h>
#include <unistd.h>

#include "astro.h"
#include "indidevapi.h"

static void sendFoc (char *msg);
static void otaSim (void *);

#define	POLLMS		250		/* poll period, ms */
#define	FOCSPD		25		/* µm/sec */
#define	FOCMAX		500		/* max foc pos, µm */
#define	FOCMIN		(-500)		/* min foc pos, µm */
#define	FOCSTP		10		/* foc step, µm */
#define	FOCDEF		100		/* foc default position, µm */
#define	FILSPD		2		/* sec/filter */

/* operaional info */
#define	MYDEV	"OTA"			/* Device name we call ourselves */
static double targetFoc;		/* target focus target */
static ISwitch *targetFil;		/* target filter */

/* INDI controls */

static INumber focN = {"Focus", "Focus,  µm", "%5.2f", FOCMIN, FOCMAX,
							    FOCSTP, FOCDEF};
static INumberVectorProperty focNv = {MYDEV, "Focuser", "Focuser", "", IP_RW,
    0, IPS_OK, &focN, 1};

static ISwitch filS[] = {
    {"B", "Blue",    ISS_OFF},
    {"V", "Visible", ISS_OFF},
    {"R", "Red",     ISS_ON},
    {"I", "IR",      ISS_OFF},
    {"H", "HII",     ISS_OFF},
    {"C", "Clear",   ISS_OFF},
};
static ISwitchVectorProperty filSv = {MYDEV, "Filter", "Filter", "", IP_RW, 
    ISR_1OFMANY, 0, IPS_OK, filS, NARRAY(filS)};

/* send client definitions of all properties */
void
ISGetProperties (const char *dev)
{
	static int inited;

	if (dev && strcmp (MYDEV, dev))
	    return;

	if (!inited) {
	    int i;

	    /* start timer to simulate mount motion */
	    IEAddTimer (POLLMS, otaSim, NULL);
	    inited = 1;

	    /* init targetFil from filS[] initialization */
	    for (i = 0; i < NARRAY(filS); i++)
		if (filS[i].s == ISS_ON)
		    targetFil = &filS[i];
	}

	IDDefNumber (&focNv, NULL);
	sendFoc(NULL);

	IDDefSwitch (&filSv, NULL);
	IDSetSwitch (&filSv, NULL);
}

void
ISNewText (const char *dev, const char *name, char *texts[], char *names[],
int n)
{
}

void
ISNewBLOB (const char *dev, const char *name, int sizes[],
    int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
{
}

/* client is telling us to set a new target focus pos */
void
ISNewNumber (const char *dev, const char *name, double *doubles, char *names[],
int n)
{
	/* ignore if not ours */
	if (strcmp (dev, MYDEV))
	    return;

	if (!strcmp (name, focNv.name)) {
	    /* new focus pos */
	    char msg[64];
	    double f = doubles[0];
	    if (f < focN.min || f > focN.max)
		IDMessage (dev, "Focus must be %g .. %g", focN.min, focN.max);
	    else {
		targetFoc = f;
		focNv.s = IPS_BUSY;;
		sprintf (msg, "Moving focus to %g", doubles[0]);
		sendFoc (msg);
	    }
	    return;
	}
}

/* client is telling us to set a new target filter pos */
void
ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[],
int n)
{
	/* ignore if not ours */
	if (strcmp (dev, MYDEV))
	    return;

	if (!strcmp (name, filSv.name)) {
	    int i;

	    for (i = 0; i < n; i++) {
		if (states[i] == ISS_ON) {
		    ISwitch *sp = IUFindSwitch (&filSv, names[i]);
		    if (sp) {
			filSv.s = IPS_BUSY;
			IDSetSwitch (&filSv, "Moving to filter %s", sp->label);
			targetFil = sp;
		    }
		    break;
		}
	    }
	}
}

/* update the "ota" over time */
void
otaSim (void *p)
{
	static struct timeval litv, lotv;	/* filter and focus timers */
	struct timeval tv;
	char msg[128];
	double dt;
	int i;

	/* update elapsed time since last poll, don't presume exactly POLLMS */
	gettimeofday (&tv, NULL);

	/* process focus per current state */
	switch (focNv.s) {
	case IPS_BUSY:
	    /* moving */

	    if (lotv.tv_sec == 0 && lotv.tv_usec == 0)
		lotv = tv;
	    dt = tv.tv_sec - lotv.tv_sec + (tv.tv_usec - lotv.tv_usec)/1e6;
	    lotv = tv;

	    if (targetFoc > focN.value) {
		focN.value += dt*FOCSPD;
		if (focN.value >= targetFoc) {
		    focN.value = targetFoc;
		    focNv.s = IPS_OK;
		}
	    } else {
		focN.value -= dt*FOCSPD;
		if (focN.value <= targetFoc) {
		    focN.value = targetFoc;
		    focNv.s = IPS_OK;
		}
	    }

	    if (focNv.s == IPS_OK) {
		sprintf (msg, "Focus now in position at %g", focN.value);
		sendFoc (msg);
		memset (&lotv, 0, sizeof(lotv));
	    } else
		sendFoc (NULL);		/* send new pos but no message */

	    break;

	default:
	    break;
	}

	/* process filter per current state */
	switch (filSv.s) {
	case IPS_BUSY:
	    /* moving */

	    if (litv.tv_sec == 0 && litv.tv_usec == 0)
		litv = tv;
	    dt = tv.tv_sec - litv.tv_sec + (tv.tv_usec - litv.tv_usec)/1e6;

	    if (dt > FILSPD) {
		/* check for success, else advance */
		for (i = 0; i < NARRAY(filS); i++) {
		    if (filS[i].s == ISS_ON) {
			if (targetFil == &filS[i]) {
			    filSv.s = IPS_OK;
			    IDSetSwitch (&filSv, "Filter at %s", filS[i].label);
			    memset (&litv, 0, sizeof(litv));
			} else {
			    IDSetSwitch (&filSv, "Filter moving past %s",
								filS[i].label);
			    filS[i].s = ISS_OFF;
			    filS[(i+1)%NARRAY(filS)].s = ISS_ON;
			    litv = tv;
			}
			break;
		    }
		}
	    }

	    break;

	default:
	    break;
	}

	/* again */
	IEAddTimer (POLLMS, otaSim, NULL);
}

/* send current focus to client with optional message */
static void
sendFoc (char *msg)
{
	IDSetNumber (&focNv, msg);
}

/* For RCS Only -- Do Not Edit */
static char *rcsid[2] = {(char *)rcsid, "@(#) $RCSfile: ota.c,v $ $Date: 2005/05/08 02:52:25 $ $Revision: 1.11 $ $Name:  $"};
