summaryrefslogtreecommitdiff
path: root/camlibs/jd11
diff options
context:
space:
mode:
authorMarcus Meissner <marcus@jet.franken.de>2001-10-28 11:26:04 +0000
committerMarcus Meissner <marcus@jet.franken.de>2001-10-28 11:26:04 +0000
commit14f3866e30b878c548a9bece246068a86d37ddd7 (patch)
treecfb4136f41ef3d21be8b380935df91ba38e7c524 /camlibs/jd11
parent7ebc0343f0b053309d5fab613d12fafe4a929b76 (diff)
downloadlibgphoto2-14f3866e30b878c548a9bece246068a86d37ddd7.tar.gz
JD11 cam driver for gphoto2.
git-svn-id: https://svn.code.sf.net/p/gphoto/code/trunk/libgphoto2@2826 67ed7778-7388-44ab-90cf-0a291f65f57c
Diffstat (limited to 'camlibs/jd11')
-rw-r--r--camlibs/jd11/Makefile.am14
-rw-r--r--camlibs/jd11/decomp.c149
-rw-r--r--camlibs/jd11/decomp.h5
-rw-r--r--camlibs/jd11/jd11.c197
-rw-r--r--camlibs/jd11/jd11.html303
-rw-r--r--camlibs/jd11/serial.c381
-rw-r--r--camlibs/jd11/serial.h15
7 files changed, 1064 insertions, 0 deletions
diff --git a/camlibs/jd11/Makefile.am b/camlibs/jd11/Makefile.am
new file mode 100644
index 000000000..a5e88bd39
--- /dev/null
+++ b/camlibs/jd11/Makefile.am
@@ -0,0 +1,14 @@
+EXTRA_DIST = jd11.html
+
+camlibdir = $(prefix)/lib/gphoto2
+camlib_LTLIBRARIES = libgphoto2_jd11.la
+
+INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/libgphoto2
+
+libgphoto2_jd11_la_SOURCES = jd11.c serial.h serial.c decomp.h decomp.c
+
+libgphoto2_jd11_la_LDFLAGS =\
+ -avoid-version\
+ -L$(shell pwd)/$(top_builddir)/libgphoto2/@dotlibs@
+
+libgphoto2_jd11_la_LIBADD = -lgphoto2
diff --git a/camlibs/jd11/decomp.c b/camlibs/jd11/decomp.c
new file mode 100644
index 000000000..34272e6c8
--- /dev/null
+++ b/camlibs/jd11/decomp.c
@@ -0,0 +1,149 @@
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "decomp.h"
+
+struct chain { int left,val,right; };
+struct compstate {
+ unsigned char curmask;
+ unsigned char bytebuf;
+ unsigned char *byteptr;
+
+ struct chain cl[200];
+ int stackstart;
+};
+
+/* FLOAT_QUERY: 0.860000 1.030000 1.150000. */
+
+/********************************************************/
+/* The bit stage */
+/********************************************************/
+static inline int
+jd11_getbit(struct compstate *cs) {
+ int ret;
+ if (cs->curmask == 0x80)
+ cs->bytebuf = *cs->byteptr++;
+ ret = cs->curmask & cs->bytebuf;
+ cs->curmask >>=1;
+ if (!cs->curmask) cs->curmask = 0x80;
+ return !!ret;
+}
+
+/********************************************************/
+/* The huffman compressor stage */
+/********************************************************/
+static int
+decomp_1byte(struct compstate *cs) {
+ int xcs = cs->stackstart;
+ int xbit;
+
+ while ((cs->cl[xcs].left>=0) && (cs->cl[xcs].right>=0)) {
+ xbit = jd11_getbit(cs);
+ if (xbit)
+ xcs = cs->cl[xcs].left;
+ else
+ xcs = cs->cl[xcs].right;
+ }
+ return cs->cl[xcs].val;
+}
+
+static void
+build_huffmann_tree(struct compstate *cs) {
+ int xstack[200];
+ int i,curcl=0,curstack=0;
+ const int df[] = {
+ -180,180,1000,-90,1000,90,1000,-45,1000,45,1000,-20,1000,
+ 20,1000,-11,1000,11,1000,-6,1000,2,1000,6,-2,1000,1000
+ };
+ for (i=0;i<sizeof(df)/sizeof(df[0]);i++) {
+ if (df[i]!=1000) {
+ cs->cl[curcl].left = -1;
+ cs->cl[curcl].right = -1;
+ cs->cl[curcl].val = df[i];
+ } else {
+ cs->cl[curcl].right = xstack[--curstack];
+ cs->cl[curcl].left = xstack[--curstack];
+ }
+ xstack[curstack++] = curcl++;
+ }
+ cs->stackstart = xstack[0];
+}
+
+#define F1 0.5
+#define F2 0.0
+#define F3 0.5
+#define F4 0.0
+
+void
+picture_decomp_v1(char *compressed,char *uncompressed,int width,int height) {
+ struct compstate cs;
+ unsigned char xbyte;
+ int i=0,curbyte=0,diff,lastval;
+ int *line,*lastline;
+
+ cs.curmask = 0x80; cs.bytebuf = 0; cs.byteptr = compressed;
+
+ build_huffmann_tree(&cs);
+
+ line = (int*)malloc(sizeof(int)*width);
+ lastline= (int*)malloc(sizeof(int)*width);
+ curbyte=0;
+ memset(line,0,sizeof(line));
+ memset(lastline,0,sizeof(line));
+ for (i=0;i<width;i++) {
+ diff = decomp_1byte(&cs);
+ curbyte+=diff;
+ xbyte = curbyte;
+ if (curbyte>255) xbyte = 255;
+ if (curbyte<0) xbyte = 0;
+
+ *uncompressed++=xbyte;
+
+ line[i] = curbyte;
+ }
+ height--;
+ while (height--) {
+ lastval = line[0]; /* just before the copy */
+ memcpy(lastline,line,width*sizeof(int));
+ memset(line,0,width*sizeof(int));
+ for (i=0;i<width;i++) {
+ diff = decomp_1byte(&cs);
+ line[i]=diff+lastval;
+ if (i<width-2) {
+ lastval = (int)((lastline[i+2]*F4+lastline[i]*F2+lastline[i+1]*F1+line[i]*F3));
+ } else {
+ if (i==width-2)
+ lastval = (int)(lastline[i]*F2+lastline[i+1]*F1+line[i]*F3);
+ else
+ lastval = line[i];
+ }
+
+ xbyte = line[i];
+ if (line[i]>255) xbyte = 255;
+ if (line[i]<0) xbyte = 0;
+ *uncompressed++=xbyte;
+ }
+ }
+}
+
+/* Just blow up the picture from 6 bit uncompressed to 8 bit uncompressed */
+void
+picture_decomp_v2(char *compressed,char *uncompressed,int width,int height) {
+ struct compstate cs;
+ int i,j;
+ unsigned char xbyte;
+
+ cs.curmask = 0x80; cs.bytebuf = 0; cs.byteptr = compressed;
+ for (i=width*height;i--;) {
+ unsigned char xmask = 0x80;
+ xbyte = 0;
+ for (j=6;j--;) {
+ if (jd11_getbit(&cs))
+ xbyte|=xmask;
+ xmask>>=1;
+ }
+ *uncompressed++=xbyte;
+ }
+}
diff --git a/camlibs/jd11/decomp.h b/camlibs/jd11/decomp.h
new file mode 100644
index 000000000..30de7e597
--- /dev/null
+++ b/camlibs/jd11/decomp.h
@@ -0,0 +1,5 @@
+#ifndef _JD11_DECOMP_H
+#define _JD11_DECOMP_H
+extern void picture_decomp_v1(char *compressed,char *uncompressed,int width,int height);
+extern void picture_decomp_v2(char *compressed,char *uncompressed,int width,int height);
+#endif
diff --git a/camlibs/jd11/jd11.c b/camlibs/jd11/jd11.c
new file mode 100644
index 000000000..c6c93aa17
--- /dev/null
+++ b/camlibs/jd11/jd11.c
@@ -0,0 +1,197 @@
+/*
+ * Jenoptik JD11 Driver
+ * Copyright (C) 1999-2001 Marcus Meissner <marcus@jet.franken.de>
+ *
+ * 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
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <gphoto2.h>
+#include <gphoto2-port.h>
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+# undef _
+# define _(String) dgettext (PACKAGE, String)
+# ifdef gettext_noop
+# define N_(String) gettext_noop (String)
+# else
+# define _(String) (String)
+# define N_(String) (String)
+# endif
+#else
+# define _(String) (String)
+# define N_(String) (String)
+#endif
+
+#include "serial.h"
+
+int camera_id (CameraText *id)
+{
+ strcpy(id->text, "JD11");
+ return (GP_OK);
+}
+
+int camera_abilities (CameraAbilitiesList *list)
+{
+ CameraAbilities a;
+
+ strcpy(a.model, "Jenoptik JD11");
+ a.status = GP_DRIVER_STATUS_TESTING;
+ a.port = GP_PORT_SERIAL;
+ a.speed[0] = 115200;
+ a.speed[1] = 0;
+ a.operations = GP_OPERATION_NONE ;
+ a.file_operations = GP_FILE_OPERATION_PREVIEW;
+ a.folder_operations = GP_FOLDER_OPERATION_DELETE_ALL;
+
+ gp_abilities_list_append(list, a);
+
+ return (GP_OK);
+}
+
+static int file_list_func (CameraFilesystem *fs, const char *folder,
+ CameraList *list, void *data)
+{
+ Camera *camera = data;
+ int count, result;
+
+ result = jd11_file_count(camera->port, &count);
+ if (result != GP_OK)
+ return result;
+
+ gp_list_populate(list, "image%02i.pnm", count);
+
+ return (GP_OK);
+}
+
+static int get_file_func (CameraFilesystem *fs, const char *folder,
+ const char *filename, CameraFileType type,
+ CameraFile *file, void *user_data)
+{
+ Camera *camera = user_data;
+ int image_no, result;
+ char *data;
+ long int size;
+
+ image_no = gp_filesystem_number(fs, folder, filename);
+
+ if(image_no < 0)
+ return image_no;
+
+ switch (type) {
+ /*
+ case GP_FILE_TYPE_RAW:
+ result = jd11_get_image_raw (camera->port, image_no, &data,
+ (int*) &size);
+ break;
+ */
+ case GP_FILE_TYPE_NORMAL:
+ result = jd11_get_image_full (camera->port, image_no, &data,
+ (int*) &size);
+ break;
+ case GP_FILE_TYPE_PREVIEW:
+ result = jd11_get_image_preview (camera->port, image_no,
+ &data, (int*) &size);
+ break;
+ default:
+ return (GP_ERROR_NOT_SUPPORTED);
+ }
+
+ if (result < 0)
+ return result;
+
+ gp_file_set_name (file, filename);
+ gp_file_set_mime_type (file, "image/pnm");
+ gp_file_set_data_and_size (file, data, size);
+
+ return (GP_OK);
+}
+
+static int
+delete_all_func (CameraFilesystem *fs, const char* folder, void *data)
+{
+ Camera *camera = data;
+ if (strcmp (folder, "/"))
+ return (GP_ERROR_DIRECTORY_NOT_FOUND);
+ return jd11_erase_all(camera->port);
+}
+
+static int camera_summary (Camera *camera, CameraText *summary)
+{
+ strcpy(summary->text, _("No summary available."));
+ return (GP_OK);
+}
+
+static int camera_manual (Camera *camera, CameraText *manual)
+{
+ strcpy(manual->text,
+ _(
+ "The JD11 camera works rather well with this driver.\n"
+ "It gets a bit confused if some data is left over on the serial line,\n"
+ "and will report I/O errors on startup. Just switch the camera off and\n"
+ "on again and it will no longer do that.\n"
+ "An RS232 interface @ 115 kbit is required for image transfer.\n"
+ "The driver allows you to get\n\n"
+ " - thumbnails (64x48 PGM format)\n"
+ " - full images (640x480 PPM format)\n"
+ "No color correction and no interpolation is applied as of this time.\n"
+ "\n\n"));
+
+ return (GP_OK);
+}
+
+static int camera_about (Camera *camera, CameraText *about)
+{
+ strcpy (about->text,
+ _("JD11\n"
+ "Marcus Meissner <marcus@jet.franken.de>\n"
+ "Driver for the Jenoptik JD11 camera.\n"
+ "Protocol reverse engineered using WINE and IDA."));
+
+ return (GP_OK);
+}
+
+int camera_init (Camera *camera)
+{
+ gp_port_settings settings;
+ int ret;
+
+ /* First, set up all the function pointers */
+ camera->functions->summary = camera_summary;
+ camera->functions->manual = camera_manual;
+ camera->functions->about = camera_about;
+
+ /* Configure port */
+ gp_port_set_timeout(camera->port, 1000);
+ gp_port_get_settings(camera->port, &settings);
+ settings.serial.speed = 115200;
+ settings.serial.bits = 8;
+ settings.serial.parity = 0;
+ settings.serial.stopbits= 1;
+ gp_port_set_settings(camera->port, settings);
+
+ /* Set up the filesystem */
+ gp_filesystem_set_list_funcs(camera->fs, file_list_func, NULL,camera);
+ gp_filesystem_set_file_funcs(camera->fs,get_file_func,NULL,camera);
+ gp_filesystem_set_folder_funcs(camera->fs,NULL,delete_all_func, NULL,NULL,camera);
+ /* test camera */
+ ret = jd11_ping(camera->port);
+ return (ret);
+}
+
diff --git a/camlibs/jd11/jd11.html b/camlibs/jd11/jd11.html
new file mode 100644
index 000000000..34f3e4c67
--- /dev/null
+++ b/camlibs/jd11/jd11.html
@@ -0,0 +1,303 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="GENERATOR" content="Mozilla/4.61C-CCK-MCD Caldera Systems OpenLinux [en] (X11; I; Linux 2.2.14 i586) [Netscape]">
+ <title>Reverse Engineering the JD11 (JenCam 11) </title>
+</head>
+<body>
+
+<h1>
+Reverse Engineering the JD11 - A drama in n chapters</h1>
+
+<hr>
+<h2>
+What is the JD11?</h2>
+The JD11 is a cheap digital camera produced by JenOptik (apparenly a subsidary
+of Zeiss Jena). It was at the time of writing this available for around
+250 DM.
+<p>There is software available, a windows program called <i>FotoBee</i>
+which is a huge heap of MFC dung. It can be made to run with a small hack
+under the free Windows Emulator <a href="http://winehq.com">WINE</a>.
+<p>WINE was crucial in reverse engineering the camera at all stages of
+the project.
+<h2>
+Chapter 1:The lowlevel serial protocol</h2>
+The JD11 comes with a serial cable with a DB9 connector on the computerside
+and a small klinkenstecker at the camera end. So just 2 lines (RX, TX)
+and GND.
+<p>There is no documenation on the serial protocol in the inlaying documentation.
+<p>The FotoBee.exe program uses the Windows serial communications interface,
+which is fortunately implemented in WINE. By snooping the setup calls,
+the serial parameters are:
+<pre>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 115200 Baud, no parity, 1 stopbit, 8 bit data, no flow control
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (neither crtscts, nor XON/XOFF)</pre>
+It also setting some large timeouts, since the camera sometimes needs like
+2 seconds to think about something.
+<p>That was rather trivial.
+<h2>
+Chapter 2: The serial data transfer protocol</h2>
+The next step was to find out how the FotoBee software talks to the camera.
+<p>&nbsp;Luckily the program still uses the standard Win32 serial communication
+functions for that, so I added some code to the <tt>ReadFile</tt> and
+<tt>WriteFile</tt>
+functions, which dumps the read/written buffer if it is writing to a serial
+device.
+<p>In general, the program sends a command down the serial line (with optional
+arguments) and receives data back. Every command starts with a <tt>0xFF</tt>
+character.
+
+<table BORDER COLS=4 WIDTH="100%" >
+<tr><td width=10%>Command</td><td width=15%>Arguments</td><td>Returns</td><td width=55%>Description</td></tr>
+
+<tr>
+ <td>0xff 0x08</td>
+ <td>None</td>
+ <td>0xff 0xf1</td>
+ <td>Ping!s the camera.</td>
+</tr>
+
+<tr>
+ <td>0xff 0xa7</td>
+ <td>None</td>
+ <td>A packet with between 10 and 20 bytes is returned.</td>
+ <td>
+ 3 float factors are returned, they are calculated:<br>
+ <code>
+ f1=r[1]+r[2]*0.1+r[3]*0.01;<br>
+ f2=r[4]+r[5]*0.1+r[6]*0.01;<br>
+ f3=r[7]+r[8]*0.1+r[9]*0.01;<br>
+ </code><br>
+ Their use is still unknown to me.
+ </td>
+</tr>
+
+<tr>
+ <td>0xff 0xa4</td>
+ <td>None</td>
+ <td>0xff 0x01</td>
+ <td>Select Index Picture to transfer, is followed by packetreading</td>
+</tr>
+
+<tr>
+ <td>0xff 0xa1</td>
+ <td>0xff <i>picturenr</i></td>
+ <td>0xff 0x01</td>
+ <td>Selects Image <i>picturenr</i> to transfer. 3 Bytestreams follow (packetreading has to be called 3 times)</td>
+</tr>
+
+<tr>
+ <td>0xff 0xa8</td>
+ <td>10 bytes of data ... Set floats</td>
+ <td>0xff 0x01</td>
+ <td>Sets the 3 floats. Sends 10 bytes to the camera.<br>
+ The bytestream looks:
+ </code>0xFF A B C D E F G H I</code><br>
+ The floats are: A.BC D.EF G.HI<br>
+ A...I are binary values between 0 and 9.
+ </td>
+</tr>
+<tr>
+ <td>0xff 0xa6</td>
+ <td>None</td>
+ <td>No return expected.</td>
+ <td>Delete all images.</td>
+</tr>
+
+<tr>
+ <td>0xff 0xf0</td>
+ <td>None</td>
+ <td>Returns an ASCII hexnumber (starting with two 0x66 ('f'))</td>
+ <td>
+ Returns the size (in bytes) of the image to transfer next. This
+ command is issued after "select index" or "select image"
+ and is followed by (multiple) packet reads.
+ </td>
+</tr>
+
+<tr>
+ <td>0xff 0xf1</td>
+ <td>None</td>
+ <td>Returns 201 or less bytes</td>
+ <td>
+ The packetreader. It is called in a loop after querying the
+ imagesize. If 201 bytes are read, the 201th byte is the checksum
+ (sum of 0-199)&0xff, otherwise there is no checksum.<br>
+ Returns the 200 bytes read.
+ </td>
+</tr>
+<tr>
+ <td>0xff 0xf3</td>
+ <td>None</td>
+ <td>Returns 201 or less bytes</td>
+ <td>
+ The packet resend command. Works exactly like the
+ <tt>0xff 0xf1</tt> packetread command, but the last packet is
+ retransmitted. This is useful for corrupted transmissions.
+ </td>
+</tr>
+<tr>
+ <td>0xff 0x72</td>
+ <td>None</td>
+ <td>0xff 0x71</td>
+ <td>Function unknown.</td>
+</tr>
+<tr>
+ <td>0xff 0x73</td>
+ <td>None</td>
+ <td>0xff 0x70</td>
+ <td>Function unknown</td>
+</tr>
+<tr>
+ <td>0xff 0x75</td>
+ <td>None</td>
+ <td>0xff70 or 0xff71 or something else.</td>
+ <td>Function unknown. Returns bool?</td>
+</tr>
+<tr>
+ <td>0xff 0x78</td>
+ <td>None.</td>
+ <td>Some data.</td>
+ <td>Appears to take a snapshot and then it should read N packets of
+ data. Strange. There is some text generated and printed into
+ a dialog. I only get back 0xff.</td>
+</tr>
+<tr>
+ <td>0xff 0x79</td>
+ <td>None</td>
+ <td>0xff 0xXX</td>
+ <td>Function unknown, seems to open/close the shutter in rapid succession.</td>
+</tr>
+<tr>
+ <td>0xff 0x7a</td>
+ <td>None.</td>
+ <td>Some data.</td>
+ <td>Unclear. GUI enables wait cursor before call and disabled it after.</td>
+</tr>
+<tr>
+ <td>0xff 0x7b</td>
+ <td>0xff value.</td>
+ <td>0xff 0xf1</td>
+ <td>Unclear.</td>
+</tr>
+<tr>
+ <td>0xff 0xa9</td>
+ <td>0xff value.</td>
+ <td>0xff 0xf1</td>
+ <td>Set Bulb Mode. Apparently values between 1 and 9.</td>
+</tr>
+</table>
+<p>
+Following commands can be used on toplevel:
+<ul>
+<li>PING
+<li>FLOAT FACTORS
+<li>SELECT INDEX
+<li>SELECT IMAGE
+<li>...
+</ul>
+For reading the INDEX picture following sequence appears:
+<ul>
+<li>SELECT INDEX
+<li>IMAGE SIZE
+<li>READ PACKET until packets exhausted.
+</ul>
+For reading the IMAGE picture following sequence appears:
+<ul>
+<li>SELECT IMAGE <i>n</i>
+<li>Do 3 times:
+<ul>
+ <li>IMAGE SIZE
+ <li>READ PACKET until packets exhausted.
+</ul>
+</ul>
+Thats all we needed to know about the serial commands.
+
+<h2>Chapter 3: The image compression, stage 1</h2>
+
+Judging from other cameras it is easy to suspect the camera uses the JPEG
+format. It does not unfortunately.
+
+<h3>Chapter 3.1: The format of the index picture</h3>
+Casting a closer look at the raw data of the index picture shows a a stream
+of 8 bit values. Some experimentation later it turns out to a stream of
+64x48 grayscale pictures, upside down.
+Using:<pre>
+ rawtopgm &lt;index 64 (sizeof_index/64) |pnmflip -tb &gt; index.ppm
+</pre>
+we can convert it into a .PPM and convert it further using standard UNIX tools.
+<p>
+
+The size of the index picture also tells us the number of pictures that are
+currently stored in the camera. Just divide the size of the indeximage by 64*48.
+
+<h3>Chapter 3.2: The low quality compressed image format</h3>
+ The camera has two modes to store pictures, either in low or
+ high quality. This is marked by the letters "L" and "H" on the
+ cameras LCD. Default is the low quality format.<p>
+ It is still unclear to me how to detect high/low quality formats,
+ except with the size of the returned image.
+
+<p>
+At first look the data returned from the camera looks like junknoise. So I had
+to peek into the diassembly, which shows a slightly inefficient huffman decompressor, with following bitpatterns:
+<table border>
+<tr><td>Bits</td><td>Value</td></tr>
+<tr><td>00</td> <td>-2</td></tr>
+<tr><td>01</td> <td>6</td></tr>
+<tr><td>10</td> <td>2</td></tr>
+<tr><td>110</td> <td>6</td></tr>
+<tr><td>1110</td> <td>11</td></tr>
+<tr><td>1111 0</td> <td>-11</td></tr>
+<tr><td>1111 10</td> <td>20</td></tr>
+<tr><td>1111 110</td> <td>-20</td></tr>
+<tr><td>1111 1110</td> <td>45</td></tr>
+<tr><td>1111 1111 0</td> <td>-45</td></tr>
+<tr><td>1111 1111 10</td> <td>90</td></tr>
+<tr><td>1111 1111 110</td> <td>-90</td></tr>
+<tr><td>1111 1111 1110</td> <td>180</td></tr>
+<tr><td>1111 1111 1111 0</td> <td>-180</td></tr>
+</table>
+The result of this decompression run is 320*480 bytes for the first image,
+and 320*240 for the second and third image. But these are not the pixel values,
+these are differences!
+<br>
+So I had another look. Pixels are 8bit unsigned values and computed like this:
+<ul>
+<li>In the first row of the image: <code>pixel[i] = lastpixel+decompressed_diff;</code><br>
+ The first pixel is based on 0.
+<li>In all other rows (starting lastvalue is pixel[0] of the previous line):
+<ul>
+ <li><code>newpixelvalue = lastvalue + decompressed_diff;</code><br>
+ <li><code>lastvalue = newpixelvalue*0.5 + pixel_lastline[i+1]*0.5;</code>
+ <pre>
+ 0 X 0
+ X N 0
+ </pre>
+ The 'X' pixels influence the 'N' pixel (together with the uncompressed diff)
+</ul>
+</ul>
+Tada. We now have 3 uncompressed images. To view each of them we can use:
+<pre>
+ rawtopgm 320 480 &lt;image0 | pnmflip -tb &gt; image0.ppm
+ rawtopgm 320 240 &lt;image1 | pnmflip -tb &gt; image1.ppm
+ rawtopgm 320 240 &lt;image2 | pnmflip -tb &gt; image2.ppm
+</pre>
+where image0 is the first image read, and image1,image2 the next two images.<p>
+Both give pretty grayscale pictures, but are not yet in color.
+
+<h3>Chapter 3.2: The high quality compressed image format</h3>
+The high quality format uses a very different compression.<p>
+It just compacts all 8bit values to 6bit by shortening out the 2 least
+significant bits.
+So we just restore them (AAAA AABB BBBB CCCC CCDD DDDD -> AAAAAA00, BBBBBB00,
+CCCCCC00, DDDDDD00) and get the same grayscale images as with the lowquality
+compression.
+
+<h3>Chapter 3.3: Getting colors</h3>
+I am still working on that. They are not RGB.
+<hr>
+<h2>Download</h2>
+The package is available as .tar.gz file: <a href="http://www.franken.de/users/jet/jd11-1.0.tar.gz">jd11-1.0.tar.gz</a>.
+</body>
+</html>
diff --git a/camlibs/jd11/serial.c b/camlibs/jd11/serial.c
new file mode 100644
index 000000000..7a5ee6a88
--- /dev/null
+++ b/camlibs/jd11/serial.c
@@ -0,0 +1,381 @@
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <gphoto2.h>
+#include <gphoto2-port.h>
+
+#include "serial.h"
+#include "decomp.h"
+
+#if 0
+static int
+dread(GPPort *port, caddr_t buf, int xsize) {
+ int i;
+ int ret = gp_port_read(port,buf,xsize);
+
+ if (ret == -1) {
+ perror("dread");
+ return -1;
+ }
+ fprintf(stderr,"dread[%d]:",ret);
+ for (i=0;i<ret;i++) fprintf(stderr,"%02x,",buf[i]);
+ fprintf(stderr,"\n");
+ return ret;
+}
+static int
+dwrite(GPPort*port, caddr_t buf, int xsize) {
+ int i;
+ int ret = gp_port_write(port,buf,xsize);
+
+ if (ret == -1) {
+ perror("dwrite");
+ return -1;
+ }
+ fprintf(stderr,"dwrite[%d/%d]:",xsize,ret);
+ for (i=0;i<xsize;i++) fprintf(stderr,"%02x,",buf[i]);
+ fprintf(stderr,"\n");
+ return ret;
+}
+#endif
+
+#define READ gp_port_read
+#define WRITE gp_port_write
+
+static int _send_cmd(GPPort *port,unsigned short cmd) {
+ int res;
+ unsigned char buf[2];
+ buf[0] = cmd>>8;
+ buf[1] = cmd&0xff;
+ res = WRITE(port,buf,2);
+ if (res!=2)
+ return GP_ERROR_IO;
+ return GP_OK;
+}
+
+static void _read_cmd(GPPort *port,unsigned short *xcmd) {
+ unsigned char buf[2];
+ int i = 0;
+ *xcmd = 0x4242;
+ do {
+ if (2==READ(port,buf,2)) {
+ if (buf[0]==0xff)
+ break;
+ continue;
+ }
+ /*usleep(100);*/ i=10;
+ } while (i++<10);
+ *xcmd = (buf[0]<<8)|buf[1];
+}
+
+#if 0
+static void _dump_buf(unsigned char *buf,int size) {
+ int i;
+ fprintf(stderr,"[");
+ for (i=0;i<size;i++)
+ fprintf(stderr,"%02x ",buf[i]);
+ fprintf(stderr,"]\n");
+}
+#endif
+
+int jd11_ping(GPPort *port) {
+ unsigned short xcmd;
+ char buf[1];
+
+ while (1==READ(port,buf,1))
+ /* drain input queue before PING */;
+ _send_cmd(port,0xff08);
+ _read_cmd(port,&xcmd);
+ if (xcmd==0xfff1)
+ return GP_OK;
+ return GP_ERROR_IO;
+}
+
+int
+jd11_float_query(GPPort *port) {
+ char buf[20];
+ int ret,curread=0;
+ float f1,f2,f3;
+
+ _send_cmd(port,0xffa7);
+ while (curread<10) {
+ ret=READ(port,buf+curread,20-curread);
+ switch (ret) {
+ case -1: assert(0);
+ default:
+ curread+=ret;
+ break;
+ }
+ }
+ f1 = buf[1]+buf[2]*0.1+buf[3]*0.01;
+ f2 = buf[4]+buf[5]*0.1+buf[6]*0.01;
+ f3 = buf[7]+buf[8]*0.1+buf[9]*0.01;
+ if(curread<10) {
+ fprintf(stderr,"%d returned bytes on float query.\n",ret);
+ return GP_ERROR_IO;
+ }
+ /*fprintf(stderr,"FLOAT_QUERY: %f %f %f.\n",f1,f2,f3);*/
+ return GP_OK;
+}
+
+int
+jd11_select_index(GPPort *port) { /* select index */
+ unsigned short xcmd;
+
+ _send_cmd(port,0xffa4);
+ _read_cmd(port,&xcmd);
+ if (xcmd!=0xff01)
+ return GP_ERROR_IO;
+ return GP_OK;
+}
+
+int
+jd11_select_image(GPPort *port,int nr) { /* select image <nr> */
+ unsigned short xcmd;
+
+ _send_cmd(port,0xffa1);_send_cmd(port,0xff00|nr);
+ _read_cmd(port,&xcmd);
+ if (xcmd != 0xff01)
+ return GP_ERROR_IO;
+ return GP_OK;
+}
+
+#if 0
+static void cmd_75(GPPort *port) {
+ unsigned short xcmd;
+
+ _send_cmd(port,0xff75);
+ _read_cmd(port,&xcmd);
+ fprintf(stderr,"75: done, xcmd=%x\n",xcmd);
+}
+static void cmd_72(GPPort *port) {
+ unsigned short xcmd;
+
+ _send_cmd(port,0xff72);
+ _read_cmd(port,&xcmd);
+ assert(xcmd==0xff01); /* this seems to be the OK value or Go Ahead */
+ fprintf(stderr,"72: done.\n");
+}
+static void cmd_73(GPPort *port) {
+ unsigned short xcmd;
+
+ _send_cmd(port,0xff73);
+ _read_cmd(port,&xcmd);
+ fprintf(stderr,"73: xcmd = %x.\n",xcmd);
+}
+
+/* some kind of selftest ... shuts the shutters, beeps... only stops by
+ * powercycle. */
+static void cmd_79(GPPort *port) {
+ unsigned short xcmd;
+
+ _send_cmd(port,0xff79);
+ _read_cmd(port,&xcmd);
+ fprintf(stderr,"79: done, xcmd =%x\n",xcmd);
+}
+#endif
+
+
+static int
+jd11_imgsize(GPPort *port) {
+ char buf[20];
+ int ret;
+ int i=0,curread=0;
+
+ _send_cmd(port,0xfff0);
+ do {
+ ret=READ(port,&buf[curread],10-curread);
+ if (ret>0)
+ curread+=ret;
+ usleep(1000);
+ } while ((i++<20) && (curread<10));
+ /*_dump_buf(buf,ret);*/
+ ret=strtol(&buf[2],NULL,16);
+ /*fprintf(stderr,"IMGSIZE: %d (%d images)\n",ret,ret/64/48);*/
+ return ret;
+}
+
+static int
+getpacket(GPPort *port,unsigned char *buf, int expect) {
+ int curread = 0, csum = 0;
+ int tries = 0;
+ if (expect == 200)
+ expect++;
+ while (tries++<5) {
+ int i=0,ret;
+
+ do {
+ ret=READ(port,buf+curread,expect-curread);
+ if (ret>0) {
+ curread+=ret;
+ i=0;
+ continue;
+ }
+ usleep(100);
+ } while ((i++<2) && (curread<expect));
+ if (!curread)
+ return 0;
+ /*printf("curread is %d\n",curread);*/
+ /*printf("PACKET:");_dump_buf(buf,curread);*/
+ for (i=0;i<curread-1;i++)
+ csum+=buf[i];
+ if (buf[curread-1]==(csum&0xff) || (curread!=201))
+ return curread-1;
+ fprintf(stderr,"BAD CHECKSUM %x vs %x, trying resend...\n",buf[curread-1],csum&0xff);
+ _send_cmd(port,0xfff3);
+ curread = csum = 0;
+ /*return curread-1;*/
+ }
+ fprintf(stderr,"Giving up after 5 tries.\n");
+ exit(1);
+ /* not reached */
+}
+
+int
+jd11_erase_all(GPPort *port) {
+ _send_cmd(port,0xffa6);
+ return GP_OK;
+}
+
+int
+jd11_get_image_preview(GPPort *port,int nr, char **data, int *size) {
+ int xsize,packets=0,curread=0,ret=0;
+ char *indexbuf,*src,*dst;
+ int y;
+ char header[200];
+
+ if (nr < 0) return GP_ERROR_BAD_PARAMETERS;
+ jd11_select_index(port);
+ xsize = jd11_imgsize(port);
+ if (nr > xsize/(64*48)) {
+ fprintf(stderr,"ERROR: nr %d is larger than %d\n",nr,xsize/64/48);
+ return GP_ERROR_BAD_PARAMETERS;
+ }
+ xsize = (xsize / (64*48)) * (64*48);
+ indexbuf = malloc(xsize+400);
+ if (!indexbuf) return GP_ERROR_NO_MEMORY;
+ _send_cmd(port,0xfff1);
+ while (1) {
+ int readsize = xsize-curread;
+ if (readsize>200) readsize = 200;
+ ret=getpacket(port,indexbuf+curread,readsize);
+ if (ret==0)
+ break;
+ curread+=ret;
+ packets++;
+ if (ret<200)
+ break;
+ _send_cmd(port,0xfff1);
+ }
+ strcpy(header,"P5\n# gPhoto2 JD11 thumbnail image\n64 48 255\n");
+ *size = 64*48+strlen(header);
+ *data = malloc(*size);
+ if (!*data) return GP_ERROR_NO_MEMORY;
+ strcpy(*data,header);
+ src = indexbuf+(nr*64*48);
+ dst = (*data)+strlen(header);
+
+ for (y=0;y<48;y++) {
+ int x,off = 64*y;
+ for (x=0;x<64;x++)
+ dst[47*64-off+(63-x)] = src[off+x];
+ }
+ free(indexbuf);
+ return GP_OK;
+}
+
+int
+jd11_file_count(GPPort *port,int *count) {
+ int xsize,packets=0,curread=0,ret=0;
+ char tmpbuf[202];
+
+ jd11_select_index(port);
+ xsize = jd11_imgsize(port); xsize = (xsize / (64*48)) * (64*48);
+ _send_cmd(port,0xfff1);
+ while (curread <= xsize) {
+ int readsize = xsize-curread;
+ if (readsize>200) readsize = 200;
+ ret=getpacket(port,tmpbuf,readsize);
+ if (ret==0)
+ break;
+ curread+=ret;
+ packets++;
+ if (ret<200)
+ break;
+ _send_cmd(port,0xfff1);
+ }
+ *count = curread/64/48+1;
+ return GP_OK;
+}
+
+static int
+serial_image_reader(GPPort *port,int nr,unsigned char ***imagebufs,int *sizes) {
+ int picnum,packets,curread,ret=0;
+
+ jd11_select_image(port,nr);
+ *imagebufs = (unsigned char**)malloc(3*sizeof(char**));
+ for (picnum=0;picnum<3;picnum++) {
+ packets=0;
+ curread=0;
+ sizes[picnum] = jd11_imgsize(port);
+ (*imagebufs)[picnum]=(unsigned char*)malloc(sizes[picnum]+400);
+ _send_cmd(port,0xfff1);
+ while (curread<sizes[picnum]) {
+ int readsize = sizes[picnum]-curread;
+ if (readsize > 200) readsize = 200;
+ ret=getpacket(port,(*imagebufs)[picnum]+curread,readsize);
+ if (ret==0)
+ break;
+ curread+=ret;
+ packets++;
+ if (ret<200)
+ break;
+ _send_cmd(port,0xfff1);
+ }
+ }
+ return GP_OK;
+}
+
+
+int
+jd11_get_image_full(GPPort *port,int nr, char **data, int *size) {
+ unsigned char *s,*uncomp[3],**imagebufs;
+ int ret,sizes[3];
+ char header[200];
+ int h;
+
+ ret = serial_image_reader(port,nr,&imagebufs,sizes);
+ if (ret!=GP_OK)
+ return ret;
+ uncomp[0] = malloc(320*480);
+ uncomp[1] = malloc(320*480/2);
+ uncomp[2] = malloc(320*480/2);
+ if (sizes[0]!=115200) {
+ picture_decomp_v1(imagebufs[0],uncomp[0],320,480);
+ picture_decomp_v1(imagebufs[1],uncomp[1],320,480/2);
+ picture_decomp_v1(imagebufs[2],uncomp[2],320,480/2);
+ } else {
+ picture_decomp_v2(imagebufs[0],uncomp[0],320,480);
+ picture_decomp_v2(imagebufs[1],uncomp[1],320,480/2);
+ picture_decomp_v2(imagebufs[2],uncomp[2],320,480/2);
+ }
+
+ strcpy(header,"P6\n# gPhoto2 JD11 thumbnail image\n640 480 255\n");
+ *size = 640*480*3+strlen(header);
+ *data = malloc(*size);
+ strcpy(*data,header);
+ s=(*data)+strlen(header);
+ for (h=480;h--;) { /* upside down */
+ int w;
+ for (w=640;w--;) { /* right to left */
+ /* and images are in green red blue */
+ *s++=uncomp[1][(h/2)*320+(w/2)];
+ *s++=uncomp[0][h*320+(w/2)];
+ *s++=uncomp[2][(h/2)*320+(w/2)];
+ }
+ }
+ free(uncomp[0]);free(uncomp[1]);free(uncomp[2]);
+ free(imagebufs[0]);free(imagebufs[1]);free(imagebufs[2]);free(imagebufs);
+ return GP_OK;
+}
diff --git a/camlibs/jd11/serial.h b/camlibs/jd11/serial.h
new file mode 100644
index 000000000..ce5dab6d0
--- /dev/null
+++ b/camlibs/jd11/serial.h
@@ -0,0 +1,15 @@
+#ifndef _JD11_SERIAL_H
+#define _JD11_SERIAL_H
+
+#include <gphoto2.h>
+#include <gphoto2-port.h>
+
+extern int jd11_file_count(GPPort *port, int *count);
+extern int jd11_get_image_preview(GPPort *port,int nr, char **data, int *size);
+extern int jd11_get_image_full(GPPort *port,int nr, char **data, int *size);
+extern int jd11_erase_all(GPPort *port);
+extern int jd11_ping(GPPort *port);
+extern int jd11_float_query(GPPort *port);
+extern int jd11_select_index(GPPort *port);
+extern int jd11_select_image(GPPort *port, int nr);
+#endif