1 
2 #ifndef SS_IMP_H
3 
4 /*
5  * Argyll Color Correction System
6  *
7  * Gretag Spectrolino and Spectroscan related
8  * defines and declarations - implementation.
9  *
10  * Author: Graeme W. Gill
11  * Date:   14/7/2005
12  *
13  * Copyright 2005 - 2013 Graeme W. Gill
14  * All rights reserved.
15  *
16  * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
17  * see the License2.txt file for licencing details.
18  *
19  * This is an alternative driver to spm/gretag.
20  */
21 
22 /*
23    If you make use of the instrument driver code here, please note
24    that it is the author(s) of the code who take responsibility
25    for its operation. Any problems or queries regarding driving
26    instruments with the Argyll drivers, should be directed to
27    the Argyll's author(s), and not to any other party.
28 
29    If there is some instrument feature or function that you
30    would like supported here, it is recommended that you
31    contact Argyll's author(s) first, rather than attempt to
32    modify the software yourself, if you don't have firm knowledge
33    of the instrument communicate protocols. There is a chance
34    that an instrument could be damaged by an incautious command
35    sequence, and the instrument companies generally cannot and
36    will not support developers that they have not qualified
37    and agreed to support.
38  */
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <ctype.h>
43 #include <string.h>
44 #include <time.h>
45 #ifndef SALONEINSTLIB
46 #include "copyright.h"
47 #include "aconfig.h"
48 #include "numlib.h"
49 #else /* SALONEINSTLIB */
50 #include "sa_config.h"
51 #include "numsup.h"
52 #endif /* SALONEINSTLIB */
53 #include "xspect.h"
54 #include "insttypes.h"
55 #include "conv.h"
56 #include "icoms.h"
57 #include "ss.h"
58 
59 /* ------------------------------------------- */
60 /* Serialisation for different types functions */
61 
62 /* QUERY: */
63 
64 /* These add characters to the current send buffer, */
65 /* And set the status appropriately */
66 
67 /* 4 bits to hex character conversion table */
68 static char b2h[16] = {
69 	'0', '1', '2', '3', '4', '5', '6', '7',
70 	'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
71 };
72 
73 /* Check that there is enough space to write size more characters */
74 #define CHWSPACE(size)                          \
75 	if (p->snerr == ss_et_NoError               \
76 	  && (p->sbufe - p->sbuf) < (size)) {       \
77 		p->snerr = ss_et_SendBufferFull;        \
78 	}                                           \
79 	if (p->snerr != ss_et_NoError)              \
80 		return;
81 
82 /* Reset send buffer, and init with start character */
ss_init_send(ss * p)83 void ss_init_send(ss *p) {
84 	p->snerr = ss_et_NoError;
85 	p->sbuf = p->_sbuf;
86 	CHWSPACE(1);
87 	p->sbuf[0] = ';';
88 	p->sbuf += 1;
89 }
90 
91 /* Reset send buffer, and add an Spectrolino Request enum */
ss_add_soreq(ss * p,int rq)92 void ss_add_soreq(ss *p, int rq) {
93 	ss_init_send(p);
94 	CHWSPACE(2);
95 	p->sbuf[0] = b2h[(rq >> 4) & 0xf];
96 	p->sbuf[1] = b2h[(rq >> 0) & 0xf];
97 	p->sbuf += 2;
98 }
99 
100 /* Reset send buffer, and add an SpectroScan Request enum */
ss_add_ssreq(ss * p,int rq)101 void ss_add_ssreq(ss *p, int rq) {
102 	ss_init_send(p);
103 	CHWSPACE(4);
104 	p->sbuf[0] = b2h[((int)ss_ReqPFX >> 4) & 0xf];	/* Prefix */
105 	p->sbuf[1] = b2h[((int)ss_ReqPFX >> 0) & 0xf];	/* Prefix */
106 	p->sbuf[2] = b2h[(rq >> 4) & 0xf];
107 	p->sbuf[3] = b2h[(rq >> 0) & 0xf];
108 	p->sbuf += 4;
109 }
110 
111 /* Add an int/enum/char into one byte type */
ss_add_1(ss * p,int c)112 void ss_add_1(ss *p, int c) {
113 	CHWSPACE(2);
114 	p->sbuf[0] = b2h[(c >> 4) & 0xf];
115 	p->sbuf[1] = b2h[(c >> 0) & 0xf];
116 	p->sbuf += 2;
117 }
118 
119 /* Add an int/enum into two byte type */
ss_add_2(ss * p,int s)120 void ss_add_2(ss *p, int s) {
121 	CHWSPACE(4);
122 	p->sbuf[0] = b2h[(s >> 4) & 0xf];	/* LSB */
123 	p->sbuf[1] = b2h[(s >> 0) & 0xf];
124 	p->sbuf[2] = b2h[(s >> 12) & 0xf];	/* MSB */
125 	p->sbuf[3] = b2h[(s >> 8) & 0xf];
126 	p->sbuf += 4;
127 }
128 
129 /* Add an int/enum into four byte type */
ss_add_4(ss * p,int i)130 void ss_add_4(ss *p, int i) {
131 	CHWSPACE(8);
132 	p->sbuf[0] = b2h[(i >> 4) & 0xf];	/* LSB */
133 	p->sbuf[1] = b2h[(i >> 0) & 0xf];
134 	p->sbuf[2] = b2h[(i >> 12) & 0xf];
135 	p->sbuf[3] = b2h[(i >> 8) & 0xf];
136 	p->sbuf[4] = b2h[(i >> 20) & 0xf];
137 	p->sbuf[5] = b2h[(i >> 16) & 0xf];
138 	p->sbuf[6] = b2h[(i >> 28) & 0xf];	/* MSB */
139 	p->sbuf[7] = b2h[(i >> 24) & 0xf];
140 	p->sbuf += 8;
141 }
142 
143 /* Add a double into four byte type */
ss_add_double(ss * p,double d)144 void ss_add_double(ss *p, double d) {
145 	unsigned int id;
146 
147 	id = doubletoIEEE754(d);
148 
149 	ss_add_4(p, id);
150 }
151 
152 /* Add an  ASCII string into the send buffer. */
153 /* The string will be padded with nul's up to len. */
ss_add_string(ss * p,char * t,int len)154 void ss_add_string(ss *p, char *t, int len) {
155 	int i;
156 
157 	CHWSPACE(2 * len);
158 	for (i = 0; i < len; i++) {
159 		p->sbuf[2 * i + 0] = b2h[(t[i] >> 4) & 0xf];
160 		p->sbuf[2 * i + 1] = b2h[(t[i] >> 0) & 0xf];
161 		if (t[i] == '\000')
162 			break;
163 	}
164 	for (; i < len; i++) {
165 		p->sbuf[2 * i + 0] = '0';
166 		p->sbuf[2 * i + 1] = '0';
167 	}
168 	p->sbuf += 2 * len;
169 }
170 
171 /* - - - - - - - - - - - - - - - - - - - - - */
172 /* ANSWER: */
173 
174 /* Check that there is not already an error, */
175 /* and that there is enough space to read size more characters. */
176 /* Return nz if not. */
chrspace(ss * p,int size)177 static int chrspace(ss *p, int size) {
178 
179 	if (p->snerr != ss_et_NoError)		/* Problem assembling request */
180 		return 1;
181 
182 	if ((p->rbufe - p->rbuf) < (size)) {
183 		p->snerr = ss_et_RecBufferEmpty;
184 		return 1;
185 	}
186 	{
187 		char *rr = p->rbuf, *re = p->rbuf + size;
188 		while (rr < re && *rr != '\000')
189 			rr++;
190 		if (rr < re) {
191 			p->snerr = ss_et_RecBufferEmpty;
192 			return 1;
193 		}
194 	}
195 	return 0;
196 }
197 
198 /* Check that the read buffer is fully consumed. */
chended(ss * p)199 static void chended(ss *p) {
200 	if (p->snerr == ss_et_NoError	/* Don't overrite any existing error */
201 	 && p->rbufe != p->rbuf) {
202 		p->snerr = ss_et_BadAnsFormat;
203 	}
204 }
205 
206 /* Convert an ASCII Hex character to an integer. */
h2b(ss * p,char c)207 static int h2b(ss *p, char c) {
208 	if (c >= '0' && c <= '9')
209 		return (c-(int)'0');
210 	if (c >= 'A' && c <= 'F')
211 		return (10 + c-(int)'A');
212 	if (c >= 'a' && c <= 'f')
213 		return (10 + c-(int)'a');
214 	if (p->snerr == ss_et_NoError)   /* Don't overrite any existing error */
215 		p->snerr = ss_et_BadHexEncoding;
216 	return 0;
217 }
218 
219 /* Return the first enum from the recieve buffer without removing it. */
ss_peek_ans(ss * p)220 int ss_peek_ans(ss *p) {
221 	int rv;
222 
223 	if (chrspace(p, 2))
224 		return 0;
225 	rv = (h2b(p, p->rbuf[0]) << 4)
226 	   | (h2b(p, p->rbuf[1]) << 0);
227 
228 	return rv;
229 }
230 
231 /* Remove a Spectrolino answer enum from the revieve buffer, */
232 /* and check it is correct.  */
ss_sub_soans(ss * p,int cv)233 void ss_sub_soans(ss *p, int cv) {
234 	int rv;
235 
236 	if (chrspace(p, 2))
237 		return;
238 	rv = (h2b(p, p->rbuf[0]) << 4)
239 	   | (h2b(p, p->rbuf[1]) << 0);
240 	p->rbuf += 2;
241 	if (rv != cv) {
242 		if (p->snerr == ss_et_NoError)   /* Don't overrite any existing error */
243 			p->snerr = ss_et_BadAnsFormat;
244 		return;
245 	}
246 	return;
247 }
248 
249 /* Remove a SpectroScan Prefix and answer enum from the revieve buffer, */
250 /* and check it is correct.  */
ss_sub_ssans(ss * p,int cv)251 void ss_sub_ssans(ss *p, int cv) {
252 	int rv;
253 
254 	if (chrspace(p, 4))
255 		return;
256 	if (p->rbuf[0] != 'D'
257 	 || p->rbuf[1] != '1') {
258 		if (p->snerr == ss_et_NoError)   /* Don't overrite any existing error */
259 			p->snerr = ss_et_BadAnsFormat;
260 		return;
261 	}
262 	rv = (h2b(p, p->rbuf[2]) << 4)
263 	   | (h2b(p, p->rbuf[3]) << 0);
264 	p->rbuf += 4;
265 	if (rv != cv) {
266 		if (p->snerr == ss_et_NoError)   /* Don't overrite any existing error */
267 			p->snerr = ss_et_BadAnsFormat;
268 		return;
269 	}
270 	return;
271 }
272 
273 /* Remove an int/enum/char into one byte type */
ss_sub_1(ss * p)274 int ss_sub_1(ss *p) {
275 	int rv;
276 
277 	if (chrspace(p, 2))
278 		return 0;
279 	rv = (h2b(p, p->rbuf[0]) << 4)
280 	   | (h2b(p, p->rbuf[1]) << 0);
281 	p->rbuf += 2;
282 
283 	return rv;
284 }
285 
286 /* Remove an int/enum into two byte type */
ss_sub_2(ss * p)287 int ss_sub_2(ss *p) {
288 	int rv;
289 
290 	if (chrspace(p, 4))
291 		return 0;
292 	rv = (h2b(p, p->rbuf[0]) << 4)
293 	   | (h2b(p, p->rbuf[1]) << 0)
294 	   | (h2b(p, p->rbuf[2]) << 12)
295 	   | (h2b(p, p->rbuf[3]) << 8);
296 	p->rbuf += 4;
297 
298 	return rv;
299 }
300 
301 /* Remove an int/enum into four byte type */
ss_sub_4(ss * p)302 int ss_sub_4(ss *p) {
303 	int rv;
304 
305 	if (chrspace(p, 8))
306 		return 0;
307 	rv = (h2b(p, p->rbuf[0]) << 4)
308 	   | (h2b(p, p->rbuf[1]) << 0)
309 	   | (h2b(p, p->rbuf[2]) << 12)
310 	   | (h2b(p, p->rbuf[3]) << 8)
311 	   | (h2b(p, p->rbuf[4]) << 20)
312 	   | (h2b(p, p->rbuf[5]) << 16)
313 	   | (h2b(p, p->rbuf[6]) << 28)
314 	   | (h2b(p, p->rbuf[7]) << 24);
315 	p->rbuf += 8;
316 
317 	return rv;
318 }
319 
320 /* Remove a double into four byte type */
ss_sub_double(ss * p)321 double ss_sub_double(ss *p) {
322 	unsigned int ip;
323 	double op;
324 
325 	ip = (unsigned int)ss_sub_4(p);
326 
327 	op = IEEE754todouble(ip);
328 
329 	return op;
330 }
331 
332 /* Remove an ASCII string from the receive buffer. */
333 /* The string will be terminated with a nul, so a buffer */
334 /* of len+1 should be provided to return the string in. */
ss_sub_string(ss * p,char * t,int len)335 void ss_sub_string(ss *p, char *t, int len) {
336 	int i;
337 
338 	if (chrspace(p, 2 * len))
339 		return;
340 	for (i = 0; i < len; i++) {
341 		t[i] = (char)((h2b(p, p->rbuf[2 * i + 0]) << 4)
342 		            | (h2b(p, p->rbuf[2 * i + 1]) << 0));
343 	}
344 	t[i] = '\000';
345 	p->rbuf += 2 * len;
346 }
347 
348 /* - - - - - - - - - - - - - - - - - - - - - */
349 /* ERROR CODES: */
350 
351 /* Convert an ss error into an inst_error */
ss_inst_err(ss * p)352 inst_code ss_inst_err(ss *p) {
353 	ss_et ec = p->snerr;
354 
355 	switch(ec) {
356 		case ss_et_NoError:
357 			return inst_ok;
358 
359 		case ss_et_WhiteMeasOK:
360 		case ss_et_ResetDone:
361 		case ss_et_EmissionCalOK:
362 			return inst_notify | ec;
363 
364 		case ss_et_WhiteMeasWarn:
365 			return inst_warning | ec;
366 
367 		case ss_et_SendTimeout:
368 		case ss_et_SerialFail:
369 			return inst_coms_fail | ec;
370 
371 		case ss_et_StopButNoStart:
372 		case ss_et_IllegalCharInRec:
373 		case ss_et_IncorrectRecLen:
374 		case ss_et_IllegalRecType:
375 		case ss_et_NoTagField:
376 		case ss_et_ConvError:
377 		case ss_et_RecBufferEmpty:
378 		case ss_et_BadAnsFormat:
379 		case ss_et_BadHexEncoding:
380 		case ss_et_RecBufferOverun:
381 		case ss_et_OutOfRange:
382 		case ss_et_NotAnSROrBoolean:
383 			return inst_protocol_error | ec;
384 
385 		case ss_et_RemOverFlow:
386 		case ss_et_MeasDisabled:
387 		case ss_et_MeasurementError:
388 		case ss_et_DorlOutOfRange:
389 		case ss_et_ReflectanceOutOfRange:
390 		case ss_et_Color1OutOfRange:
391 		case ss_et_Color2OutOfRange:
392 		case ss_et_Color3OutOfRange:
393 			return inst_misread | ec;
394 
395 		case ss_et_NoValidCommand:
396 		case ss_et_NoUserAccess:
397 			return inst_unsupported | ec;
398 
399 		case ss_et_NotReady:
400 		case ss_et_NoAccess:
401 			return inst_other_error | ec;
402 			break;
403 
404 		case ss_et_SendBufferFull:
405 			return inst_internal_error | ec;
406 
407 		case ss_et_NoValidMeas:
408 		case ss_et_OnlyEmission:
409 		case ss_et_DensCalError:
410 		case ss_et_NoTransmTable:
411 		case ss_et_InvalidForEmission:
412 		case ss_et_NotInTransmMode:
413 		case ss_et_NotInReflectMode:
414 		case ss_et_NoValidDStd:
415 		case ss_et_NoValidWhite:
416 		case ss_et_NoValidIllum:
417 		case ss_et_NoValidObserver:
418 		case ss_et_NoValidMaxLambda:
419 		case ss_et_NoValidSpect:
420 		case ss_et_NoValidColSysOrIndex:
421 		case ss_et_NoValidChar:
422 		case ss_et_NoValidValOrRef:
423 		case ss_et_DeviceIsOffline:
424 		case ss_et_NoDeviceFound:
425 			return inst_wrong_setup | ec;
426 
427 		case ss_et_BackupError:
428 		case ss_et_ProgramROMError:
429 		case ss_et_CheckSumWrong:
430 		case ss_et_MemoryError:
431 		case ss_et_FullMemory:
432 		case ss_et_EPROMFailure:
433 		case ss_et_MemoryFailure:
434 		case ss_et_PowerFailure:
435 		case ss_et_LampFailure:
436 		case ss_et_HardwareFailure:
437 		case ss_et_DriveError:
438 		case ss_et_FilterOutOfPos:
439 		case ss_et_ProgrammingError:
440 			return inst_hardware_fail | ec;
441 	}
442 	return inst_other_error | ec;
443 }
444 
445 /* Incorporate a error into the snerr value */
ss_incorp_err(ss * p,ss_et se)446 void ss_incorp_err(ss *p, ss_et se) {
447 	if (p->snerr != ss_et_NoError)		/* Don't overrite any existing error */
448 		return;
449 	if (se == ss_set_NoError)
450 		return;
451 
452 	p->snerr = se;
453 }
454 
455 /* Incororate Remote Error Set values into snerr value */
456 /* Since ss_res is a bit mask, we just prioritize the errors. */
ss_incorp_remerrset(ss * p,ss_res es)457 void ss_incorp_remerrset(ss *p, ss_res es) {
458 	int i, ii;
459 	if (p->snerr != ss_et_NoError)      /* Don't overrite any existing error */
460 		return;
461 	if (es == ss_res_NoError)
462 		return;
463 
464 	for (i = ss_et_NoValidDStd, ii = 0x0001; ii < 0x10000; i++, ii <<= 1) {
465 		if ((ii & es) != 0) {
466 			break;
467 		}
468 	}
469 	p->snerr = i;
470 }
471 
472 /* Incorporate a scan error into the snerr value */
ss_incorp_scanerr(ss * p,ss_set se)473 void ss_incorp_scanerr(ss *p, ss_set se) {
474 	if (p->snerr != ss_et_NoError)		/* Don't overrite any existing error */
475 		return;
476 	if (se == ss_set_NoError)
477 		return;
478 
479 	p->snerr = se + ss_et_DeviceIsOffline - 1;
480 }
481 
482 /* Incorporate a device communication error into the snerr value */
ss_incorp_comerr(ss * p,ss_cet se)483 void ss_incorp_comerr(ss *p, ss_cet se) {
484 	if (p->snerr != ss_et_NoError)		/* Don't overrite any existing error */
485 		return;
486 	if (se == ss_cet_NoError)
487 		return;
488 
489 	p->snerr = se + ss_et_StopButNoStart - 1;
490 }
491 
492 /* - - - - - - - - - - - - - - - - - - - - - */
493 /* EXECUTION: */
494 
495 /* Interpret an icoms error into a SS error */
icoms2ss_err(int se)496 int icoms2ss_err(int se) {
497 	if (se != ICOM_OK)
498 		return ss_et_SerialFail;
499 	return ss_et_NoError;
500 }
501 
502 /* Terminate the send buffer, and then do a */
503 /* send/receieve to the device. */
504 /* Leave any error in p->snerr */
ss_command(ss * p,double tmo)505 void ss_command(ss *p, double tmo) {
506 	int se;
507 
508 	CHWSPACE(3);
509 	p->sbuf[0] = '\r';
510 	p->sbuf[1] = '\n';
511 	p->sbuf[2] = '\00';					/* write_read terminates on nul */
512 
513 	p->rbuf = p->_rbuf;				/* Reset read pointer */
514 	if ((se = p->icom->write_read_ex(p->icom, p->_sbuf, 0, p->_rbuf, SS_MAX_RD_SIZE, NULL, "\n", 1, tmo, 1)) != 0) {
515 		p->snerr = icoms2ss_err(se);
516 		return;
517 	}
518 
519 	/* Figure out receive size, and clean up termination. */
520 	p->rbufe = p->_rbuf + strlen(p->_rbuf);
521 	if ((p->rbufe - p->rbuf) >= 1 && p->rbufe[-1] == '\n') {
522 		--p->rbufe;
523 		*p->rbufe = '\000';
524 	}
525 	if ((p->rbufe - p->rbuf) >= 1 && p->rbufe[-1] == '\r') {
526 		--p->rbufe;
527 		*p->rbufe = '\000';
528 	}
529 
530 	/* Check receive format and look for a COM error */
531 	if ((p->rbufe - p->rbuf) < 1 || p->rbuf[0] != ':') {
532 		p->snerr = ss_et_BadAnsFormat;
533 		return;
534 	}
535 	p->rbuf++;
536 
537 	/* See if this is a Spectroscan COM error */
538 	if ((p->rbufe - p->rbuf) >= 2
539 	  && p->rbuf[0] == 'D'
540 	  && p->rbuf[1] == '1') {
541 
542 		if ((p->rbufe - p->rbuf) >= 4
543 		  && p->rbuf[0] == 'A'
544 		  && p->rbuf[1] == '0') {	/* COMM Error */
545 			p->rbuf += 4;
546 			ss_incorp_comerr(p, (ss_cet)ss_sub_1(p));
547 			return;
548 		}
549 	}
550 
551 	/* See if it is a Spectrolino COMM error */
552 	if ((p->rbufe - p->rbuf) >= 2
553 	  && p->rbuf[0] == '2'
554 	  && p->rbuf[1] == '6') {	/* COMM Error */
555 		p->rbuf += 2;
556 		ss_incorp_comerr(p, (ss_cet)ss_sub_1(p));
557 		return;
558 	}
559 	return;
560 }
561 
562 /* =================================================================== */
563 /* Complete Spectrolino instructions. */
564 /* [This is less elegant than Neil Okamoto's */
565 /*  table approach, but is more flexible, */
566 /*  and it does the job.] */
567 
568 /* - - - - - - - - - - - - - - - - - - - - */
569 /* Device Initialisation and configuration */
570 
571 /* Reset instrument */
so_do_ResetStatusDownload(ss * p,ss_smt sm)572 inst_code so_do_ResetStatusDownload(
573 ss *p,
574 ss_smt sm		/* Init all or all except communications */
575 ) {
576 	ss_add_soreq(p, ss_ResetStatusDownload);
577 	ss_add_1(p, 0x01);
578 	ss_add_1(p, 0x04);
579 	ss_add_1(p, sm);
580 	ss_command(p, IT_TMO);
581 	ss_sub_soans(p, ss_DownloadError);
582 	ss_incorp_remerrset(p, ss_sub_2(p));
583 	chended(p);
584 	return ss_inst_err(p);
585 }
586 
587 /* Load various parameters, such as: */
588 /* comm flow control, baud rate, speaker, */
589 /* reflective/tranmission/emmission mode, */
590 /* custom filter on/off */
so_do_MeasControlDownload(ss * p,ss_ctt ct)591 inst_code so_do_MeasControlDownload(
592 ss *p,
593 ss_ctt ct			/* Control */
594 ) {
595 	ss_add_soreq(p, ss_MeasControlDownload);
596 	ss_add_1(p, ct);
597 	ss_command(p, DF_TMO);
598 	ss_sub_soans(p, ss_DownloadError);
599 	ss_incorp_remerrset(p, ss_sub_2(p));
600 	chended(p);
601 
602 	return ss_inst_err(p);
603 }
604 
605 /* Query various current parameters, such as: */
606 /* comm flow control, baud rate, speaker, */
607 /* reflective/tranmission/emmission mode, */
608 /* custom filter on/off. */
so_do_MeasControlRequest(ss * p,ss_ctt ct,ss_ctt * rct)609 inst_code so_do_MeasControlRequest(
610 ss *p,
611 ss_ctt ct,		/* Control to query  */
612 ss_ctt *rct		/* Return current state */
613 ) {
614 	ss_add_soreq(p, ss_MeasControlRequest);
615 	ss_add_1(p, ct);
616 	ss_command(p, DF_TMO);
617 	ss_sub_soans(p, ss_MeasControlAnswer);
618 	ss_sub_1(p);			/* Should be the same as ct */
619 	*rct = ss_sub_1(p);
620 	ss_incorp_remerrset(p, ss_sub_2(p));
621 	chended(p);
622 	return ss_inst_err(p);
623 }
624 
625 /* Queries specific device data */
so_do_DeviceDataRequest(ss * p,char dn[19],ss_dnot * dno,char pn[9],unsigned int * sn,char sv[13])626 inst_code so_do_DeviceDataRequest(
627 ss *p,
628 char dn[19],		/* Return the device name */
629 ss_dnot *dno,		/* Return device number */
630 char pn[9],			/* Return the part number */
631 unsigned int *sn,	/* Return serial number */
632 char sv[13]			/* Return software version */
633 ) {
634 	char rsv[17];	/* Space for reserved field */
635 	ss_add_soreq(p, ss_DeviceDataRequest);
636 	ss_command(p, DF_TMO);
637 	ss_sub_soans(p, ss_DeviceDataAnswer);
638 	ss_sub_string(p, dn, 18);
639 	*dno = ss_sub_1(p);
640 	ss_sub_string(p, pn, 8);
641 	*sn = (unsigned int)ss_sub_4(p);
642 	ss_sub_string(p, sv, 12);
643 	ss_sub_string(p, rsv, 16);
644 	chended(p);
645 	return ss_inst_err(p);
646 }
647 
648 /* Query special device data */
so_do_TargetIdRequest(ss * p,char dn[19],int * sn,int * sr,int * yp,int * mp,int * dp,int * hp,int * np,ss_ttt * tt,int * fswl,int * nosw,int * dpsw)649 inst_code so_do_TargetIdRequest(
650 ss *p,
651 char dn[19],	/* Return Device Name */
652 int *sn,		/* Return Serial Number (1-65535) */
653 int *sr,		/* Return Software Release */
654 int *yp,		/* Return Year of production (e.g. 1996) */
655 int *mp,		/* Return Month of production (1-12) */
656 int *dp,		/* Return Day of production (1-31) */
657 int *hp,		/* Return Hour of production (0-23) */
658 int *np,		/* Return Minuite of production (0-59) */
659 ss_ttt *tt,		/* Return Target Tech Type (SPM/Spectrolino etc.) */
660 int *fswl,		/* Return First spectral wavelength (nm) */
661 int *nosw,		/* Return Number of spectral wavelengths */
662 int *dpsw		/* Return Distance between spectral wavelengths (nm) */
663 ) {
664 	ss_add_soreq(p, ss_TargetIdRequest);
665 	ss_command(p, DF_TMO);
666 	ss_sub_soans(p, ss_TargetIdAnswer);
667 	ss_sub_string(p, dn, 18);
668 	*sn = ss_sub_2(p);
669 	*sr = ss_sub_2(p);
670 	*yp = ss_sub_2(p);
671 	*mp = ss_sub_2(p);
672 	*dp = ss_sub_2(p);
673 	*hp = ss_sub_2(p);
674 	*np = ss_sub_2(p);
675 	*tt = ss_sub_1(p);
676 	*fswl = ss_sub_2(p);
677 	*nosw = ss_sub_2(p);
678 	*dpsw = ss_sub_2(p);
679 	chended(p);
680 	return ss_inst_err(p);
681 }
682 
683 /* - - - - - - - - - - - - - */
684 /* Measurement configuration */
685 
686 /* Query the standard or user definable densitometric tables */
so_do_DensTabRequest(ss * p,ss_dst ds,ss_dst * rds,double sp[4][36])687 inst_code so_do_DensTabRequest(
688 ss *p,
689 ss_dst ds,			/* Density standard (ANSI/DIN/User etc.) */
690 ss_dst *rds,		/* Return Density standard (ANSI/DIN/User etc.) */
691 double sp[4][36]	/* Return 4 * 36 spectral weighting values */
692 ) {
693 	int n,i;
694 	ss_add_soreq(p, ss_DensTabRequest);
695 	ss_add_1(p, 0x00);
696 	ss_add_1(p, ds);
697 	ss_command(p, DF_TMO);
698 	ss_sub_soans(p, ss_DensTabAnswer);
699 	ss_sub_soans(p, 0x00);
700 	*rds = ss_sub_1(p);
701 	for (n = 0; n < 4; n++)
702 		for (i = 0; i < 36; i++)
703 			sp[n][i] = ss_sub_double(p);
704 	ss_incorp_remerrset(p, ss_sub_2(p));
705 	chended(p);
706 	return ss_inst_err(p);
707 }
708 
709 /* Download user definable densitometric tables */
so_do_DensTabDownload(ss * p,double sp[4][36])710 inst_code so_do_DensTabDownload(
711 ss *p,
712 double sp[4][36]	/* 4 * 36 spectral weighting values */
713 ) {
714 	int i, n;
715 	ss_add_soreq(p, ss_DensTabDownload);
716 	ss_add_1(p, 0x08);
717 	for (n = 0; n < 4; n++)
718 		for (i = 0; i < 36; i++)
719 			ss_add_double(p, sp[n][i]);
720 	ss_command(p, DF_TMO);
721 	ss_sub_soans(p, ss_DownloadError);
722 	ss_incorp_remerrset(p, ss_sub_2(p));
723 	chended(p);
724 	return ss_inst_err(p);
725 }
726 
727 /* Set slope values for densitometry */
so_do_SlopeDownload(ss * p,double dv[4])728 inst_code so_do_SlopeDownload(
729 ss *p,
730 double dv[4]	/* Db Dc Dm Dy density values */
731 ) {
732 	int i;
733 	ss_add_soreq(p, ss_SlopeDownload);
734 	for (i = 0; i < 4; i++)
735 		ss_add_double(p, dv[i]);
736 	ss_command(p, DF_TMO);
737 	ss_sub_soans(p, ss_DownloadError);
738 	ss_incorp_remerrset(p, ss_sub_2(p));
739 	chended(p);
740 	return ss_inst_err(p);
741 }
742 
743 /* Query slope values of densitometry */
so_do_SlopeRequest(ss * p,double dv[4])744 inst_code so_do_SlopeRequest(
745 ss *p,
746 double dv[4]	/* Return Db Dc Dm Dy density values */
747 ) {
748 	int i;
749 	ss_add_soreq(p, ss_SlopeRequest);
750 	ss_command(p, DF_TMO);
751 	ss_sub_soans(p, ss_SlopeAnswer);
752 	for (i = 0; i < 4; i++)
753 		dv[i] = ss_sub_double(p);
754 	chended(p);
755 	return ss_inst_err(p);
756 }
757 
758 /* Set the colorimetric parameters */
so_do_ParameterDownload(ss * p,ss_dst ds,ss_wbt wb,ss_ilt it,ss_ot ot)759 inst_code so_do_ParameterDownload(
760 ss *p,
761 ss_dst ds,	/* Density standard (ANSI/DIN etc.) */
762 ss_wbt wb,	/* White base (Paper/Absolute) */
763 ss_ilt it,	/* Illuminant type (A/C/D65 etc.) */
764 ss_ot  ot	/* Observer type (2deg/10deg) */
765 ) {
766 	ss_add_soreq(p, ss_ParameterDownload);
767 	ss_add_1(p, ds);
768 	ss_add_1(p, wb);
769 	ss_add_1(p, it);
770 	ss_add_1(p, ot);
771 	ss_command(p, DF_TMO);
772 	ss_sub_soans(p, ss_DownloadError);
773 	ss_incorp_remerrset(p, ss_sub_2(p));
774 	chended(p);
775 	return ss_inst_err(p);
776 }
777 
778 /* Query colorimetric parameters */
so_do_ParameterRequest(ss * p,ss_dst * ds,ss_wbt * wb,ss_ilt * it,ss_ot * ot,ss_aft * af)779 inst_code so_do_ParameterRequest(
780 ss *p,
781 ss_dst *ds,		/* Return Density Standard */
782 ss_wbt *wb,		/* Return White Base */
783 ss_ilt *it,		/* Return Illuminant type (A/C/D65/User etc.) */
784 ss_ot  *ot,		/* Return Observer type (2deg/10deg) */
785 ss_aft *af		/* Return Filter being used (None/Pol/D65/UV/custom */
786 ) {
787 	ss_add_soreq(p, ss_ParameterRequest);
788 	ss_command(p, DF_TMO);
789 	ss_sub_soans(p, ss_ParameterAnswer);
790 	*ds = ss_sub_1(p);
791 	*wb = ss_sub_1(p);
792 	*it = ss_sub_1(p);
793 	*ot = ss_sub_1(p);
794 	*af = ss_sub_1(p);
795 	chended(p);
796 	return ss_inst_err(p);
797 }
798 
799 /* Query the standard or user defined illuminant tables (Colorimetry) */
so_do_IllumTabRequest(ss * p,ss_ilt it,ss_ilt * rit,double sp[36])800 inst_code so_do_IllumTabRequest(
801 ss *p,
802 ss_ilt it,		/* Illuminant type (A/C/D65/User etc.) */
803 ss_ilt *rit,	/* Return Illuminant type (A/C/D65/User etc.) */
804 double sp[36]	/* Return 36 spectral values */
805 ) {
806 	int i;
807 	ss_add_soreq(p, ss_IllumTabRequest);
808 	ss_add_1(p, 0x00);
809 	ss_add_1(p, it);
810 	ss_command(p, DF_TMO);
811 	ss_sub_soans(p, ss_IllumTabAnswer);
812 	ss_sub_soans(p, 0x00);
813 	*rit = ss_sub_1(p);
814 	for (i = 0; i < 36; i++)
815 		sp[i] = ss_sub_double(p);
816 	ss_incorp_remerrset(p, ss_sub_2(p));
817 	chended(p);
818 	return ss_inst_err(p);
819 }
820 
821 /* Download user definable illuminant tables (Colorimetry) */
so_do_IllumTabDownload(ss * p,double sp[36])822 inst_code so_do_IllumTabDownload(
823 ss *p,
824 double sp[36]	/* 36 spectral values to set */
825 ) {
826 	int i;
827 	ss_add_soreq(p, ss_IllumTabDownload);
828 	ss_add_1(p, 0x08);
829 	for (i = 0; i < 36; i++)
830 		ss_add_double(p, sp[i]);
831 	ss_command(p, DF_TMO);
832 	ss_sub_soans(p, ss_DownloadError);
833 	ss_incorp_remerrset(p, ss_sub_2(p));
834 	chended(p);
835 	return ss_inst_err(p);
836 }
837 
838 /* Query for the color temperature of daylight illuminant Dxx */
so_do_GetValNr(ss * p,int * ct)839 inst_code so_do_GetValNr(
840 ss *p,
841 int *ct		/* Return color temperature in deg K/100 */
842 ) {
843 	ss_add_soreq(p, ss_GetValNr);
844 	ss_add_1(p, 0x60);
845 	ss_command(p, DF_TMO);
846 	ss_sub_soans(p, ss_ValNrAnswer);
847 	ss_sub_soans(p, 0x60);
848 	*ct = ss_sub_1(p);
849 	ss_incorp_remerrset(p, ss_sub_2(p));
850 	chended(p);
851 	return ss_inst_err(p);
852 }
853 
854 /* Download user definable illuminant tables (Colorimetry) */
so_do_SetValNr(ss * p,int ct)855 inst_code so_do_SetValNr(
856 ss *p,
857 int ct		/* Color temperature to set for illuminant Dxx in deg K/100 */
858 ) {
859 	ss_add_soreq(p, ss_IllumTabDownload);
860 	ss_add_1(p, 0x60);
861 	ss_add_2(p, ct);
862 	ss_command(p, DF_TMO);
863 	ss_sub_soans(p, ss_DownloadError);
864 	ss_incorp_remerrset(p, ss_sub_2(p));
865 	chended(p);
866 	return ss_inst_err(p);
867 }
868 
869 /* Queries the spectra of the white tile reference for the desired filter */
so_do_WhiteReferenceRequest(ss * p,ss_aft af,ss_aft * raf,double sp[36],ss_owrt * owr,char dtn[19])870 inst_code so_do_WhiteReferenceRequest(
871 ss *p,
872 ss_aft af,		/* Filter being queried (None/Pol/D65/UV/custom */
873 ss_aft *raf,	/* Return filter being queried (None/Pol/D65/UV/custom */
874 double sp[36],	/* Return 36 spectral values */
875 ss_owrt *owr,	/* Return original white reference (i.e. factory/user) */
876 char dtn[19]	/* Return name of data table */
877 ) {
878 	int i;
879 	ss_add_soreq(p, ss_WhiteReferenceRequest);
880 	ss_add_1(p, af);
881 	ss_command(p, DF_TMO);
882 	ss_sub_soans(p, ss_WhiteReferenceAnswer);
883 	*raf = ss_sub_1(p);
884 	for (i = 0; i < 36; i++)
885 		sp[i] = ss_sub_double(p);
886 	*owr = ss_sub_1(p);
887 	ss_sub_string(p, dtn, 18);
888 	chended(p);
889 	return ss_inst_err(p);
890 }
891 
892 /* Load spectra of a user defined white reference for the desired filter. */
893 /* This lets the user override the factory white tile calibration */
894 /* A name can be given to the white reference. */
so_do_WhiteReferenceDownld(ss * p,ss_aft af,double sp[36],char dtn[19])895 inst_code so_do_WhiteReferenceDownld(
896 ss *p,
897 ss_aft af,		/* Filter being set (None/Pol/D65/UV/custom */
898 double sp[36],	/* 36 spectral values being set */
899 char dtn[19]	/* Name for data table */
900 ) {
901 	int i;
902 	ss_add_soreq(p, ss_WhiteReferenceDownld);
903 	ss_add_1(p, af);
904 	for (i = 0; i < 36; i++)
905 		ss_add_double(p, sp[i]);
906 	ss_add_string(p, dtn, 18);
907 	ss_command(p, DF_TMO);
908 	ss_sub_soans(p, ss_DownloadError);
909 	ss_incorp_remerrset(p, ss_sub_2(p));
910 	chended(p);
911 	return ss_inst_err(p);
912 }
913 
914 /* Query the reference value for the relative photometric (emission) reference value */
so_do_FloatRequest(ss * p,ss_comft comf,ss_comft * rcomf,double * comfv)915 inst_code so_do_FloatRequest(
916 ss *p,
917 ss_comft comf,		/* Choose common float type (PhotometricYRef) */
918 ss_comft *rcomf,	/* Return common float type (PhotometricYRef) */
919 double *comfv		/* Return the reference value */
920 ) {
921 	ss_add_soreq(p, ss_FloatRequest);
922 	ss_add_1(p, comf);
923 	ss_command(p, DF_TMO);
924 	ss_sub_soans(p, ss_FloatAnswer);
925 	*rcomf = ss_sub_1(p);
926 	*comfv = ss_sub_double(p);
927 	chended(p);
928 	return ss_inst_err(p);
929 }
930 
931 /* Set the reference value for the relative photometric (emission) reference value */
so_do_FloatDownload(ss * p,ss_comft comf,double comfv)932 inst_code so_do_FloatDownload(
933 ss *p,
934 ss_comft comf,		/* Choose common float type (PhotometricYRef) */
935 double comfv		/* The reference value */
936 ) {
937 	ss_add_soreq(p, ss_FloatDownload);
938 	ss_add_1(p, comf);
939 	ss_add_double(p, comfv);
940 	ss_command(p, DF_TMO);
941 	ss_sub_soans(p, ss_DownloadError);
942 	ss_incorp_remerrset(p, ss_sub_2(p));
943 	chended(p);
944 	return ss_inst_err(p);
945 }
946 
947 /* - - - - - - */
948 /* Calibration */
949 
950 /* Reset the spectra of the respective white reference to the original data */
so_do_ExecWhiteRefToOrigDat(ss * p)951 inst_code so_do_ExecWhiteRefToOrigDat(
952 ss *p
953 ) {
954 	ss_add_soreq(p, ss_ExecWhiteRefToOrigDat);
955 	ss_command(p, DF_TMO);
956 	ss_sub_soans(p, ss_DownloadError);		/* Instrument behavour doesn't match doco. */
957 	ss_incorp_remerrset(p, ss_sub_2(p));
958 	chended(p);
959 	return ss_inst_err(p);
960 }
961 
962 
963 /* Perform a Reference Measurement */
so_do_ExecRefMeasurement(ss * p,ss_mmt mm)964 inst_code so_do_ExecRefMeasurement(
965 ss *p,
966 ss_mmt mm	/* Measurement Mode (Meas/Cal etc.) */
967 ) {
968 #ifdef EMSST
969 	if (p->tmode != 0) {
970 		ss_rvt rv;
971 		double sp[36];
972 		ss_nmt nm;
973 		p->tmode = 0;
974 		ss_do_MoveAndMeasure(p, 155.0, 230.0, sp, &rv);
975 		so_do_NewMeasureRequest(p, &nm);
976 		ss_do_MoveAbsolut(p, p->sbr, p->sbx, p->sby);
977 		p->tmode = 1;
978 		return (inst_notify | ss_et_WhiteMeasOK);
979 	}
980 #endif
981 	ss_add_soreq(p, ss_ExecRefMeasurement);
982 	ss_add_1(p, 0x09);
983 	ss_add_1(p, mm);
984 	ss_command(p, 2.0 * DF_TMO);
985 	ss_sub_soans(p, ss_ExecError);
986 	ss_incorp_err(p, ss_sub_1(p));
987 	chended(p);
988 	return ss_inst_err(p);
989 }
990 
991 /* Perform a White Measurement - not recommended */
992 /* (ExecRefMeasuremen is preferred instead) */
so_do_ExecWhiteMeasurement(ss * p)993 inst_code so_do_ExecWhiteMeasurement(
994 ss *p
995 ) {
996 	ss_add_soreq(p, ss_ExecWhiteMeasurement);
997 	ss_command(p, DF_TMO);
998 	ss_sub_soans(p, ss_ExecError);
999 	ss_incorp_err(p, ss_sub_1(p));
1000 	chended(p);
1001 	return ss_inst_err(p);
1002 }
1003 
1004 /* - - - - - - - - - - - - */
1005 /* Performing measurements */
1006 
1007 
1008 /* Perform a Measurement */
so_do_ExecMeasurement(ss * p)1009 inst_code so_do_ExecMeasurement(
1010 ss *p
1011 ) {
1012 #ifdef EMSST
1013 	if (p->tmode != 0) {
1014 		inst_code rc;
1015 		p->tmode = 0;
1016 		ss_do_MoveAbsolut(p, ss_rt_SensorRef, 155.0, 230.0);
1017 		ss_do_MoveDown(p);
1018 		rc = so_do_ExecMeasurement(p);
1019 		ss_do_MoveUp(p);
1020 		ss_do_MoveAbsolut(p, p->sbr, p->sbx, p->sby);
1021 		p->tmode = 1;
1022 		return rc;
1023 	}
1024 #endif
1025 	ss_add_soreq(p, ss_ExecMeasurement);
1026 	ss_command(p, 2.0 * DF_TMO);
1027 	ss_sub_soans(p, ss_ExecError);
1028 	ss_incorp_err(p, ss_sub_1(p));
1029 	chended(p);
1030 	return ss_inst_err(p);
1031 }
1032 
1033 /* Define automatic output after each measurement */
1034 /* [Note that dealing with the resulting measurement replies */
1035 /*  isn't directly supported, currently.] */
so_do_SetMeasurementOutput(ss * p,ss_ost os,ss_os o)1036 inst_code so_do_SetMeasurementOutput(
1037 ss *p,
1038 ss_ost os,		/* Type of output to request */
1039 ss_os o			/* bitmask of output */
1040 ) {
1041 	ss_add_soreq(p, ss_SetMeasurementOutput);
1042 	ss_add_1(p, os);
1043 	ss_add_1(p, o.i);
1044 	ss_command(p, DF_TMO);
1045 	ss_sub_soans(p, ss_DownloadError);
1046 	ss_incorp_remerrset(p, ss_sub_2(p));
1047 	chended(p);
1048 
1049 	return ss_inst_err(p);
1050 }
1051 
1052 /* - - - - - - - - */
1053 /* Getting results */
1054 
1055 /* so_do_Printout isn't currently defined. */
1056 /* It needs to cope with the expected number of values that */
1057 /* will be returned. This could probably be figured out from */
1058 /* the string itself ? */
1059 
1060 /* Query Density measurement results and associated parameters */
so_do_DensityParameterRequest(ss * p,ss_cst * rct,double dv[4],ss_sdft * sdf,ss_rvt * rv,ss_aft * af,ss_wbt * wb,ss_dst * ds,ss_ilt * rit,ss_ot * ot)1061 inst_code so_do_DensityParameterRequest(
1062 ss *p,
1063 ss_cst *rct,	/* Return Color Type (Lab/XYZ etc.) */
1064 double dv[4],	/* Return Db Dc Dm Dy density values */
1065 ss_sdft *sdf,	/* Return Standard Density Filter (Db/Dc/Dm/Dy) */
1066 ss_rvt *rv,		/* Return Reference Valid Flag */
1067 ss_aft *af,		/* Return filter being used (None/Pol/D65/UV/custom */
1068 ss_wbt *wb,		/* Return white base (Paper/Absolute) */
1069 ss_dst *ds,		/* Return Density standard (ANSI/DIN/User etc.) */
1070 ss_ilt *rit,	/* Return Illuminant type (A/C/D65/User etc.) */
1071 ss_ot  *ot		/* Return Observer type (2deg/10deg) */
1072 ) {
1073 	int i;
1074 	ss_add_soreq(p, ss_DensityParameterRequest);
1075 	ss_add_1(p, 0x09);
1076 	ss_command(p, DF_TMO);
1077 	ss_sub_soans(p, ss_DensityParameterAnswer);
1078 	ss_sub_soans(p, 0x09);
1079 	for (i = 0; i < 4; i++)
1080 		dv[i] = ss_sub_double(p);
1081 	*sdf = ss_sub_1(p);
1082 	*rv = ss_sub_1(p);
1083 	*af = ss_sub_1(p);
1084 	*wb = ss_sub_1(p);
1085 	ss_sub_soans(p, 0x02);
1086 	*ds = ss_sub_1(p);
1087 	ss_incorp_remerrset(p, ss_sub_2(p));
1088 	chended(p);
1089 	return ss_inst_err(p);
1090 }
1091 
1092 /* Query Densitometric measurement values - not recommended */
1093 /* (DensityParameterRequest is preferred instead) */
so_do_DensityRequest(ss * p,double dv[4],ss_sdft * sdf,ss_rvt * rv)1094 inst_code so_do_DensityRequest(
1095 ss *p,
1096 double dv[4],	/* Return Db Dc Dm Dy density values */
1097 ss_sdft *sdf,	/* Return Standard Density Filter (Db/Dc/Dm/Dy) */
1098 ss_rvt  *rv		/* Return Reference Valid */
1099 ) {
1100 	int i;
1101 	ss_add_soreq(p, ss_DensityRequest);
1102 	ss_add_1(p, 0x09);
1103 	ss_command(p, DF_TMO);
1104 	ss_sub_soans(p, ss_DensityAnswer);
1105 	ss_sub_soans(p, 0x09);
1106 	for (i = 0; i < 4; i++)
1107 		dv[i] = ss_sub_double(p);
1108 	*sdf = ss_sub_1(p);
1109 	*rv = ss_sub_1(p);
1110 	ss_incorp_remerrset(p, ss_sub_2(p));
1111 	chended(p);
1112 	return ss_inst_err(p);
1113 }
1114 
1115 /* Query maximum density reading */
so_do_DmaxRequest(ss * p,double * Dmax,int * lambda,ss_dmot * dmo,ss_rvt * rv)1116 inst_code so_do_DmaxRequest(
1117 ss *p,
1118 double *Dmax,	/* Return Value of Maximum Density */
1119 int *lambda,	/* Return wavelength where maximum density was found */
1120 ss_dmot *dmo,	/* Return Dmax OK flag. */
1121 ss_rvt *rv		/* Return Reference Valid Flag */
1122 ) {
1123 	ss_add_soreq(p, ss_DmaxRequest);
1124 	ss_add_1(p, 0x09);
1125 	ss_command(p, DF_TMO);
1126 	ss_sub_soans(p, ss_DmaxAnswer);
1127 	ss_sub_soans(p, 0x09);
1128 	*Dmax = ss_sub_double(p);
1129 	*lambda = ss_sub_2(p);
1130 	*dmo = ss_sub_1(p);
1131 	*rv = ss_sub_1(p);
1132 	ss_incorp_remerrset(p, ss_sub_2(p));
1133 	chended(p);
1134 	return ss_inst_err(p);
1135 }
1136 
1137 /* Query Color measurement results and associated parameters */
so_do_CParameterRequest(ss * p,ss_cst ct,ss_cst * rct,double cv[3],ss_rvt * rv,ss_aft * af,ss_wbt * wb,ss_ilt * it,ss_ot * ot)1138 inst_code so_do_CParameterRequest(
1139 ss *p,
1140 ss_cst ct,		/* Choose Color Type (Lab/XYZ etc.) */
1141 ss_cst *rct,	/* Return Color Type (Lab/XYZ etc.) */
1142 double cv[3],	/* Return 3 color values */
1143 ss_rvt *rv,		/* Return Reference Valid Flag */
1144 ss_aft *af,		/* Return filter being used (None/Pol/D65/UV/custom) */
1145 ss_wbt *wb,		/* Return white base (Paper/Absolute) */
1146 ss_ilt *it,		/* Return Illuminant type (A/C/D65/User etc.) */
1147 ss_ot  *ot		/* Return Observer type (2deg/10deg) */
1148 ) {
1149 	int i;
1150 	ss_add_soreq(p, ss_CParameterRequest);
1151 	ss_add_1(p, 0x09);
1152 	ss_add_1(p,ct);
1153 	ss_command(p, DF_TMO);
1154 
1155 	ss_sub_soans(p, ss_CParameterAnswer);
1156 	ss_sub_soans(p, 0x09);
1157 	*rct = ss_sub_1(p);
1158 	for (i = 0; i < 3; i++)
1159 		cv[i] = ss_sub_double(p);
1160 	*rv = ss_sub_1(p);
1161 	*af = ss_sub_1(p);
1162 	*wb = ss_sub_1(p);
1163 	ss_sub_soans(p, 0x02);
1164 	*it = ss_sub_1(p);
1165 	*ot = ss_sub_1(p);
1166 	ss_incorp_remerrset(p, ss_sub_2(p));
1167 	chended(p);
1168 	return ss_inst_err(p);
1169 }
1170 
1171 /* Query Colorimetric measurement results - not recommended */
1172 /* (CParameterRequest is prefered instead) */
so_do_CRequest(ss * p,ss_cst * ct,double * cv[3],ss_rvt * rv)1173 inst_code so_do_CRequest(
1174 ss *p,
1175 ss_cst *ct,		/* Return Color Type (Lab/XYZ etc.) */
1176 double *cv[3],	/* Return 3 color values */
1177 ss_rvt *rv		/* Return Reference Valid Flag */
1178 ) {
1179 	int i;
1180 	ss_add_soreq(p, ss_CRequest);
1181 	ss_add_1(p, 0x09);
1182 	ss_command(p, DF_TMO);
1183 	ss_sub_soans(p, ss_CAnswer);
1184 	ss_sub_soans(p, 0x09);
1185 	*ct = ss_sub_1(p);
1186 	for (i = 0; i < 3; i++)
1187 		*cv[i] = ss_sub_double(p);
1188 	*rv = ss_sub_1(p);
1189 	ss_incorp_remerrset(p, ss_sub_2(p));
1190 	chended(p);
1191 	return ss_inst_err(p);
1192 }
1193 
1194 /* Query Spectral measurement results and associated parameters */
so_do_SpecParameterRequest(ss * p,ss_st st,ss_st * rst,double sp[36],ss_rvt * rv,ss_aft * af,ss_wbt * wb)1195 inst_code so_do_SpecParameterRequest(
1196 ss *p,
1197 ss_st st,		/* Choose Spectrum Type (Reflectance/Density) */
1198 ss_st *rst,		/* Return Spectrum Type (Reflectance/Density) */
1199 double sp[36],	/* Return 36 spectral values */
1200 ss_rvt *rv,		/* Return Reference Valid Flag */
1201 ss_aft *af,		/* Return filter being used (None/Pol/D65/UV/custom */
1202 ss_wbt *wb		/* Return white base (Paper/Absolute) */
1203 ) {
1204 	int i;
1205 	ss_add_soreq(p, ss_SpecParameterRequest);
1206 	ss_add_1(p, 0x09);
1207 	ss_add_1(p,st);
1208 	ss_command(p, DF_TMO);
1209 	ss_sub_soans(p, ss_SpecParameterAnswer);
1210 	ss_sub_soans(p, 0x09);
1211 	*rst = ss_sub_1(p);
1212 	for (i = 0; i < 36; i++)
1213 		sp[i] = ss_sub_double(p);
1214 	*rv = ss_sub_1(p);
1215 	*af = ss_sub_1(p);
1216 	*wb = ss_sub_1(p);
1217 	ss_sub_soans(p, 0x02);
1218 	ss_incorp_remerrset(p, ss_sub_2(p));
1219 	chended(p);
1220 	return ss_inst_err(p);
1221 }
1222 
1223 /* Query Spectral measurement results - not recommended */
1224 /* (SpecParameterRequest is preferred instead) */
so_do_SpectrumRequest(ss * p,ss_st * st,double sp[36],ss_rvt * rv)1225 inst_code so_do_SpectrumRequest(
1226 ss *p,
1227 ss_st *st,		/* Return Spectrum Type (Reflectance/Density) */
1228 double sp[36],	/* Return 36 spectral values */
1229 ss_rvt *rv		/* Return Reference Valid Flag */
1230 ) {
1231 	int i;
1232 	ss_add_soreq(p, ss_SpectrumRequest);
1233 	ss_add_1(p, 0x09);
1234 	ss_command(p, DF_TMO);
1235 	ss_sub_soans(p, ss_SpectrumAnswer);
1236 	ss_sub_soans(p, 0x09);
1237 	*st = ss_sub_1(p);
1238 	for (i = 0; i < 36; i++)
1239 		sp[i] = ss_sub_double(p);
1240 	*rv = ss_sub_1(p);
1241 	ss_incorp_remerrset(p, ss_sub_2(p));
1242 	chended(p);
1243 	return ss_inst_err(p);
1244 }
1245 
1246 /* - - - - - -  */
1247 /* Miscelanious */
1248 
1249 /* Query whether a new measurement was performed since the last access */
so_do_NewMeasureRequest(ss * p,ss_nmt * nm)1250 inst_code so_do_NewMeasureRequest(
1251 ss *p,
1252 ss_nmt *nm		/* Return New Measurement (None/Meas/White etc.) */
1253 ) {
1254 	ss_add_soreq(p, ss_NewMeasureRequest);
1255 	ss_command(p, DF_TMO);
1256 	ss_sub_soans(p, ss_NewMeasureAnswer);
1257 	if (nm != NULL)
1258 		*nm = ss_sub_1(p);
1259 	ss_sub_soans(p, 0x09);
1260 	chended(p);
1261 	return ss_inst_err(p);
1262 }
1263 
1264 /* Query whether a key was pressed since the last access */
so_do_NewKeyRequest(ss * p,ss_nkt * nk,ss_ks * k)1265 inst_code so_do_NewKeyRequest(
1266 ss *p,
1267 ss_nkt *nk,		/* Return whether a new key was pressed */
1268 ss_ks *k		/* Return the key that was pressed (none/meas) */
1269 ) {
1270 	ss_add_soreq(p, ss_NewKeyRequest);
1271 	ss_command(p, DF_TMO);
1272 	ss_sub_soans(p, ss_NewKeyAnswer);
1273 	*nk = ss_sub_1(p);
1274 	*k = ss_sub_2(p);
1275 	chended(p);
1276 	return ss_inst_err(p);
1277 }
1278 
1279 /* Query for the general error status */
so_do_ActErrorRequest(ss * p)1280 inst_code so_do_ActErrorRequest(
1281 ss *p
1282 ) {
1283 	ss_add_soreq(p, ss_ActErrorRequest);
1284 	ss_command(p, DF_TMO);
1285 	ss_sub_soans(p, ss_ActErrorAnswer);
1286 	ss_incorp_err(p, ss_sub_1(p));
1287 	chended(p);
1288 	return ss_inst_err(p);
1289 }
1290 
1291 /* Set Target On/Off status (locks key function of device?) */
so_do_TargetOnOffStDownload(ss * p,ss_toost oo)1292 inst_code so_do_TargetOnOffStDownload(
1293 ss *p,
1294 ss_toost oo		/* Activated/Deactivated */
1295 ) {
1296 	ss_add_soreq(p, ss_TargetOnOffStDownload);
1297 	ss_add_1(p, 0x00);
1298 	ss_add_1(p, oo);
1299 	ss_add_1(p, 0x00);
1300 	ss_command(p, DF_TMO);
1301 	ss_sub_soans(p, ss_DownloadError);
1302 	ss_incorp_remerrset(p, ss_sub_2(p));
1303 	chended(p);
1304 	return ss_inst_err(p);
1305 }
1306 
1307 /* =========================================== */
1308 /* SpectroScan/T specific commands and queries */
1309 
1310 /* - - - - - - - - - - - - - - - - - - - - */
1311 /* Device Initialisation and configuration */
1312 
1313 /* Initialise the device. Scans the Spectrolino */
1314 /* (Doesn't work when device is offline ) */
ss_do_ScanInitializeDevice(ss * p)1315 inst_code ss_do_ScanInitializeDevice(ss *p) {
1316 	inst_code rv;
1317 	ss_add_ssreq(p, ss_InitializeDevice);
1318 	ss_command(p, IT_TMO);
1319 	ss_sub_ssans(p, ss_ErrorAnswer);
1320 	ss_incorp_scanerr(p, ss_sub_1(p));
1321 	chended(p);
1322 	rv = ss_inst_err(p);
1323 
1324 	if (rv != inst_ok)
1325 		return rv;
1326 
1327 	/* Wait for Spectroscan to finish init. */
1328 	msec_sleep(3000);
1329 	return rv;
1330 }
1331 
1332 /* Establish communications between the SpectroScan and Spectrolino */
1333 /* at the highest possible baud rate. */
1334 /* (Doesn't work when device is offline ) */
ss_do_ScanSpectrolino(ss * p)1335 inst_code ss_do_ScanSpectrolino(ss *p) {
1336 	ss_add_ssreq(p, ss_ScanSpectrolino);
1337 	ss_command(p, IT_TMO);
1338 	ss_sub_ssans(p, ss_ErrorAnswer);
1339 	ss_incorp_scanerr(p, ss_sub_1(p));
1340 	chended(p);
1341 	return ss_inst_err(p);
1342 }
1343 
1344 /* Establish the zero position of the motors and set the position to 0,0 */
1345 /* (Doesn't work when device is offline ) */
ss_do_InitMotorPosition(ss * p)1346 inst_code ss_do_InitMotorPosition(ss *p) {
1347 	ss_add_ssreq(p, ss_InitMotorPosition);
1348 	ss_command(p, MV_TMO);
1349 	ss_sub_ssans(p, ss_ErrorAnswer);
1350 	ss_incorp_scanerr(p, ss_sub_1(p));
1351 	chended(p);
1352 	return ss_inst_err(p);
1353 }
1354 
1355 /* Change the SpectroScan baud rate */
ss_do_ChangeBaudRate(ss * p,ss_bt br)1356 inst_code ss_do_ChangeBaudRate(
1357 ss *p,
1358 ss_bt br		/* Baud rate (110 - 57600) */
1359 ) {
1360 	ss_add_ssreq(p, ss_ChangeBaudRate);
1361 	ss_add_1(p, br);
1362 	ss_command(p, SH_TMO);		/* Don't really expect an answer */
1363 	ss_sub_ssans(p, ss_ErrorAnswer);
1364 	ss_incorp_scanerr(p, ss_sub_1(p));
1365 	chended(p);
1366 	return ss_inst_err(p);		/* Will probably bomb because comms will break down */
1367 }
1368 
1369 /* Change the SpectroScan handshaking mode. */
ss_do_ChangeHandshake(ss * p,ss_hst hs)1370 inst_code ss_do_ChangeHandshake(
1371 ss *p,
1372 ss_hst hs		/* Handshake type (None/XonXoff/HW) */
1373 ) {
1374 	ss_add_ssreq(p, ss_ChangeHandshake);
1375 	ss_add_1(p, hs);
1376 	ss_command(p, DF_TMO);
1377 	ss_sub_ssans(p, ss_ErrorAnswer);
1378 	ss_incorp_scanerr(p, ss_sub_1(p));
1379 	chended(p);
1380 	return ss_inst_err(p);
1381 }
1382 
1383 /* Query the type of XY table */
ss_do_OutputType(ss * p,char dt[19])1384 inst_code ss_do_OutputType(
1385 ss *p,
1386 char dt[19]		/* Return Device Type ("SpectroScan", "SpectroScan " or "SpectroScanT") */
1387 ) {
1388 	ss_add_ssreq(p, ss_OutputType);
1389 	ss_command(p, DF_TMO);
1390 	ss_sub_ssans(p, ss_TypeAnswer);
1391 	ss_sub_string(p, dt, 18);
1392 	chended(p);
1393 #ifdef EMSST
1394 	if (strcmp(dt,"SpectroScan ") == 0
1395 	 || strcmp(dt,"SpectroScan") == 0)
1396 		sprintf(dt,"SpectroScanT");
1397 #endif
1398 	return ss_inst_err(p);
1399 }
1400 
1401 /* Query the serial number of the XY table */
ss_do_OutputSerialNumber(ss * p,unsigned int * sn)1402 inst_code ss_do_OutputSerialNumber(
1403 ss *p,
1404 unsigned int *sn	/* Return serial number */
1405 ) {
1406 	ss_add_ssreq(p, ss_OutputSerialNumber);
1407 	ss_command(p, DF_TMO);
1408 	ss_sub_ssans(p, ss_SerialNumberAnswer);
1409 	*sn = (unsigned int)ss_sub_4(p);
1410 	chended(p);
1411 	return ss_inst_err(p);
1412 }
1413 
1414 /* Query the part number of the XY table */
ss_do_OutputArticleNumber(ss * p,char pn[9])1415 inst_code ss_do_OutputArticleNumber(
1416 ss *p,
1417 char pn[9]		/* Return Part Number */
1418 ) {
1419 	ss_add_ssreq(p, ss_OutputArticleNumber);
1420 	ss_command(p, DF_TMO);
1421 	ss_sub_ssans(p, ss_ArticleNumberAnswer);
1422 	ss_sub_string(p, pn, 8);
1423 	chended(p);
1424 	return ss_inst_err(p);
1425 }
1426 
1427 /* Query the production date of the XY table */
ss_do_OutputProductionDate(ss * p,int * yp,int * mp,int * dp)1428 inst_code ss_do_OutputProductionDate(
1429 ss *p,
1430 int *yp,	/* Return Year of production (e.g. 1996) */
1431 int *mp,	/* Return Month of production (1-12) */
1432 int *dp		/* Return Day of production (1-31) */
1433 ) {
1434 	ss_add_ssreq(p, ss_OutputProductionDate);
1435 	ss_command(p, DF_TMO);
1436 	ss_sub_ssans(p, ss_ProductionDateAnswer);
1437 	*dp = ss_sub_2(p);
1438 	*mp = ss_sub_2(p);
1439 	*yp = ss_sub_2(p);
1440 	chended(p);
1441 	return ss_inst_err(p);
1442 }
1443 
1444 /* Query the Software Version of the XY table */
ss_do_OutputSoftwareVersion(ss * p,char sv[13])1445 inst_code ss_do_OutputSoftwareVersion(
1446 ss *p,
1447 char sv[13]		/* Return Software Version */
1448 ) {
1449 	ss_add_ssreq(p, ss_OutputSoftwareVersion);
1450 	ss_command(p, DF_TMO);
1451 	ss_sub_ssans(p, ss_SoftwareVersionAnswer);
1452 	ss_sub_string(p, sv, 12);
1453 	chended(p);
1454 	return ss_inst_err(p);
1455 }
1456 
1457 /* - - - - - - - - - - - - - */
1458 /* Measurement configuration */
1459 
1460 /* Set the SpectroScanT to reflectance or transmission. */
1461 /* The Spectrolino is also automatically set to the corresponding mode. */
1462 /* (Doesn't work when device is offline ) */
ss_do_SetTableMode(ss * p,ss_tmt tm)1463 inst_code ss_do_SetTableMode(
1464 ss *p,
1465 ss_tmt tm	/* Table mode (Reflectance/Transmission) */
1466 ) {
1467 #ifdef EMSST
1468 	if (tm == ss_tmt_Transmission) {
1469 		if (p->tmode == 0) {
1470 			ss_do_MoveAbsolut(p, p->sbr, p->sbx, p->sby);	/* ?? */
1471 		}
1472 		p->tmode = 1;
1473 	} else {
1474 		if (p->tmode != 0) {
1475 			p->tmode = 0;
1476 			ss_do_MoveHome(p);
1477 		}
1478 		p->tmode = 0;
1479 	}
1480 	return inst_ok;
1481 #endif
1482 	ss_add_ssreq(p, ss_SetTableMode);
1483 	ss_add_1(p, tm);
1484 	ss_command(p, IT_TMO);
1485 	ss_sub_ssans(p, ss_ErrorAnswer);
1486 	ss_incorp_scanerr(p, ss_sub_1(p));
1487 	chended(p);
1488 	return ss_inst_err(p);
1489 }
1490 
1491 /* - - - - - - - - - - - - - */
1492 /* Table operation */
1493 
1494 /* Set the SpectrScan to online. All moving keys are disabled. */
1495 /* (Only valid when device is in reflectance mode.) */
ss_do_SetDeviceOnline(ss * p)1496 inst_code ss_do_SetDeviceOnline(ss *p) {
1497 #ifdef EMSST
1498 	if (p->tmode != 0)
1499 		*((char *)0) = 55;
1500 //	return inst_unsupported;
1501 #endif
1502 	ss_add_ssreq(p, ss_SetDeviceOnline);
1503 	ss_command(p, DF_TMO);
1504 	ss_sub_ssans(p, ss_ErrorAnswer);
1505 	ss_incorp_scanerr(p, ss_sub_1(p));
1506 	chended(p);
1507 	return ss_inst_err(p);
1508 }
1509 
1510 /* Set the SpectrScan to offline. All moving keys are enabled. */
1511 /* (Only valid when device is in reflectance mode.) */
1512 /* (All remote commands to move the device will be ignored.) */
ss_do_SetDeviceOffline(ss * p)1513 inst_code ss_do_SetDeviceOffline(ss *p) {
1514 #ifdef EMSST
1515 	if (p->tmode != 0)
1516 		*((char *)0) = 55;
1517 #endif
1518 	ss_add_ssreq(p, ss_SetDeviceOffline);
1519 	ss_command(p, DF_TMO);
1520 	ss_sub_ssans(p, ss_ErrorAnswer);
1521 	ss_incorp_scanerr(p, ss_sub_1(p));
1522 	chended(p);
1523 	return ss_inst_err(p);
1524 }
1525 
1526 /* Enable electrostatic paper hold. */
1527 /* (Not valid when device is offline) */
ss_do_HoldPaper(ss * p)1528 inst_code ss_do_HoldPaper(ss *p) {
1529 	ss_add_ssreq(p, ss_HoldPaper);
1530 	ss_command(p, DF_TMO);
1531 	ss_sub_ssans(p, ss_ErrorAnswer);
1532 	ss_incorp_scanerr(p, ss_sub_1(p));
1533 	chended(p);
1534 	return ss_inst_err(p);
1535 }
1536 
1537 /* Disable electrostatic paper hold. */
1538 /* (Not valid when device is offline) */
ss_do_ReleasePaper(ss * p)1539 inst_code ss_do_ReleasePaper(ss *p) {
1540 	ss_add_ssreq(p, ss_ReleasePaper);
1541 	ss_command(p, DF_TMO);
1542 	ss_sub_ssans(p, ss_ErrorAnswer);
1543 	ss_incorp_scanerr(p, ss_sub_1(p));
1544 	chended(p);
1545 	return ss_inst_err(p);
1546 }
1547 
1548 /* - - - - - - */
1549 /* Positioning */
1550 
1551 /* Move either the sight or sensor to an absolute position. */
1552 /* (Doesn't work when device is offline or transmissioin mode.) */
ss_do_MoveAbsolut(ss * p,ss_rt r,double x,double y)1553 inst_code ss_do_MoveAbsolut(
1554 ss *p,
1555 ss_rt  r,	/* Reference (Sensor/Sight) */
1556 double x,	/* X coord in mm, 0-310.0, accurate to 0.1mm */
1557 double y	/* Y coord in mm, 0-230.0, accurate to 0.1mm */
1558 ) {
1559 #ifdef EMSST
1560 	if (p->tmode != 0)
1561 		*((char *)0) = 55;
1562 #endif
1563 	ss_add_ssreq(p, ss_MoveAbsolut);
1564 	ss_add_1(p, r);
1565 	ss_add_2(p, (int)(x * 10 + 0.5));
1566 	ss_add_2(p, (int)(y * 10 + 0.5));
1567 	ss_command(p, MV_TMO);
1568 	ss_sub_ssans(p, ss_ErrorAnswer);
1569 	ss_incorp_scanerr(p, ss_sub_1(p));
1570 	chended(p);
1571 	return ss_inst_err(p);
1572 }
1573 
1574 /* Move relative to current position. */
1575 /* (Doesn't work when device is offline or transmissioin mode.) */
ss_do_MoveRelative(ss * p,double x,double y)1576 inst_code ss_do_MoveRelative(
1577 ss *p,
1578 double x,	/* X distance in mm, 0-310.0, accurate to 0.1mm */
1579 double y	/* Y distance in mm, 0-230.0, accurate to 0.1mm */
1580 ) {
1581 #ifdef EMSST
1582 	if (p->tmode != 0)
1583 		*((char *)0) = 55;
1584 #endif
1585 	ss_add_ssreq(p, ss_MoveRelative);
1586 	ss_add_2(p, (int)(x * 10 + 0.5));
1587 	ss_add_2(p, (int)(y * 10 + 0.5));
1588 	ss_command(p, MV_TMO);
1589 	ss_sub_ssans(p, ss_ErrorAnswer);
1590 	ss_incorp_scanerr(p, ss_sub_1(p));
1591 	chended(p);
1592 	return ss_inst_err(p);
1593 }
1594 
1595 /* Move to the home position (== 0,0). */
1596 /* (Doesn't work when device is offline or transmissioin mode.) */
ss_do_MoveHome(ss * p)1597 inst_code ss_do_MoveHome(
1598 ss *p
1599 ) {
1600 #ifdef EMSST
1601 	if (p->tmode != 0)
1602 		*((char *)0) = 55;
1603 #endif
1604 	ss_add_ssreq(p, ss_MoveHome);
1605 	ss_command(p, MV_TMO);
1606 	ss_sub_ssans(p, ss_ErrorAnswer);
1607 	ss_incorp_scanerr(p, ss_sub_1(p));
1608 	chended(p);
1609 	return ss_inst_err(p);
1610 }
1611 
1612 /* Move to the sensor up. */
1613 /* (Doesn't work when device is offline or transmissioin mode.) */
ss_do_MoveUp(ss * p)1614 inst_code ss_do_MoveUp(
1615 ss *p
1616 ) {
1617 #ifdef EMSST
1618 	if (p->tmode != 0)
1619 		*((char *)0) = 55;
1620 #endif
1621 	ss_add_ssreq(p, ss_MoveUp);
1622 	ss_command(p, MV_TMO);
1623 	ss_sub_ssans(p, ss_ErrorAnswer);
1624 	ss_incorp_scanerr(p, ss_sub_1(p));
1625 	chended(p);
1626 	return ss_inst_err(p);
1627 }
1628 
1629 /* Move to the sensor down. */
1630 /* (Doesn't work when device is offline or transmission mode.) */
ss_do_MoveDown(ss * p)1631 inst_code ss_do_MoveDown(
1632 ss *p
1633 ) {
1634 #ifdef EMSST
1635 	if (p->tmode != 0)
1636 		*((char *)0) = 55;
1637 #endif
1638 	ss_add_ssreq(p, ss_MoveDown);
1639 	ss_command(p, MV_TMO);
1640 	ss_sub_ssans(p, ss_ErrorAnswer);
1641 	ss_incorp_scanerr(p, ss_sub_1(p));
1642 	chended(p);
1643 	return ss_inst_err(p);
1644 }
1645 
1646 /* Query the current absolute position of the sensor or sight. */
1647 /* (Doesn't work when device is offline or transmissioin mode.) */
ss_do_OutputActualPosition(ss * p,ss_rt r,ss_rt * rr,double * x,double * y,ss_zkt * zk)1648 inst_code ss_do_OutputActualPosition(
1649 ss *p,
1650 ss_rt  r,	/* Reference (Sensor/Sight) */
1651 ss_rt  *rr,	/* Return reference (Sensor/Sight) */
1652 double *x,	/* Return the X coord in mm, 0-310.0, accurate to 0.1mm */
1653 double *y,	/* Return the Y coord in mm, 0-230.0, accurate to 0.1mm */
1654 ss_zkt *zk	/* Return the Z coordinate (Up/Down) */
1655 ) {
1656 #ifdef EMSST
1657 	if (p->tmode != 0)
1658 		*((char *)0) = 55;
1659 #endif
1660 	ss_add_ssreq(p, ss_OutputActualPosition);
1661 	ss_add_1(p, r);
1662 	ss_command(p, DF_TMO);
1663 	ss_sub_ssans(p, ss_PositionAnswer);
1664 	*rr = ss_sub_1(p);
1665 	ss_sub_soans(p, 0x00);
1666 	ss_sub_soans(p, 0x00);
1667 	*x = ss_sub_2(p)/10.0;
1668 	*y = ss_sub_2(p)/10.0;
1669 	*zk = ss_sub_1(p);
1670 	chended(p);
1671 	return ss_inst_err(p);
1672 }
1673 
1674 /* Move to the white reference position */
1675 /* (Doesn't work when device is offline or transmissioin mode.) */
ss_do_MoveToWhiteRefPos(ss * p,ss_wrpt wrp)1676 inst_code ss_do_MoveToWhiteRefPos(
1677 ss *p,
1678 ss_wrpt wrp		/* White Reference Position (Tile1/Tile2) */
1679 ) {
1680 #ifdef EMSST
1681 	if (p->tmode != 0)
1682 		*((char *)0) = 55;
1683 #endif
1684 	ss_add_ssreq(p, ss_MoveToWhiteRefPos);
1685 	ss_add_1(p, wrp);
1686 	ss_command(p, MV_TMO);
1687 	ss_sub_ssans(p, ss_ErrorAnswer);
1688 	ss_incorp_scanerr(p, ss_sub_1(p));
1689 	chended(p);
1690 	return ss_inst_err(p);
1691 }
1692 
1693 /* - - - - - - - - - - - - */
1694 /* Performing measurements */
1695 
1696 /* Move the sensor to an absolute position, move the */
1697 /* sensor down, execute a measurement, move the head up, */
1698 /* and return spectral measuring results. */
ss_do_MoveAndMeasure(ss * p,double x,double y,double sp[36],ss_rvt * rv)1699 inst_code ss_do_MoveAndMeasure(
1700 ss *p,
1701 double x,		/* X coord in mm, 0-310.0, accurate to 0.1mm */
1702 double y,		/* Y coord in mm, 0-230.0, accurate to 0.1mm */
1703 double sp[36],	/* Return 36 spectral values */
1704 ss_rvt *rv		/* Return Reference Valid Flag */
1705 ) {
1706 #ifdef EMSST
1707 	/* Not sure if this is valid on the SpectroScanT in trans mode ? */
1708 	if (p->tmode != 0) {
1709 		inst_code rc;
1710 		p->tmode = 0;
1711 		rc = ss_do_MoveAndMeasure(p, 155.0, 230.0, sp, rv);
1712 		ss_do_MoveAbsolut(p, p->sbr, p->sbx, p->sby);
1713 		p->tmode = 1;
1714 		return rc;
1715 	}
1716 #endif
1717 	ss_add_ssreq(p, ss_MoveAndMeasure);
1718 	ss_add_2(p, (int)(x * 10 + 0.5));
1719 	ss_add_2(p, (int)(y * 10 + 0.5));
1720 	ss_command(p, MV_TMO);
1721 	if (ss_peek_ans(p) == ss_SpectrumAnswer) {
1722 		int i;
1723 		ss_sub_soans(p, ss_SpectrumAnswer);
1724 		ss_sub_soans(p, 0x09);
1725 		ss_sub_soans(p, 0x00);
1726 		for (i = 0; i < 36; i++)
1727 			sp[i] = ss_sub_double(p);
1728 		*rv = ss_sub_1(p);
1729 		ss_incorp_remerrset(p, ss_sub_2(p));
1730 	} else {
1731 		ss_sub_ssans(p, ss_ErrorAnswer);
1732 		ss_incorp_scanerr(p, ss_sub_1(p));
1733 	}
1734 	chended(p);
1735 	return ss_inst_err(p);
1736 }
1737 
1738 /* - - - - - -  */
1739 /* Miscelanious */
1740 
1741 /* Set the SpectroScanT transmission light level during standby. */
1742 /* (Only valid on SpectroScanT in transmission mode) */
ss_do_SetLightLevel(ss * p,ss_llt ll)1743 inst_code ss_do_SetLightLevel(
1744 ss *p,
1745 ss_llt ll	/* Transmission light level (Off/Surround/Low) */
1746 ) {
1747 #ifdef EMSST
1748 	if (p->tmode != 0)
1749 		return inst_ok;
1750 	else
1751 		*((char *)0) = 55;
1752 #endif
1753 	ss_add_ssreq(p, ss_SetLightLevel);
1754 	ss_add_1(p, ll);
1755 	ss_command(p, DF_TMO);
1756 	ss_sub_ssans(p, ss_ErrorAnswer);
1757 	ss_incorp_scanerr(p, ss_sub_1(p));
1758 	chended(p);
1759 	return ss_inst_err(p);
1760 }
1761 
1762 /* Set tranmission standby position. */
1763 /* (Only valid on SpectroScanT in transmission mode) */
ss_do_SetTransmStandbyPos(ss * p,ss_rt r,double x,double y)1764 inst_code ss_do_SetTransmStandbyPos(
1765 ss *p,
1766 ss_rt  r,	/* Reference (Sensor/Sight) */
1767 double x,	/* X coord in mm, 0-310.0, accurate to 0.1mm */
1768 double y	/* Y coord in mm, 0-230.0, accurate to 0.1mm */
1769 ) {
1770 #ifdef EMSST
1771 	if (p->tmode != 0) {
1772 		p->sbr = r;
1773 		p->sbx = x;
1774 		p->sby = y;
1775 		return inst_ok;
1776 	} else {
1777 		*((char *)0) = 55;
1778 	}
1779 #endif
1780 	ss_add_ssreq(p, ss_SetTransmStandbyPos);
1781 	ss_add_1(p, r);
1782 	ss_add_2(p, (int)(x * 10 + 0.5));
1783 	ss_add_2(p, (int)(y * 10 + 0.5));
1784 	ss_command(p, DF_TMO);
1785 	ss_sub_ssans(p, ss_ErrorAnswer);
1786 	ss_incorp_scanerr(p, ss_sub_1(p));
1787 	chended(p);
1788 	return ss_inst_err(p);
1789 }
1790 
1791 /* Set digitizing mode. Clears digitizing buffer, */
1792 /* and puts the device offline. The user can move */
1793 /* and enter positions. */
ss_do_SetDigitizingMode(ss * p)1794 inst_code ss_do_SetDigitizingMode(ss *p) {
1795 	ss_add_ssreq(p, ss_SetDigitizingMode);
1796 	ss_command(p, DF_TMO);
1797 	ss_sub_ssans(p, ss_ErrorAnswer);
1798 	ss_incorp_scanerr(p, ss_sub_1(p));
1799 	chended(p);
1800 	return ss_inst_err(p);
1801 }
1802 
1803 /* Get last digitized position from memory. */
ss_do_OutputDigitizingValues(ss * p,ss_rt r,ss_rt * rr,int * nrp,double * x,double * y,ss_zkt * zk)1804 inst_code ss_do_OutputDigitizingValues(
1805 ss *p,
1806 ss_rt  r,	/* Reference (Sensor/Sight) */
1807 ss_rt  *rr,	/* Return reference (Sensor/Sight) */
1808 int    *nrp,/* Return the number of remaining positions in memory. */
1809 double *x,	/* Return the X coord in mm, 0-310.0, accurate to 0.1mm */
1810 double *y,	/* Return the Y coord in mm, 0-230.0, accurate to 0.1mm */
1811 ss_zkt *zk	/* Return the Z coordinate (Up/Down) */
1812 ) {
1813 	ss_add_ssreq(p, ss_OutputDigitizingValues);
1814 	ss_add_1(p, r);
1815 	ss_command(p, DF_TMO);
1816 	ss_sub_ssans(p, ss_PositionAnswer);
1817 	*rr = ss_sub_1(p);
1818 	*nrp = ss_sub_2(p);		/* Should be unsigned ?? */
1819 	*x = ss_sub_2(p)/10.0;
1820 	*y = ss_sub_2(p)/10.0;
1821 	*zk = ss_sub_1(p);
1822 	chended(p);
1823 	return ss_inst_err(p);
1824 }
1825 
1826 /* Turn on key aknowledge mode. Causes a KeyAnswer message */
1827 /* to be generated whenever a key is pressed. */
1828 /* (KetAnswer isn't well supported here ?) */
ss_do_SetKeyAcknowlge(ss * p)1829 inst_code ss_do_SetKeyAcknowlge(ss *p) {
1830 	ss_add_ssreq(p, ss_SetKeyAcknowlge);
1831 	ss_command(p, DF_TMO);
1832 	ss_sub_ssans(p, ss_ErrorAnswer);
1833 	ss_incorp_scanerr(p, ss_sub_1(p));
1834 	chended(p);
1835 	return ss_inst_err(p);
1836 }
1837 
1838 /* Turn off key aknowledge mode. */
ss_do_ResetKeyAcknowlge(ss * p)1839 inst_code ss_do_ResetKeyAcknowlge(ss *p) {
1840 	ss_add_ssreq(p, ss_ResetKeyAcknowlge);
1841 	ss_command(p, DF_TMO);
1842 	ss_sub_ssans(p, ss_ErrorAnswer);
1843 	ss_incorp_scanerr(p, ss_sub_1(p));
1844 	chended(p);
1845 	return ss_inst_err(p);
1846 }
1847 
1848 /* Query the keys that are currently pressed */
ss_do_OutputActualKey(ss * p,ss_sks * sk,ss_ptt * pt)1849 inst_code ss_do_OutputActualKey(
1850 ss *p,
1851 ss_sks *sk,	/* Return Scan Key Set (Key bitmask) */
1852 ss_ptt *pt	/* Return press time (Short/Long) */
1853 ) {
1854 	ss_add_ssreq(p, ss_OutputActualKey);
1855 	ss_command(p, DF_TMO);
1856 	ss_sub_ssans(p, ss_KeyAnswer);
1857 	*sk = ss_sub_1(p);
1858 	*pt = ss_sub_1(p);
1859 	chended(p);
1860 	return ss_inst_err(p);
1861 }
1862 
1863 /* Query the keys that were last pressed */
ss_do_OutputLastKey(ss * p,ss_sks * sk,ss_ptt * pt)1864 inst_code ss_do_OutputLastKey(
1865 ss *p,
1866 ss_sks *sk,	/* Return Scan Key bitmask (Keys) */
1867 ss_ptt *pt	/* Return press time (Short/Long) */
1868 ) {
1869 	ss_add_ssreq(p, ss_OutputLastKey);
1870 	ss_command(p, DF_TMO);
1871 	ss_sub_ssans(p, ss_KeyAnswer);
1872 	*sk = ss_sub_1(p);
1873 	*pt = ss_sub_1(p);
1874 	chended(p);
1875 	return ss_inst_err(p);
1876 }
1877 
1878 /* Query the status register */
ss_do_OutputStatus(ss * p,ss_sts * st)1879 inst_code ss_do_OutputStatus(
1880 ss *p,
1881 ss_sts *st	/* Return status bitmask (Enter key/Online/Digitize/KeyAck/Paper) */
1882 ) {
1883 	ss_add_ssreq(p, ss_OutputStatus);
1884 	ss_command(p, DF_TMO);
1885 	ss_sub_ssans(p, ss_StatusAnswer);
1886 	*st = ss_sub_1(p);
1887 	chended(p);
1888 	return ss_inst_err(p);
1889 }
1890 
1891 /* Clear the status register */
ss_do_ClearStatus(ss * p,ss_sts st)1892 inst_code ss_do_ClearStatus(
1893 ss *p,
1894 ss_sts st	/* Status to reset (Enter key/Online/Digitize/KeyAck/Paper) */
1895 ) {
1896 	ss_add_ssreq(p, ss_ClearStatus);
1897 	ss_add_1(p, st);
1898 	ss_command(p, DF_TMO);
1899 	ss_sub_ssans(p, ss_ErrorAnswer);
1900 	ss_incorp_scanerr(p, ss_sub_1(p));
1901 	chended(p);
1902 	return ss_inst_err(p);
1903 }
1904 
1905 /* Set the special status register */
1906 /* (Set to all 0 on reset) */
ss_do_SetSpecialStatus(ss * p,ss_sss sss)1907 inst_code ss_do_SetSpecialStatus(
1908 ss *p,
1909 ss_sss sss	/* Status bits to set (HeadDwnOnMv/TableInTransMode/AllLightsOn) */
1910 ) {
1911 	ss_add_ssreq(p, ss_SetSpecialStatus);
1912 	ss_add_1(p, sss);
1913 	ss_command(p, DF_TMO);
1914 	ss_sub_ssans(p, ss_ErrorAnswer);
1915 	ss_incorp_scanerr(p, ss_sub_1(p));
1916 	chended(p);
1917 	return ss_inst_err(p);
1918 }
1919 
1920 /* Clear the special status register */
ss_do_ClearSpecialStatus(ss * p,ss_sss sss)1921 inst_code ss_do_ClearSpecialStatus(
1922 ss *p,
1923 ss_sss sss	/* Status bits to clear (HeadDwnOnMv/TableInTransMode/AllLightsOn) */
1924 ) {
1925 	ss_add_ssreq(p, ss_ClearSpecialStatus);
1926 	ss_add_1(p, sss);
1927 	ss_command(p, DF_TMO);
1928 	ss_sub_ssans(p, ss_ErrorAnswer);
1929 	ss_incorp_scanerr(p, ss_sub_1(p));
1930 	chended(p);
1931 	return ss_inst_err(p);
1932 }
1933 
1934 /* Query the special status register */
ss_do_OutputSpecialStatus(ss * p,ss_sss * sss)1935 inst_code ss_do_OutputSpecialStatus(
1936 ss *p,
1937 ss_sss *sss	/* Return Special Status bits */
1938 ) {
1939 	ss_add_ssreq(p, ss_OutputSpecialStatus);
1940 	ss_command(p, DF_TMO);
1941 	ss_sub_ssans(p, ss_StatusAnswer);
1942 	*sss = ss_sub_1(p);
1943 	chended(p);
1944 	return ss_inst_err(p);
1945 }
1946 
1947 
1948 #define SS_IMP_H
1949 #endif /* SS_IMP_H */
1950 
1951 
1952