627 lines
14 KiB
C
627 lines
14 KiB
C
/*
|
|
* -*- linux-c -*-
|
|
* Linux Kernel Module for the Audine Camera
|
|
* Copyright (C) 2001 Peter Kirchgessner
|
|
* http://www.kirchgessner.net, mailto:peter@kirchgessner.net
|
|
*
|
|
* Modified by F. Manenti <oss_astr_cav@arcanet.it> for the use in the
|
|
* NOVA environment (nova.sourceforge.net)
|
|
*
|
|
*
|
|
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* The sample interface routines for the module have been taken from the
|
|
* Linux Kernel Module Programming Guide by Ori Pomerantz contained
|
|
* in the Linux Documentation Project.
|
|
*
|
|
*/
|
|
|
|
#include "audineccd.h"
|
|
#include "audinelib.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <sys/ioctl.h>
|
|
#include <errno.h>
|
|
|
|
#define AUD_DEVICE_FILE "/dev/audine"
|
|
|
|
#define ERRNORET(a) return (errno = a, -(a))
|
|
|
|
#define AUD_HANDLE_CHECK(a) \
|
|
{ if (!(a)) ERRNORET (EINVAL); if ((a)->fd < 0) ERRNORET (EBADF); }
|
|
|
|
struct aud_handle_s {
|
|
int fd;
|
|
int single_read;
|
|
};
|
|
|
|
|
|
AUD_HANDLE aud_open(void)
|
|
{
|
|
AUD_HANDLE aud = (AUD_HANDLE) calloc(1, sizeof(struct aud_handle_s));
|
|
|
|
if (!aud)
|
|
return 0;
|
|
|
|
aud->fd = open(AUD_DEVICE_FILE, O_RDWR);
|
|
if (aud->fd < 0) {
|
|
free(aud);
|
|
return 0;
|
|
}
|
|
|
|
aud->single_read = 1;
|
|
|
|
return aud;
|
|
}
|
|
|
|
|
|
void aud_close(AUD_HANDLE aud)
|
|
{
|
|
if (aud) {
|
|
if (aud->fd >= 0)
|
|
close(aud->fd);
|
|
free(aud);
|
|
}
|
|
}
|
|
|
|
|
|
char *aud_version(const AUD_HANDLE aud)
|
|
{
|
|
static struct ccd_capability Info;
|
|
char *version = "";
|
|
int ret;
|
|
|
|
if (aud) {
|
|
ret = ioctl(aud->fd, CCD_RD_VER, &Info);
|
|
if (ret == 0)
|
|
version = &(Info.name[0]);
|
|
}
|
|
return version;
|
|
}
|
|
|
|
|
|
int aud_clear(const AUD_HANDLE aud, int nclear)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (nclear > 0)
|
|
ret = ioctl(aud->fd, CCD_CLR, &nclear);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int aud_binning_set(const AUD_HANDLE aud, int vb, int hb)
|
|
{
|
|
struct ccd_capability Info;
|
|
|
|
AUD_HANDLE_CHECK(aud);
|
|
if ((vb < 1) || (vb > 4) || (hb < 1) || (hb > 4))
|
|
ERRNORET(EINVAL);
|
|
|
|
Info.width = vb;
|
|
Info.height = hb;
|
|
return ioctl(aud->fd, CCD_SET_BNN, &Info);
|
|
}
|
|
|
|
|
|
int aud_binning_get(const AUD_HANDLE aud, int *vb, int *hb)
|
|
{
|
|
struct ccd_capability Info;
|
|
int ret;
|
|
|
|
AUD_HANDLE_CHECK(aud);
|
|
|
|
Info.width = 0;
|
|
Info.height = 0;
|
|
ret = ioctl(aud->fd, CCD_SET_BNN, &Info);
|
|
if (ret != 0)
|
|
return ret;
|
|
if (vb)
|
|
*vb = Info.width;
|
|
if (hb)
|
|
*hb = Info.height;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int aud_geometry_set(const AUD_HANDLE aud, int x, int y, int width,
|
|
int height)
|
|
{
|
|
struct ccd_capability Info;
|
|
|
|
AUD_HANDLE_CHECK(aud);
|
|
|
|
Info.minwidth = x;
|
|
Info.minheight = y;
|
|
Info.width = width;
|
|
Info.height = height;
|
|
return ioctl(aud->fd, CCD_SET_WND, &Info);
|
|
}
|
|
|
|
|
|
int aud_geometry_reset(const AUD_HANDLE aud)
|
|
{
|
|
AUD_HANDLE_CHECK(aud);
|
|
|
|
return ioctl(aud->fd, CCD_RST_WND);
|
|
}
|
|
|
|
|
|
int aud_geometry_get(const AUD_HANDLE aud, int *xorigin, int *yorigin,
|
|
int *winwidth, int *winheight, int *color)
|
|
{
|
|
struct ccd_capability Info;
|
|
int ret;
|
|
|
|
AUD_HANDLE_CHECK(aud);
|
|
|
|
ret = ioctl(aud->fd, CCD_RD_GEOM, &Info);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
if (xorigin)
|
|
*xorigin = Info.minwidth;
|
|
if (yorigin)
|
|
*yorigin = Info.minheight;
|
|
if (winwidth)
|
|
*winwidth = Info.width;
|
|
if (winheight)
|
|
*winheight = Info.height;
|
|
if (color)
|
|
*color = Info.color;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int aud_port_set(const AUD_HANDLE aud, int base)
|
|
{
|
|
AUD_HANDLE_CHECK(aud);
|
|
if (base <= 0)
|
|
return -EINVAL;
|
|
return ioctl(aud->fd, CCD_SET_PRT, &base);
|
|
}
|
|
|
|
|
|
int aud_port_get(const AUD_HANDLE aud, int *base)
|
|
{
|
|
AUD_HANDLE_CHECK(aud);
|
|
if (!base)
|
|
return -1;
|
|
*base = 0;
|
|
return ioctl(aud->fd, CCD_SET_PRT, base);
|
|
}
|
|
|
|
|
|
static int aud_line_ctrl_set(const AUD_HANDLE aud, int cmd, int ctrl)
|
|
{
|
|
AUD_HANDLE_CHECK(aud);
|
|
if ((ctrl != 1) && (ctrl != 2) && (ctrl != 4) && (ctrl != 8))
|
|
ERRNORET(EINVAL);
|
|
return ioctl(aud->fd, cmd, &ctrl);
|
|
}
|
|
|
|
|
|
static int aud_line_set(const AUD_HANDLE aud, int cmd, int on_off)
|
|
{
|
|
AUD_HANDLE_CHECK(aud);
|
|
if (on_off != 0)
|
|
on_off = 1;
|
|
return ioctl(aud->fd, cmd, &on_off);
|
|
}
|
|
|
|
|
|
int aud_amplifier_ctrl_set(const AUD_HANDLE aud, int ctrl)
|
|
{
|
|
return aud_line_ctrl_set(aud, CCD_SET_AMP, ctrl);
|
|
}
|
|
|
|
|
|
int aud_amplifier_set(const AUD_HANDLE aud, int on_off)
|
|
{
|
|
return aud_line_set(aud, CCD_SWTC_AMP, on_off);
|
|
}
|
|
|
|
|
|
int aud_shutter_ctrl_set(const AUD_HANDLE aud, int ctrl)
|
|
{
|
|
return aud_line_ctrl_set(aud, CCD_SET_SHT, ctrl);
|
|
}
|
|
|
|
|
|
int aud_shutter_set(const AUD_HANDLE aud, int on_off)
|
|
{
|
|
return aud_line_set(aud, CCD_SWTC_SHT, on_off);
|
|
}
|
|
|
|
|
|
int aud_aux0_ctrl_set(const AUD_HANDLE aud, int ctrl)
|
|
{
|
|
return aud_line_ctrl_set(aud, CCD_SET_AX0, ctrl);
|
|
}
|
|
|
|
|
|
int aud_aux0_set(const AUD_HANDLE aud, int on_off)
|
|
{
|
|
return aud_line_set(aud, CCD_SWTC_AX0, on_off);
|
|
}
|
|
|
|
|
|
int aud_aux1_ctrl_set(const AUD_HANDLE aud, int ctrl)
|
|
{
|
|
return aud_line_ctrl_set(aud, CCD_SET_AX1, ctrl);
|
|
}
|
|
|
|
|
|
int aud_aux1_set(const AUD_HANDLE aud, int on_off)
|
|
{
|
|
return aud_line_set(aud, CCD_SWTC_AX1, on_off);
|
|
}
|
|
|
|
|
|
int aud_ccd_info_get(const AUD_HANDLE aud, char **name, int *width,
|
|
int *height, int *color)
|
|
{
|
|
static struct ccd_capability Info;
|
|
int ret;
|
|
|
|
AUD_HANDLE_CHECK(aud);
|
|
if ((ret = ioctl(aud->fd, CCD_RD_CHIP, &Info)) != 0)
|
|
return ret;
|
|
|
|
if (name)
|
|
*name = Info.name;
|
|
if (width)
|
|
*width = Info.width;
|
|
if (height)
|
|
*height = Info.height;
|
|
if (color)
|
|
*color = Info.color;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int aud_ccd_listentry_get(const AUD_HANDLE aud, int entry, char **name,
|
|
int *width, int *height, int *color)
|
|
{
|
|
static struct ccd_capability Info;
|
|
int ret;
|
|
|
|
AUD_HANDLE_CHECK(aud);
|
|
Info.color = entry;
|
|
if ((ret = ioctl(aud->fd, CCD_RD_CCDL, &Info)) != 0)
|
|
return ret;
|
|
|
|
if (name)
|
|
*name = Info.name;
|
|
if (width)
|
|
*width = Info.width;
|
|
if (height)
|
|
*height = Info.height;
|
|
if (color)
|
|
*color = Info.color;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void aud_single_read_set(AUD_HANDLE aud, int single_read)
|
|
{
|
|
if (aud)
|
|
aud->single_read = single_read;
|
|
}
|
|
|
|
|
|
int aud_image_read(const AUD_HANDLE aud, char **buf, int *bufsize,
|
|
int *width, int *height, int *color)
|
|
{
|
|
int ret;
|
|
int nbytes, nread, len;
|
|
char *imgbuf;
|
|
|
|
AUD_HANDLE_CHECK(aud);
|
|
if ((!buf) || (!bufsize))
|
|
ERRNORET(EINVAL);
|
|
if ((!width) || (!height) || (!color))
|
|
ERRNORET(EINVAL);
|
|
|
|
if ((ret = aud_geometry_get(aud, 0, 0, width, height, color)) != 0)
|
|
return ret;
|
|
|
|
/* We get 2 bytes per pixel */
|
|
nbytes = *width * *height * 2;
|
|
|
|
/* Do we have to free the user buffer ? */
|
|
if ((*bufsize < nbytes) && (*buf)) {
|
|
free(*buf);
|
|
*buf = 0;
|
|
*bufsize = 0;
|
|
}
|
|
|
|
/* If buffer not supplied, allocate buffer */
|
|
if (!(*buf)) {
|
|
*buf = malloc(nbytes);
|
|
if (!*buf)
|
|
ERRNORET(ENOMEM);
|
|
*bufsize = nbytes;
|
|
}
|
|
|
|
/* Start reading image */
|
|
if ((ret = ioctl(aud->fd, CCD_RD_IMG)) != 0)
|
|
return ret;
|
|
|
|
imgbuf = *buf;
|
|
while (nbytes > 0) {
|
|
if (aud->single_read) {
|
|
nread = nbytes;
|
|
} else {
|
|
nread = *width * 2;
|
|
if (nread > nbytes)
|
|
nread = nbytes;
|
|
}
|
|
len = read(aud->fd, imgbuf, nread);
|
|
if (len <= 0)
|
|
return -1;
|
|
nbytes -= len;
|
|
imgbuf += len;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void aud_ccdstruct_log(const char *fct, int ret,
|
|
const struct ccd_capability *info)
|
|
{
|
|
printf("\nFunction %s returned %d\n", fct, ret);
|
|
if (ret != 0)
|
|
return;
|
|
printf("Name : %-32s\n", info->name);
|
|
printf
|
|
("Width: %-6d Height: %-6d Minwidth: %-6d Minheight: %-6d Color: %d\n",
|
|
info->width, info->height, info->minwidth, info->minheight,
|
|
info->color);
|
|
}
|
|
|
|
|
|
void aud_ioctl_test(AUD_HANDLE aud)
|
|
{
|
|
int ret;
|
|
struct ccd_capability info;
|
|
char *buf = 0, *name;
|
|
int bufsize = 0, width, height, color;
|
|
int j;
|
|
|
|
if ((!aud) || (aud->fd < 0))
|
|
return;
|
|
|
|
/* Read driver version */
|
|
ret = ioctl(aud->fd, CCD_RD_VER, &info);
|
|
aud_ccdstruct_log("CCD_RD_VER", ret, &info);
|
|
|
|
/* Read chip information */
|
|
ret = ioctl(aud->fd, CCD_RD_CHIP, &info);
|
|
aud_ccdstruct_log("CCD_RD_CHIP", ret, &info);
|
|
|
|
/* Read geometry */
|
|
ret = ioctl(aud->fd, CCD_RD_GEOM, &info);
|
|
aud_ccdstruct_log("CCD_RD_GEOM", ret, &info);
|
|
|
|
/* Set Window */
|
|
info.minwidth = 1;
|
|
info.minheight = 2;
|
|
info.width = 200;
|
|
info.height = 100;
|
|
ret = ioctl(aud->fd, CCD_SET_WND, &info);
|
|
printf("\nCalled CCD_SET_WND: (1,2) (200,100)\n");
|
|
|
|
/* Read geometry */
|
|
ret = ioctl(aud->fd, CCD_RD_GEOM, &info);
|
|
aud_ccdstruct_log("CCD_RD_GEOM", ret, &info);
|
|
|
|
/* Set binning */
|
|
info.width = 2;
|
|
info.height = 3;
|
|
printf("\nSet binning %dx%d", info.width, info.height);
|
|
ret = ioctl(aud->fd, CCD_SET_BNN, &info);
|
|
aud_ccdstruct_log("CCD_SET_BNN", ret, &info);
|
|
|
|
/* Read geometry */
|
|
ret = ioctl(aud->fd, CCD_RD_GEOM, &info);
|
|
aud_ccdstruct_log("CCD_RD_GEOM", ret, &info);
|
|
|
|
/* (Re-)Set binning */
|
|
info.width = 1;
|
|
info.height = 1;
|
|
printf("\nSet binning %dx%d", info.width, info.height);
|
|
ret = ioctl(aud->fd, CCD_SET_BNN, &info);
|
|
aud_ccdstruct_log("CCD_SET_BNN", ret, &info);
|
|
|
|
/* Read geometry */
|
|
ret = ioctl(aud->fd, CCD_RD_GEOM, &info);
|
|
aud_ccdstruct_log("CCD_RD_GEOM", ret, &info);
|
|
|
|
/* Clear two times */
|
|
info.width = 2;
|
|
printf("\nStart clear %d times\n", info.width);
|
|
fflush(stdout);
|
|
ret = ioctl(aud->fd, CCD_CLR, &info.width);
|
|
printf("Clear finished.\n");
|
|
|
|
/* Reading */
|
|
printf("Start reading image\n");
|
|
ret = aud_image_read(aud, &buf, &bufsize, &width, &height, &color);
|
|
printf
|
|
("Finished reading: ret=%d width=%d height=%d color=%d bufsize=%d\n",
|
|
ret, width, height, color, bufsize);
|
|
|
|
/* Set binning */
|
|
info.width = 2;
|
|
info.height = 3;
|
|
printf("\nSet binning %dx%d", info.width, info.height);
|
|
ret = ioctl(aud->fd, CCD_SET_BNN, &info);
|
|
aud_ccdstruct_log("CCD_SET_BNN", ret, &info);
|
|
|
|
/* Reading */
|
|
printf("Start reading small image\n");
|
|
ret = aud_image_read(aud, &buf, &bufsize, &width, &height, &color);
|
|
printf
|
|
("Finished reading: ret=%d width=%d height=%d color=%d bufsize=%d\n",
|
|
ret, width, height, color, bufsize);
|
|
|
|
/* Reset window */
|
|
ret = ioctl(aud->fd, CCD_RST_WND);
|
|
printf("\nReset window\n");
|
|
|
|
/* Reset binning */
|
|
info.width = 1;
|
|
info.height = 1;
|
|
printf("\nSet binning %dx%d", info.width, info.height);
|
|
ret = ioctl(aud->fd, CCD_SET_BNN, &info);
|
|
aud_ccdstruct_log("CCD_SET_BNN", ret, &info);
|
|
|
|
/* Read geometry */
|
|
ret = ioctl(aud->fd, CCD_RD_GEOM, &info);
|
|
aud_ccdstruct_log("CCD_RD_GEOM", ret, &info);
|
|
|
|
/* Reading */
|
|
printf("Start reading large image\n");
|
|
ret = aud_image_read(aud, &buf, &bufsize, &width, &height, &color);
|
|
printf
|
|
("Finished reading: ret=%d width=%d height=%d color=%d bufsize=%d\n",
|
|
ret, width, height, color, bufsize);
|
|
|
|
/* Read current port */
|
|
ret = aud_port_get(aud, &j);
|
|
printf("\naud_port_get returned with %d. Port=0x%x\n", ret, j);
|
|
|
|
printf("\nList of supported CCDs:\n");
|
|
j = 0;
|
|
while (aud_ccd_listentry_get(aud, j, &name, &info.width, &info.height,
|
|
&info.color) == 0) {
|
|
printf("%d: %s, %dx%d, %d\n", j, name, info.width, info.height,
|
|
info.color);
|
|
j++;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#define FITS_WRITE_BOOLCARD(fp,key,value) \
|
|
{char card[81]; \
|
|
sprintf (card, "%-8.8s= %20s%50s", key, value ? "T" : "F", " "); \
|
|
fwrite (card, 1, 80, fp); }
|
|
|
|
#define FITS_WRITE_LONGCARD(fp,key,value) \
|
|
{char card[81]; \
|
|
sprintf (card, "%-8.8s= %20ld%50s", key, (long)value, " "); \
|
|
fwrite (card, 1, 80, fp); }
|
|
|
|
#define FITS_WRITE_DOUBLECARD(fp,key,value) \
|
|
{char card[81], dbl[21], *istr; \
|
|
sprintf (dbl, "%20f", (double)value); istr = strstr (dbl, "e"); \
|
|
if (istr) *istr = 'E'; \
|
|
sprintf (card, "%-8.8s= %20.20s%50s", key, dbl, " "); \
|
|
fwrite (card, 1, 80, fp); }
|
|
|
|
#define FITS_WRITE_STRINGCARD(fp,key,value) \
|
|
{char card[81]; int k;\
|
|
sprintf (card, "%-8.8s= \'%s", key, value); \
|
|
for (k = strlen (card); k < 81; k++) card[k] = ' '; \
|
|
k = strlen (key); if (k < 8) card[19] = '\''; else card[11+k] = '\''; \
|
|
fwrite (card, 1, 80, fp); }
|
|
|
|
#define FITS_WRITE_CARD(fp,value) \
|
|
{char card[81]; \
|
|
sprintf (card, "%-80.80s", value); \
|
|
fwrite (card, 1, 80, fp); }
|
|
|
|
int audine_fits_write(const char *fname, const char *img,
|
|
int width, int height)
|
|
{
|
|
FILE *fp;
|
|
char value[50];
|
|
unsigned short *us_img = (unsigned short *) img;
|
|
unsigned short *row;
|
|
int x, y, high, low;
|
|
long pos;
|
|
time_t timp;
|
|
|
|
fp = fopen(fname, "w");
|
|
if (fp) {
|
|
FITS_WRITE_BOOLCARD(fp, "SIMPLE", 1);
|
|
FITS_WRITE_LONGCARD(fp, "BITPIX", 16);
|
|
FITS_WRITE_LONGCARD(fp, "NAXIS", 2);
|
|
FITS_WRITE_LONGCARD(fp, "NAXIS1", width);
|
|
FITS_WRITE_LONGCARD(fp, "NAXIS2", height);
|
|
FITS_WRITE_DOUBLECARD(fp, "BZERO", 0.0);
|
|
FITS_WRITE_DOUBLECARD(fp, "BSCALE", 1.0);
|
|
FITS_WRITE_DOUBLECARD(fp, "DATAMIN", 0.0);
|
|
FITS_WRITE_DOUBLECARD(fp, "DATAMAX", 32767.0);
|
|
FITS_WRITE_CARD(fp, " ");
|
|
FITS_WRITE_CARD(fp, "HISTORY THIS FILE WAS GENERATED BY AUDINELIB");
|
|
FITS_WRITE_CARD(fp, " ");
|
|
|
|
timp = time(NULL);
|
|
strftime(value, sizeof(value), "%d/%m/%Y", gmtime(&timp));
|
|
FITS_WRITE_STRINGCARD(fp, "DATE", value);
|
|
|
|
FITS_WRITE_CARD(fp, "END");
|
|
|
|
/* Fill up primary HDU to multiple of 2880 bytes */
|
|
fflush(fp);
|
|
pos = ftell(fp) % 2880;
|
|
if (pos != 0) {
|
|
pos = 2880 - pos;
|
|
while (pos-- > 0)
|
|
putc(' ', fp);
|
|
}
|
|
|
|
/* FITS standard requires integer data to be most significant
|
|
byte first, */
|
|
/* image origin bottom left. We want to create an astronomical
|
|
oriented image (top is bottom, left is right) */
|
|
for (y = 0; y < height; y++) {
|
|
row = us_img + y * width;
|
|
for (x = width - 1; x >= 0; x--) {
|
|
high = (row[x] >> 8) & 0xff;
|
|
low = (row[x] & 0xff);
|
|
putc(high, fp);
|
|
putc(low, fp);
|
|
}
|
|
}
|
|
/* Fill up file to multiple of 2880 bytes */
|
|
fflush(fp);
|
|
pos = ftell(fp) % 2880;
|
|
if (pos != 0) {
|
|
pos = 2880 - pos;
|
|
while (pos-- > 0)
|
|
putc(0, fp);
|
|
}
|
|
fclose(fp);
|
|
} else {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|