Commit cd63e2fb by jim

Move ethstream to the parent directory


git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@8314 ddd99763-3ecb-0310-9145-efcb8ce7c51f
parent 329871bf
...@@ -32,11 +32,11 @@ default: lin ...@@ -32,11 +32,11 @@ default: lin
all: lin win all: lin win
.PHONY: lin .PHONY: lin
lin: ljtest ethstream ljconfig \ lin: ethstream \
ethstream.1 ljconfig.1 ethstream.1
.PHONY: win .PHONY: win
win: ljtest.exe ethstream.exe ljconfig.exe win: ethstream.exe
version.h: VERSION version.h: VERSION
...@@ -46,17 +46,11 @@ version.h: VERSION ...@@ -46,17 +46,11 @@ version.h: VERSION
# Object files for each executable # Object files for each executable
obj-common = opt.o ue9.o ue9error.o netutil.o debug.o nerdjack.o obj-common = opt.o ue9.o ue9error.o netutil.o debug.o nerdjack.o
obj-ljconfig = ljconfig.o $(obj-common)
obj-ethstream = ethstream.o $(obj-common) obj-ethstream = ethstream.o $(obj-common)
obj-ljtest = ljtest.o $(obj-common)
ljconfig: $(obj-ljconfig)
ethstream: $(obj-ethstream) ethstream: $(obj-ethstream)
ljtest: $(obj-ljtest)
ljconfig.exe: $(obj-ljconfig:.o=.obj) compat-win32.obj
ethstream.exe: $(obj-ethstream:.o=.obj) compat-win32.obj ethstream.exe: $(obj-ethstream:.o=.obj) compat-win32.obj
ljtest.exe: $(obj-ljtest:.o=.obj) compat-win32.obj
# Manpages # Manpages
...@@ -92,7 +86,7 @@ dist: version.h ...@@ -92,7 +86,7 @@ dist: version.h
.PHONY: clean distclean .PHONY: clean distclean
clean distclean: clean distclean:
rm -f *.o *.obj *.exe ethstream ljtest ljconfig core *.d *.1 *.txt rm -f *.o *.obj *.exe ethstream core *.d *.1 *.txt
# Dependency tracking: # Dependency tracking:
......
...@@ -2,5 +2,10 @@ Labjack/Nerdjack Tools ...@@ -2,5 +2,10 @@ Labjack/Nerdjack Tools
by Jim Paris <jim@jtan.com> by Jim Paris <jim@jtan.com>
with modifications by Zach Clifford <zacharyc@mit.edu> with modifications by Zach Clifford <zacharyc@mit.edu>
These tools are for interacting with the LabJack UE9 or the NerdJack over the Ethernet interface. More information about the UE9 device: These tools are for interacting with the LabJack UE9 or the NerdJack
over the Ethernet interface. More information about the UE9 device:
http://www.labjack.com/labjack_ue9.php http://www.labjack.com/labjack_ue9.php
The NerdJack device is a custom board made in LEES by Zach Clifford.
Use ethstream -h or ethstream -X for usage instructions and examples.
...@@ -3,83 +3,56 @@ ...@@ -3,83 +3,56 @@
#include "compat.h" #include "compat.h"
#include <windows.h> #include <windows.h>
unsigned int unsigned int sleep(unsigned int seconds)
sleep (unsigned int seconds)
{ {
Sleep (seconds * 1000); Sleep(seconds * 1000);
return 0; return 0;
} }
static struct static struct {
{ int num;
int num; char *msg;
char *msg; } win32_error[] = {
} win32_error[] = /* Errors that we might vaguely expect to see */
{ { WSAEINTR, "Winsock: Interrupted system call"},
/* Errors that we might vaguely expect to see */ { WSAEBADF, "Winsock: Bad file number"},
{ { WSAEFAULT, "Winsock: Bad address"},
WSAEINTR, "Winsock: Interrupted system call"}, { WSAEINVAL, "Winsock: Invalid argument"},
{ { WSAEMFILE, "Winsock: Too many open files"},
WSAEBADF, "Winsock: Bad file number"}, { WSAEWOULDBLOCK, "Winsock: Operation would block"},
{ { WSAEINPROGRESS, "Winsock: Operation now in progress"},
WSAEFAULT, "Winsock: Bad address"}, { WSAEALREADY, "Winsock: Operation already in progress"},
{ { WSAENOTSOCK, "Winsock: Socket operation on nonsocket"},
WSAEINVAL, "Winsock: Invalid argument"}, { WSAEADDRINUSE, "Winsock: Address already in use"},
{ { WSAEADDRNOTAVAIL, "Winsock: Cannot assign requested address"},
WSAEMFILE, "Winsock: Too many open files"}, { WSAENETDOWN, "Winsock: Network is down"},
{ { WSAENETUNREACH, "Winsock: Network is unreachable"},
WSAEWOULDBLOCK, "Winsock: Operation would block"}, { WSAENETRESET, "Winsock: Network dropped connection on reset"},
{ { WSAECONNABORTED, "Winsock: Software caused connection abort"},
WSAEINPROGRESS, "Winsock: Operation now in progress"}, { WSAECONNRESET, "Winsock: Connection reset by peer"},
{ { WSAETIMEDOUT, "Winsock: Connection timed out"},
WSAEALREADY, "Winsock: Operation already in progress"}, { WSAECONNREFUSED, "Winsock: Connection refused"},
{ { WSAEHOSTDOWN, "Winsock: Host is down"},
WSAENOTSOCK, "Winsock: Socket operation on nonsocket"}, { WSAEHOSTUNREACH, "Winsock: No route to host"},
{ { WSAVERNOTSUPPORTED, "Winsock: Unsupported Winsock version"},
WSAEADDRINUSE, "Winsock: Address already in use"}, { ETIMEDOUT, "Connection timed out"},
{ { ENOTCONN, "Not connected"},
WSAEADDRNOTAVAIL, "Winsock: Cannot assign requested address"}, { -1, NULL},
{ };
WSAENETDOWN, "Winsock: Network is down"},
{ char *compat_strerror(int errnum)
WSAENETUNREACH, "Winsock: Network is unreachable"},
{
WSAENETRESET, "Winsock: Network dropped connection on reset"},
{
WSAECONNABORTED, "Winsock: Software caused connection abort"},
{
WSAECONNRESET, "Winsock: Connection reset by peer"},
{
WSAETIMEDOUT, "Winsock: Connection timed out"},
{
WSAECONNREFUSED, "Winsock: Connection refused"},
{
WSAEHOSTDOWN, "Winsock: Host is down"},
{
WSAEHOSTUNREACH, "Winsock: No route to host"},
{
WSAVERNOTSUPPORTED, "Winsock: Unsupported Winsock version"},
{
ETIMEDOUT, "Connection timed out"},
{
ENOTCONN, "Not connected"},
{
-1, NULL},};
char *
compat_strerror (int errnum)
{ {
int i; int i;
static char buf[128]; static char buf[128];
for (i = 0; win32_error[i].num != -1; i++) for (i = 0; win32_error[i].num != -1; i++)
if (errnum == win32_error[i].num) if (errnum == win32_error[i].num)
return win32_error[i].msg; return win32_error[i].msg;
if (errnum >= 10000) if (errnum >= 10000) {
{ sprintf(buf, "Winsock: unknown error %d\n", errnum);
sprintf (buf, "Winsock: unknown error %d\n", errnum); return buf;
return buf; }
} return strerror(errnum);
return strerror (errnum);
} }
#ifdef __WIN32__ #ifdef __WIN32__
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
#define COMPAT_H #define COMPAT_H
#ifdef __WIN32__ #ifdef __WIN32__
unsigned int sleep (unsigned int seconds); unsigned int sleep(unsigned int seconds);
char *compat_strerror (int errnum); char *compat_strerror(int errnum);
//const char *inet_ntop(int af, void *src, const char *dst, socklen_t cnt); //const char *inet_ntop(int af, void *src, const char *dst, socklen_t cnt);
#define INET_ADDRSTRLEN 16 #define INET_ADDRSTRLEN 16
#define ETIMEDOUT 110 #define ETIMEDOUT 110
......
...@@ -4,15 +4,14 @@ ...@@ -4,15 +4,14 @@
int verb_count = 0; int verb_count = 0;
int int func_fprintf(const char *func, FILE * stream, const char *format, ...)
func_fprintf (const char *func, FILE * stream, const char *format, ...)
{ {
va_list ap; va_list ap;
int ret; int ret;
fprintf (stream, "%s: ", func); fprintf(stream, "%s: ", func);
va_start (ap, format); va_start(ap, format);
ret = vfprintf (stream, format, ap); ret = vfprintf(stream, format, ap);
va_end (ap); va_end(ap);
return ret; return ret;
} }
...@@ -14,8 +14,8 @@ extern int verb_count; ...@@ -14,8 +14,8 @@ extern int verb_count;
#include <stdio.h> #include <stdio.h>
int func_fprintf (const char *func, FILE * stream, const char *format, int func_fprintf(const char *func, FILE * stream, const char *format,
...) __attribute__ ((format (printf, 3, 4))); ...) __attribute__ ((format(printf, 3, 4)));
#define debug(x...) ({ \ #define debug(x...) ({ \
if(verb_count >= 2) \ if(verb_count >= 2) \
......
...@@ -7,11 +7,6 @@ ...@@ -7,11 +7,6 @@
* License as published by the Free Software Foundation; see COPYING. * License as published by the Free Software Foundation; see COPYING.
*/ */
/* ljstream: Stream data from the first N (1-14) analog inputs.
Resolution is set to 12-bit and all channels are in bipolar (-5 to
+5V) mode.
*/
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
...@@ -37,697 +32,697 @@ ...@@ -37,697 +32,697 @@
#define UE9_COMMAND_PORT 52360 #define UE9_COMMAND_PORT 52360
#define UE9_DATA_PORT 52361 #define UE9_DATA_PORT 52361
#define MAX_CHANNELS 256
struct callbackInfo struct callbackInfo {
{ struct ue9Calibration calib;
struct ue9Calibration calib; int convert;
int convert; int maxlines;
int maxlines;
}; };
struct options opt[] = { struct options opt[] = {
{'a', "address", "string", "host/address of device (192.168.1.209)"}, {'a', "address", "string", "host/address of device (192.168.1.209)"},
{'n', "numchannels", "n", "sample the first N ADC channels (2)"}, {'n', "numchannels", "n", "sample the first N ADC channels (2)"},
{'N', "nerdjack", NULL, "Force NerdJack device"}, {'C', "channels", "a,b,c", "sample channels a, b, and c"},
{'L', "labjack",NULL,"Force LabJack device"}, {'r', "rate", "hz", "sample each channel at this rate (8000.0)"},
{'d', "detect", NULL, "Detect NerdJack IP address"},
{'R', "range", "a,b", {'L', "labjack", NULL, "Force LabJack device"},
"Set range on NerdJack for channels 0-5,6-11 to either 5 or 10 (10,10)"}, {'t', "timers", "a,b,c", "set LabJack timer modes to a, b, and c"},
{'C', "channels", "a,b,c", "sample channels a, b, and c"}, {'T', "timerdivisor", "n", "set LabJack timer divisor to n"},
{'r', "rate", "hz", "sample each channel at this rate (8000.0)"},
{'o', "oneshot", NULL, "don't retry in case of errors"}, {'N', "nerdjack", NULL, "Force NerdJack device"},
{'f', "forceretry", NULL, "retry no matter what happens"}, {'d', "detect", NULL, "Detect NerdJack IP address"},
{'c', "convert", NULL, "convert output to volts"}, {'R', "range", "a,b",
{'H', "converthex", NULL, "convert output to hex"}, "Set range on NerdJack for channels 0-5,6-11 to either 5 or 10 (10,10)"},
{'m', "showmem", NULL, "output memory stats with data (NJ only)"}, {'o', "oneshot", NULL, "don't retry in case of errors"},
{'l', "lines", "num", "if set, output this many lines and quit"}, {'f', "forceretry", NULL, "retry no matter what happens"},
{'h', "help", NULL, "this help"}, {'c', "convert", NULL, "convert output to volts"},
{'v', "verbose", NULL, "be verbose"}, {'H', "converthex", NULL, "convert output to hex"},
{'V', "version", NULL, "show version number and exit"}, {'m', "showmem", NULL, "output memory stats with data (NJ only)"},
{'X', "examples",NULL, "show ethstream examples and exit"}, {'l', "lines", "num", "if set, output this many lines and quit"},
{0, NULL, NULL, NULL} {'h', "help", NULL, "this help"},
{'v', "verbose", NULL, "be verbose"},
{'V', "version", NULL, "show version number and exit"},
{'i', "info", NULL, "get info from device (NJ only)"},
{'X', "examples", NULL, "show ethstream examples and exit"},
{0, NULL, NULL, NULL}
}; };
int doStream (const char *address, uint8_t scanconfig, uint16_t scaninterval, int doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval,
int *channel_list, int channel_count, int convert, int *channel_list, int channel_count,
int maxlines); int *timer_mode_list, int timer_mode_count, int timer_divisor,
int nerdDoStream (const char *address, int *channel_list, int channel_count, int convert, int maxlines);
int precision, unsigned long period, int convert, int lines, int nerdDoStream(const char *address, int *channel_list, int channel_count,
int showmem); int precision, unsigned long period, int convert, int lines,
int data_callback (int channels, uint16_t * data, void *context); int showmem);
int data_callback(int channels, uint16_t * data, void *context);
int columns_left = 0; int columns_left = 0;
void void handle_sig(int sig)
handle_sig (int sig)
{ {
while (columns_left--) while (columns_left--) {
{ printf(" 0");
printf (" 0"); }
} fflush(stdout);
fflush (stdout); exit(0);
exit (0);
} }
int int main(int argc, char *argv[])
main (int argc, char *argv[])
{ {
int optind; int optind;
char *optarg, *endp; char *optarg, *endp;
char c; char c;
int tmp, i; int tmp, i;
FILE *help = stderr; FILE *help = stderr;
char *address = strdup (DEFAULT_HOST); char *address = strdup(DEFAULT_HOST);
double desired_rate = 8000.0; double desired_rate = 8000.0;
int lines = 0; int lines = 0;
double actual_rate; double actual_rate;
int oneshot = 0; int oneshot = 0;
int forceretry = 0; int forceretry = 0;
int convert = CONVERT_DEC; int convert = CONVERT_DEC;
int showmem = 0; int showmem = 0;
uint8_t scanconfig; int inform = 0;
uint16_t scaninterval; uint8_t scanconfig;
#if UE9_CHANNELS > NERDJACK_CHANNELS uint16_t scaninterval;
int channel_list[UE9_CHANNELS]; int timer_mode_list[UE9_TIMERS];
#else int timer_mode_count = 0;
int channel_list[NERDJACK_CHANNELS]; int timer_divisor = 1;
#endif int channel_list[MAX_CHANNELS];
int channel_count = 0; int channel_count = 0;
int nerdjack = 0; int nerdjack = 0;
int labjack = 0; int labjack = 0;
int detect = 0; int detect = 0;
int precision = 0; int precision = 0;
int addressSpecified = 0; int addressSpecified = 0;
int donerdjack = 0; int donerdjack = 0;
unsigned long period = NERDJACK_CLOCK_RATE / desired_rate; unsigned long period = NERDJACK_CLOCK_RATE / desired_rate;
/* Parse arguments */ /* Parse arguments */
opt_init (&optind); opt_init(&optind);
while ((c = opt_parse (argc, argv, &optind, &optarg, opt)) != 0) while ((c = opt_parse(argc, argv, &optind, &optarg, opt)) != 0) {
{ switch (c) {
switch (c) case 'a':
{ free(address);
case 'a': address = strdup(optarg);
free (address); addressSpecified = 1;
address = strdup (optarg); break;
addressSpecified = 1; case 'n':
break; channel_count = 0;
case 'n': tmp = strtol(optarg, &endp, 0);
channel_count = 0; if (*endp || tmp < 1 || tmp > MAX_CHANNELS) {
tmp = strtol (optarg, &endp, 0); info("bad number of channels: %s\n", optarg);
if (*endp || tmp < 1 || tmp > UE9_CHANNELS) goto printhelp;
{ }
info ("bad number of channels: %s\n", optarg); for (i = 0; i < tmp; i++)
goto printhelp; channel_list[channel_count++] = i;
} break;
for (i = 0; i < tmp; i++) case 'C':
channel_list[channel_count++] = i; channel_count = 0;
break; do {
case 'C': tmp = strtol(optarg, &endp, 0);
channel_count = 0; if (*endp != '\0' && *endp != ',') {
do info("bad channel number: %s\n",
{ optarg);
tmp = strtol (optarg, &endp, 0); goto printhelp;
if (*endp != '\0' && *endp != ',') }
{ //We do not want to overflow channel_list, so we need the check here
info ("bad channel number: %s\n", optarg); //The rest of the sanity checking can come later after we know
goto printhelp; //whether this is a
} //LabJack or a NerdJack
//We do not want to overflow channel_list, so we need the check here if (channel_count >= MAX_CHANNELS) {
//The rest of the sanity checking can come later after we know info("error: too many channels specified\n");
//whether this is a goto printhelp;
//LabJack or a NerdJack }
#if UE9_CHANNELS > NERDJACK_CHANNELS channel_list[channel_count++] = tmp;
if (channel_count >= UE9_CHANNELS) optarg = endp + 1;
{ }
#else while (*endp);
if (channel_count >= NERDJACK_CHANNELS) break;
{ case 't': /* labjack only */
#endif timer_mode_count = 0;
info ("error: too many channels specified\n"); do {
goto printhelp; tmp = strtol(optarg, &endp, 0);
if (*endp != '\0' && *endp != ',') {
info("bad timer mode: %s\n", optarg);
goto printhelp;
}
if (timer_mode_count >= UE9_TIMERS) {
info("error: too many timers specified\n");
goto printhelp;
}
timer_mode_list[timer_mode_count++] = tmp;
optarg = endp + 1;
}
while (*endp);
break;
case 'T': /* labjack only */
timer_divisor = strtod(optarg, &endp);
if (*endp || timer_divisor < 0 || timer_divisor > 255) {
info("bad timer divisor: %s\n", optarg);
goto printhelp;
}
break;
case 'r':
desired_rate = strtod(optarg, &endp);
if (*endp || desired_rate <= 0) {
info("bad rate: %s\n", optarg);
goto printhelp;
}
break;
case 'l':
lines = strtol(optarg, &endp, 0);
if (*endp || lines <= 0) {
info("bad number of lines: %s\n", optarg);
goto printhelp;
}
break;
case 'R':
tmp = strtol(optarg, &endp, 0);
if (*endp != ',') {
info("bad range number: %s\n", optarg);
goto printhelp;
}
if (tmp != 5 && tmp != 10) {
info("valid choices for range are 5 or 10\n");
goto printhelp;
}
if (tmp == 5)
precision = precision + 1;
optarg = endp + 1;
if (*endp == '\0') {
info("Range needs two numbers, one for channels 0-5 and another for 6-11\n");
goto printhelp;
}
tmp = strtol(optarg, &endp, 0);
if (*endp != '\0') {
info("Range needs only two numbers, one for channels 0-5 and another for 6-11\n");
goto printhelp;
}
if (tmp != 5 && tmp != 10) {
info("valid choices for range are 5 or 10\n");
goto printhelp;
}
if (tmp == 5)
precision = precision + 2;
break;
case 'N':
nerdjack++;
break;
case 'L':
labjack++;
break;
case 'd':
detect++;
break;
case 'o':
oneshot++;
break;
case 'f':
forceretry++;
break;
case 'c':
if (convert != 0) {
info("specify only one conversion type\n");
goto printhelp;
}
convert = CONVERT_VOLTS;
break;
case 'H':
if (convert != 0) {
info("specify only one conversion type\n");
goto printhelp;
}
convert = CONVERT_HEX;
break;
case 'm':
showmem++;
case 'v':
verb_count++;
break;
case 'X':
printf("%s", examplestring);
return 0;
break;
case 'V':
printf("etherstream " VERSION "\n");
printf("Written by Jim Paris <jim@jtan.com>\n");
printf("and Zachary Clifford <zacharyc@mit.edu>\n");
printf("This program comes with no warranty and is "
"provided under the GPLv2.\n");
return 0;
break;
case 'i':
inform++;
break;
case 'h':
help = stdout;
default:
printhelp:
fprintf(help, "Usage: %s [options]\n", *argv);
opt_help(opt, help);
fprintf(help, "Read data from the specified Labjack UE9"
" via Ethernet. See README for details.\n");
return (help == stdout) ? 0 : 1;
} }
channel_list[channel_count++] = tmp;
optarg = endp + 1;
}
while (*endp);
break;
case 'r':
desired_rate = strtod (optarg, &endp);
if (*endp || desired_rate <= 0)
{
info ("bad rate: %s\n", optarg);
goto printhelp;
}
break;
case 'l':
lines = strtol (optarg, &endp, 0);
if (*endp || lines <= 0)
{
info ("bad number of lines: %s\n", optarg);
goto printhelp;
}
break;
case 'R':
tmp = strtol (optarg, &endp, 0);
if (*endp != ',')
{
info ("bad range number: %s\n", optarg);
goto printhelp;
}
if(tmp != 5 && tmp != 10) {
info("valid choices for range are 5 or 10\n");
goto printhelp;
}
if(tmp == 5) precision = precision + 1;
optarg = endp + 1;
if (*endp == '\0') {
info("Range needs two numbers, one for channels 0-5 and another for 6-11\n");
goto printhelp;
}
tmp = strtol (optarg, &endp, 0);
if (*endp != '\0') {
info("Range needs only two numbers, one for channels 0-5 and another for 6-11\n");
goto printhelp;
}
if(tmp != 5 && tmp != 10) {
info("valid choices for range are 5 or 10\n");
goto printhelp;
}
if(tmp == 5) precision = precision + 2;
break;
case 'N':
nerdjack++;
break;
case 'L':
labjack++;
break;
case 'd':
detect++;
break;
case 'o':
oneshot++;
break;
case 'f':
forceretry++;
break;
case 'c':
if (convert != 0)
{
info ("specify only one conversion type\n");
goto printhelp;
}
convert = CONVERT_VOLTS;
break;
case 'H':
if (convert != 0)
{
info ("specify only one conversion type\n");
goto printhelp;
}
convert = CONVERT_HEX;
break;
case 'm':
showmem++;
case 'v':
verb_count++;
break;
case 'X':
printf("%s",examplestring);
return 0;
break;
case 'V':
printf ("etherstream " VERSION "\n");
printf ("Written by Jim Paris <jim@jtan.com>\n");
printf ("and Zachary Clifford <zacharyc@mit.edu>\n");
printf ("This program comes with no warranty and is "
"provided under the GPLv2.\n");
return 0;
break;
case 'h':
help = stdout;
default:
printhelp:
fprintf (help, "Usage: %s [options]\n", *argv);
opt_help (opt, help);
fprintf (help, "Read data from the specified Labjack UE9"
" via Ethernet. See README for details.\n");
return (help == stdout) ? 0 : 1;
}
}
if (detect && labjack) {
info("The LabJack does not support autodetection\n");
goto printhelp;
}
if (detect && !nerdjack) {
info("Only the NerdJack supports autodetection - assuming -N option\n");
nerdjack = 1;
}
if (detect && addressSpecified) {
info("Autodetection and specifying address are mutually exclusive\n");
goto printhelp;
}
if (nerdjack && labjack) {
info("Nerdjack and Labjack options are mutually exclusive\n");
goto printhelp;
}
donerdjack = nerdjack;
//First if no options were supplied try the Nerdjack
//The second time through, donerdjack will be true and this will not fire
if (!nerdjack && !labjack) {
info("No device specified...Defaulting to Nerdjack\n");
donerdjack = 1;
}
doneparse:
if (donerdjack)
{
if (channel_count > NERDJACK_CHANNELS)
{
info ("Too many channels for NerdJack\n");
goto printhelp;
}
for (i = 0; i < channel_count; i++)
{
if (channel_list[i] >= NERDJACK_CHANNELS)
{
info ("Channel is out of NerdJack range: %d\n",
channel_list[i]);
goto printhelp;
}
}
}
else
{
if (channel_count > UE9_CHANNELS)
{
info ("Too many channels for LabJack\n");
goto printhelp;
} }
for (i = 0; i < channel_count; i++)
{ if (detect && labjack) {
if (channel_list[i] >= UE9_CHANNELS) info("The LabJack does not support autodetection\n");
{ goto printhelp;
info ("Channel is out of LabJack range: %d\n", channel_list[i]);
goto printhelp;
}
} }
}
if (detect && !nerdjack) {
info("Only the NerdJack supports autodetection - assuming -N option\n");
nerdjack = 1;
if (optind < argc)
{
info ("error: too many arguments (%s)\n\n", argv[optind]);
goto printhelp;
}
if (forceretry && oneshot)
{
info ("forceretry and oneshot options are mutually exclusive\n");
goto printhelp;
}
/* Two channels if none specified */
if (channel_count == 0)
{
channel_list[channel_count++] = 0;
channel_list[channel_count++] = 1;
}
if (verb_count)
{
info ("Scanning channels:");
for (i = 0; i < channel_count; i++)
info (" AIN%d", channel_list[i]);
info ("\n");
}
/* Figure out actual rate. */
if (donerdjack)
{
if (nerdjack_choose_scan (desired_rate, &actual_rate, &period) < 0)
{
info ("error: can't achieve requested scan rate (%lf Hz)\n",
desired_rate);
} }
}
else if (detect && addressSpecified) {
{ info("Autodetection and specifying address are mutually exclusive\n");
if (ue9_choose_scan (desired_rate, &actual_rate, goto printhelp;
&scanconfig, &scaninterval) < 0)
{
info ("error: can't achieve requested scan rate (%lf Hz)\n",
desired_rate);
} }
}
if (nerdjack && labjack) {
info("Nerdjack and Labjack options are mutually exclusive\n");
goto printhelp;
}
if ((desired_rate != actual_rate) || verb_count) donerdjack = nerdjack;
{
info ("Actual scanrate is %lf Hz\n", actual_rate);
info ("Period is %ld\n", period);
}
//First if no options were supplied try the Nerdjack
//The second time through, donerdjack will be true and this will not fire
if (!nerdjack && !labjack) {
info("No device specified...Defaulting to Nerdjack\n");
donerdjack = 1;
}
if (verb_count && lines) doneparse:
{
info ("Stopping capture after %d lines\n", lines);
}
signal (SIGINT, handle_sig); if (inform) {
signal (SIGTERM, handle_sig); //We just want information from NerdJack
if (!detect) {
if (nerd_get_version(address) < 0) {
info("Could not find NerdJack at specified address\n");
} else {
return 0;
}
}
info("Autodetecting NerdJack address\n");
free(address);
if (nerdjack_detect(address) < 0) {
info("Error with autodetection\n");
goto printhelp;
} else {
info("Found NerdJack at address: %s\n", address);
if (nerd_get_version(address) < 0) {
info("Error getting NerdJack version\n");
goto printhelp;
}
return 0;
}
}
if (detect) if (donerdjack) {
{ if (channel_count > NERDJACK_CHANNELS) {
info ("Autodetecting NerdJack address\n"); info("Too many channels for NerdJack\n");
free (address); goto printhelp;
if (nerdjack_detect (address) < 0) }
{ for (i = 0; i < channel_count; i++) {
info ("Error with autodetection\n"); if (channel_list[i] >= NERDJACK_CHANNELS) {
goto printhelp; info("Channel is out of NerdJack range: %d\n",
channel_list[i]);
goto printhelp;
}
}
} else {
if (channel_count > UE9_MAX_CHANNEL_COUNT) {
info("Too many channels for LabJack\n");
goto printhelp;
}
for (i = 0; i < channel_count; i++) {
if (channel_list[i] > UE9_MAX_CHANNEL) {
info("Channel is out of LabJack range: %d\n",
channel_list[i]);
goto printhelp;
}
}
} }
else
{ /* Timer requires Labjack */
info ("Found NerdJack at address: %s\n", address); if (timer_mode_count && !labjack) {
info("Can't use timers on NerdJack\n");
goto printhelp;
} }
}
if (optind < argc) {
info("error: too many arguments (%s)\n\n", argv[optind]);
goto printhelp;
}
for (;;) if (forceretry && oneshot) {
{ info("forceretry and oneshot options are mutually exclusive\n");
int ret; goto printhelp;
if (donerdjack) }
{
ret =
nerdDoStream (address, channel_list, channel_count, precision,
period, convert, lines, showmem);
verb ("nerdDoStream returned %d\n", ret);
/* Two channels if none specified */
if (channel_count == 0) {
channel_list[channel_count++] = 0;
channel_list[channel_count++] = 1;
} }
else
{ if (verb_count) {
ret = doStream (address, scanconfig, scaninterval, info("Scanning channels:");
channel_list, channel_count, convert, lines); for (i = 0; i < channel_count; i++)
verb ("doStream returned %d\n", ret); info(" AIN%d", channel_list[i]);
info("\n");
} }
if (oneshot)
break; /* Figure out actual rate. */
if (donerdjack) {
if (ret == 0) if (nerdjack_choose_scan(desired_rate, &actual_rate, &period) <
break; 0) {
info("error: can't achieve requested scan rate (%lf Hz)\n", desired_rate);
//Neither options specified at command line and first time through. }
//Try LabJack } else {
if (ret == -ENOTCONN && donerdjack && !labjack && !nerdjack) if (ue9_choose_scan(desired_rate, &actual_rate,
{ &scanconfig, &scaninterval) < 0) {
info ("Could not connect NerdJack...Trying LabJack\n"); info("error: can't achieve requested scan rate (%lf Hz)\n", desired_rate);
donerdjack = 0; }
goto doneparse;
} }
//Neither option supplied, no address, and second time through. if ((desired_rate != actual_rate) || verb_count) {
//Try autodetection info("Actual scanrate is %lf Hz\n", actual_rate);
if (ret == -ENOTCONN && !donerdjack && !labjack && !nerdjack && !addressSpecified) { info("Period is %ld\n", period);
info ("Could not connect LabJack...Trying to autodetect Nerdjack\n");
detect = 1;
donerdjack = 1;
goto doneparse;
}
if (ret == -ENOTCONN && nerdjack && !detect && !addressSpecified) {
info ("Could not reach NerdJack...Trying to autodetect\n");
detect = 1;
goto doneparse;
}
if (ret == -ENOTCONN && !forceretry)
{
info ("Initial connection failed, giving up\n");
break;
} }
if (ret == -EAGAIN || ret == -ENOTCONN) if (verb_count && lines) {
{ info("Stopping capture after %d lines\n", lines);
/* Some transient error. Wait a tiny bit, then retry */
info ("Retrying in 5 secs.\n");
sleep (5);
} }
else
{ signal(SIGINT, handle_sig);
info ("Retrying now.\n"); signal(SIGTERM, handle_sig);
if (detect) {
info("Autodetecting NerdJack address\n");
free(address);
if (nerdjack_detect(address) < 0) {
info("Error with autodetection\n");
goto printhelp;
} else {
info("Found NerdJack at address: %s\n", address);
}
} }
}
debug ("Done loop\n"); for (;;) {
int ret;
if (donerdjack) {
ret =
nerdDoStream(address, channel_list, channel_count,
precision, period, convert, lines,
showmem);
verb("nerdDoStream returned %d\n", ret);
} else {
ret = doStream(address, scanconfig, scaninterval,
channel_list, channel_count,
timer_mode_list, timer_mode_count, timer_divisor,
convert, lines);
verb("doStream returned %d\n", ret);
}
if (oneshot)
break;
if (ret == 0)
break;
//Neither options specified at command line and first time through.
//Try LabJack
if (ret == -ENOTCONN && donerdjack && !labjack && !nerdjack) {
info("Could not connect NerdJack...Trying LabJack\n");
donerdjack = 0;
goto doneparse;
}
//Neither option supplied, no address, and second time through.
//Try autodetection
if (ret == -ENOTCONN && !donerdjack && !labjack && !nerdjack
&& !addressSpecified) {
info("Could not connect LabJack...Trying to autodetect Nerdjack\n");
detect = 1;
donerdjack = 1;
goto doneparse;
}
return 0; if (ret == -ENOTCONN && nerdjack && !detect
&& !addressSpecified) {
info("Could not reach NerdJack...Trying to autodetect\n");
detect = 1;
goto doneparse;
}
if (ret == -ENOTCONN && !forceretry) {
info("Initial connection failed, giving up\n");
break;
}
if (ret == -EAGAIN || ret == -ENOTCONN) {
/* Some transient error. Wait a tiny bit, then retry */
info("Retrying in 5 secs.\n");
sleep(5);
} else {
info("Retrying now.\n");
}
}
debug("Done loop\n");
return 0;
} }
int int
nerdDoStream (const char *address, int *channel_list, int channel_count, nerdDoStream(const char *address, int *channel_list, int channel_count,
int precision, unsigned long period, int convert, int lines, int precision, unsigned long period, int convert, int lines,
int showmem) int showmem)
{ {
int retval = -EAGAIN; int retval = -EAGAIN;
int fd_data; int fd_data;
static int first_call = 1; static int first_call = 1;
static int started = 0; static int started = 0;
static int wasreset = 0; static int wasreset = 0;
getPacket command; getPacket command;
static unsigned short currentcount = 0; static unsigned short currentcount = 0;
tryagain: tryagain:
//If this is the first time, set up acquisition //If this is the first time, set up acquisition
//Otherwise try to resume the previous one //Otherwise try to resume the previous one
if (started == 0) if (started == 0) {
{ if (nerd_generate_command
if (nerd_generate_command (&command, channel_list, channel_count, precision,
(&command, channel_list, channel_count, precision, period) < 0) period) < 0) {
{ info("Failed to create configuration command\n");
info ("Failed to create configuration command\n"); goto out;
goto out; }
}
if (nerd_send_command (address, "STOP", 4) < 0) if (nerd_send_command(address, "STOP", 4) < 0) {
{ if (first_call) {
if (first_call) { retval = -ENOTCONN;
retval = -ENOTCONN; if (verb_count)
if(verb_count) info("Failed to send STOP command\n"); info("Failed to send STOP command\n");
} else { } else {
info ("Failed to send STOP command\n"); info("Failed to send STOP command\n");
} }
goto out; goto out;
}
if (nerd_send_command(address, &command, sizeof(command)) < 0) {
info("Failed to send GET command\n");
goto out;
}
} else {
//If we had a transmission in progress, send a command to resume from there
char cmdbuf[10];
sprintf(cmdbuf, "SETC%05hd", currentcount);
retval = nerd_send_command(address, cmdbuf, strlen(cmdbuf));
if (retval == -4) {
info("NerdJack was reset\n");
//Assume we have not started yet, reset on this side.
//If this routine is retried, start over
printf("# NerdJack was reset here\n");
currentcount = 0;
started = 0;
wasreset = 1;
goto tryagain;
} else if (retval < 0) {
info("Failed to send SETC command\n");
goto out;
}
} }
if (nerd_send_command (address, &command, sizeof (command)) < 0) //The transmission has begun
{ started = 1;
info ("Failed to send GET command\n");
goto out; /* Open connection */
fd_data = nerd_open(address, NERDJACK_DATA_PORT);
if (fd_data < 0) {
info("Connect failed: %s:%d\n", address, NERDJACK_DATA_PORT);
goto out;
} }
} retval = nerd_data_stream
else (fd_data, channel_count, channel_list, precision, convert, lines,
{ showmem, &currentcount, period, wasreset);
//If we had a transmission in progress, send a command to resume from there wasreset = 0;
char cmdbuf[10]; if (retval == -3) {
sprintf (cmdbuf, "SETC%05hd", currentcount); retval = 0;
retval = nerd_send_command (address, cmdbuf, strlen (cmdbuf));
if (retval == -4)
{
info ("NerdJack was reset\n");
//Assume we have not started yet, reset on this side.
//If this routine is retried, start over
printf ("# NerdJack was reset here\n");
currentcount = 0;
started = 0;
wasreset = 1;
goto tryagain;
} }
else if (retval < 0) if (retval < 0) {
{ info("Failed to open data stream\n");
info ("Failed to send SETC command\n"); goto out1;
goto out;
} }
}
//The transmission has begun
started = 1;
/* Open connection */
fd_data = nerd_open (address, NERDJACK_DATA_PORT);
if (fd_data < 0)
{
info ("Connect failed: %s:%d\n", address, NERDJACK_DATA_PORT);
goto out;
}
retval = nerd_data_stream
(fd_data, channel_count, channel_list, precision, convert, lines,
showmem, &currentcount, period, wasreset);
wasreset = 0;
if (retval == -3)
{
retval = 0;
}
if (retval < 0)
{
info ("Failed to open data stream\n");
goto out1;
}
info ("Stream finished\n");
retval = 0;
out1:
nerd_close_conn (fd_data);
out:
//We've tried communicating, so this is not the first call anymore
first_call = 0;
return retval;
}
int info("Stream finished\n");
doStream (const char *address, uint8_t scanconfig, uint16_t scaninterval, retval = 0;
int *channel_list, int channel_count, int convert, int lines)
{ out1:
int retval = -EAGAIN; nerd_close_conn(fd_data);
int fd_cmd, fd_data; out:
int ret; //We've tried communicating, so this is not the first call anymore
static int first_call = 1; first_call = 0;
struct callbackInfo ci = { return retval;
.convert = convert,
.maxlines = lines,
};
/* Open command connection. If this fails, and this is the
first attempt, return a different error code so we give up. */
fd_cmd = ue9_open (address, UE9_COMMAND_PORT);
if (fd_cmd < 0)
{
info ("Connect failed: %s:%d\n", address, UE9_COMMAND_PORT);
if (first_call)
retval = -ENOTCONN;
goto out;
}
first_call = 0;
/* Make sure nothing is left over from a previous stream */
if (ue9_stream_stop (fd_cmd) == 0)
verb ("Stopped previous stream.\n");
ue9_buffer_flush (fd_cmd);
/* Open data connection */
fd_data = ue9_open (address, UE9_DATA_PORT);
if (fd_data < 0)
{
info ("Connect failed: %s:%d\n", address, UE9_DATA_PORT);
goto out1;
}
/* Get calibration */
if (ue9_get_calibration (fd_cmd, &ci.calib) < 0)
{
info ("Failed to get device calibration\n");
goto out2;
}
/* Set stream configuration */
if (ue9_streamconfig_simple (fd_cmd, channel_list, channel_count,
scanconfig, scaninterval,
UE9_BIPOLAR_GAIN1) < 0)
{
info ("Failed to set stream configuration\n");
goto out2;
}
/* Start stream */
if (ue9_stream_start (fd_cmd) < 0)
{
info ("Failed to start stream\n");
goto out2;
}
/* Stream data */
ret = ue9_stream_data (fd_data, channel_count, data_callback, (void *) &ci);
if (ret < 0)
{
info ("Data stream failed with error %d\n", ret);
goto out3;
}
info ("Stream finished\n");
retval = 0;
out3:
/* Stop stream and clean up */
ue9_stream_stop (fd_cmd);
ue9_buffer_flush (fd_cmd);
out2:
ue9_close (fd_data);
out1:
ue9_close (fd_cmd);
out:
return retval;
} }
int int
data_callback (int channels, uint16_t * data, void *context) doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval,
int *channel_list, int channel_count,
int *timer_mode_list, int timer_mode_count, int timer_divisor,
int convert, int lines)
{ {
int i; int retval = -EAGAIN;
struct callbackInfo *ci = (struct callbackInfo *) context; int fd_cmd, fd_data;
static int lines = 0; int ret;
static int first_call = 1;
columns_left = channels; struct callbackInfo ci = {
for (i = 0; i < channels; i++) .convert = convert,
{ .maxlines = lines,
switch (ci->convert) };
{
case CONVERT_VOLTS: /* Open command connection. If this fails, and this is the
if (printf first attempt, return a different error code so we give up. */
("%lf", fd_cmd = ue9_open(address, UE9_COMMAND_PORT);
ue9_binary_to_analog (&ci->calib, UE9_BIPOLAR_GAIN1, 12, if (fd_cmd < 0) {
data[i])) < 0) info("Connect failed: %s:%d\n", address, UE9_COMMAND_PORT);
goto bad; if (first_call)
break; retval = -ENOTCONN;
case CONVERT_HEX: goto out;
if (printf ("%04X", data[i]) < 0) }
goto bad; first_call = 0;
break;
default: /* Make sure nothing is left over from a previous stream */
case CONVERT_DEC: if (ue9_stream_stop(fd_cmd) == 0)
if (printf ("%d", data[i]) < 0) verb("Stopped previous stream.\n");
goto bad; ue9_buffer_flush(fd_cmd);
break;
/* Open data connection */
fd_data = ue9_open(address, UE9_DATA_PORT);
if (fd_data < 0) {
info("Connect failed: %s:%d\n", address, UE9_DATA_PORT);
goto out1;
}
/* Get calibration */
if (ue9_get_calibration(fd_cmd, &ci.calib) < 0) {
info("Failed to get device calibration\n");
goto out2;
}
/* Set timer configuration */
if (timer_mode_count &&
ue9_timer_config(fd_cmd, timer_mode_list, timer_mode_count,
timer_divisor) < 0) {
info("Failed to set timer configuration\n");
goto out2;
}
/* Set stream configuration */
if (ue9_streamconfig_simple(fd_cmd, channel_list, channel_count,
scanconfig, scaninterval,
UE9_BIPOLAR_GAIN1) < 0) {
info("Failed to set stream configuration\n");
goto out2;
}
/* Start stream */
if (ue9_stream_start(fd_cmd) < 0) {
info("Failed to start stream\n");
goto out2;
} }
columns_left--;
if (i < (channels - 1)) /* Stream data */
{ ret =
if (ci->convert != CONVERT_HEX && putchar (' ') < 0) ue9_stream_data(fd_data, channel_count, data_callback, (void *)&ci);
goto bad; if (ret < 0) {
info("Data stream failed with error %d\n", ret);
goto out3;
} }
else
{ info("Stream finished\n");
if (putchar ('\n') < 0) retval = 0;
goto bad;
lines++; out3:
if (ci->maxlines && lines >= ci->maxlines) /* Stop stream and clean up */
return -1; ue9_stream_stop(fd_cmd);
ue9_buffer_flush(fd_cmd);
out2:
ue9_close(fd_data);
out1:
ue9_close(fd_cmd);
out:
return retval;
}
int data_callback(int channels, uint16_t * data, void *context)
{
int i;
struct callbackInfo *ci = (struct callbackInfo *)context;
static int lines = 0;
columns_left = channels;
for (i = 0; i < channels; i++) {
switch (ci->convert) {
case CONVERT_VOLTS:
if (printf
("%lf",
ue9_binary_to_analog(&ci->calib, UE9_BIPOLAR_GAIN1,
12, data[i])) < 0)
goto bad;
break;
case CONVERT_HEX:
if (printf("%04X", data[i]) < 0)
goto bad;
break;
default:
case CONVERT_DEC:
if (printf("%d", data[i]) < 0)
goto bad;
break;
}
columns_left--;
if (i < (channels - 1)) {
if (ci->convert != CONVERT_HEX && putchar(' ') < 0)
goto bad;
} else {
if (putchar('\n') < 0)
goto bad;
lines++;
if (ci->maxlines && lines >= ci->maxlines)
return -1;
}
} }
}
return 0; return 0;
bad: bad:
info ("Output error (disk full?)\n"); info("Output error (disk full?)\n");
return -3; return -3;
} }
...@@ -6,15 +6,18 @@ char examplestring[] = "\n\ ...@@ -6,15 +6,18 @@ char examplestring[] = "\n\
For the most part, typing \"ethstream\" by itself will sample the first\n\ For the most part, typing \"ethstream\" by itself will sample the first\n\
two channels at 8 kHz on 10V range. Press CTRL-C to terminate sampling.\n\ two channels at 8 kHz on 10V range. Press CTRL-C to terminate sampling.\n\
\n\ \n\
If you want a voltage and current measurement on the first phase of NILM\n\ If you want current measurements on the first two phases of NILM\n\
with default sample rate of 8 kHz and 10V range:\n\ with default sample rate of 8 kHz and 10V range:\n\
\n\ \n\
ethstream -C 0,3\n\ ethstream -C 0,3\n\
\n\ \n\
The device is configured so that channels 0 through 2 are voltages for\n\ The device is configured so that channels 0 through 2 are currents for\n\
the three phases and channels 3-5 are for currents of the three phases\n\ the three phases and channels 3-5 are for voltages of the three phases.\n\
The current channels sample voltages that will depend on the DIP switch\n\
settings in the NILM box. The DIP switch positions allow you to convert\n\
ethstream's readings to true current readings.\n\
\n\ \n\
If you want only voltages at 16 kHz and 10V range:\n\ If you want only currents at 16 kHz and 10V range:\n\
\n\ \n\
ethstream -n 3 -r 16000\n\ ethstream -n 3 -r 16000\n\
\n\ \n\
...@@ -26,20 +29,20 @@ all 12 at once.\n\ ...@@ -26,20 +29,20 @@ all 12 at once.\n\
Ethstream will warn if you approach the limits of the NerdJack with the\n\ Ethstream will warn if you approach the limits of the NerdJack with the\n\
given sampled channels. Sampling outside the range of the NerdJack might\n\ given sampled channels. Sampling outside the range of the NerdJack might\n\
result in corrupt data or crashing of the device. There will be no\n\ result in corrupt data or crashing of the device. There will be no\n\
permanent damage to NILM or NerdJack.\n\ permanent damage to NILM or NerdJack, but be aware of the possibility of\n\
data corruption.\n\
\n\ \n\
If you need a higher accuracy but lower range measurement on the currents:\n\ If you need a higher accuracy but lower range measurement on the voltages:\n\
\n\ \n\
ethstream -R 5,10 -C 3,4,5\n\ ethstream -R 5,10 -C 3,4,5\n\
\n\ \n\
The two numbers to the R command set the range to either 5V or 10V. Above,\n\ The two numbers to the R command set the range to either 5V or 10V. Above,\n\
we are setting channels 0-5 to 5 V range and channels 6-11 to 10 V range.\n\ we are setting channels 0-5 to 5 V range and channels 6-11 to 10 V range.\n\
Channels 6-11 are unconnected, but they can have range set independently.\n\ Channels 6-11 are unconnected, but they can have range set independently.\n\
The values here depend on the NILM box settings to the current transducers.\n\
The value read is the voltage seen by the NerdJack.\n\
\n\ \n\
All of the above examples output a digital number from 0 to 65535 with\n\ All of the above examples output a digital number from 0 to 65535 with\n\
65535 representing the highest range (5V or 10V). If you want conversion\n\ 65535 representing the highest range (5V or 10V). 0 represents the most\n\
negative range (-5V or -10V). If you want conversion\n\
to volts for all six voltages and currents:\n\ to volts for all six voltages and currents:\n\
\n\ \n\
ethstream -c -C 0,3,1,4,2,5\n\ ethstream -c -C 0,3,1,4,2,5\n\
......
/*
* Labjack Tools
* Copyright (c) 2003-2007 Jim Paris <jim@jtan.com>
*
* This is free software; you can redistribute it and/or modify it and
* it is provided under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation; see COPYING.
*/
/* ljconfig: display/change comm/control processor configuration */
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "debug.h"
#include "ue9.h"
#include "ue9error.h"
#include "opt.h"
#include "version.h"
#define DEFAULT_HOST "192.168.1.209"
#define UE9_COMMAND_PORT 52360
struct options opt[] = {
{'a', "address", "string", "host/address of UE9 (192.168.1.209)"},
{'h', "help", NULL, "this help"},
{'v', "verbose", NULL, "be verbose"},
{'V', "version", NULL, "show version number and exit"},
{0, NULL, NULL, NULL}
};
int
main (int argc, char *argv[])
{
int optind;
char *optarg;
char c;
FILE *help = stderr;
char *address = strdup (DEFAULT_HOST);
int fd;
int ret;
/* Parse arguments */
opt_init (&optind);
while ((c = opt_parse (argc, argv, &optind, &optarg, opt)) != 0)
{
switch (c)
{
case 'a':
free (address);
address = strdup (optarg);
break;
case 'v':
verb_count++;
break;
case 'V':
printf ("ljconfig " VERSION "\n");
printf ("Written by Jim Paris <jim@jtan.com>\n");
printf ("This program comes with no warranty and is "
"provided under the GPLv2.\n");
return 0;
break;
case 'h':
help = stdout;
default:
printhelp:
fprintf (help, "Usage: %s [options]\n", *argv);
opt_help (opt, help);
fprintf (help, "Displays/changes Labjack UE9 config.\n");
return (help == stdout) ? 0 : 1;
}
}
if (optind < argc)
{
info ("Error: too many arguments (%s)\n\n", argv[optind]);
goto printhelp;
}
ret = 1;
/* Open */
fd = ue9_open (address, UE9_COMMAND_PORT);
if (fd < 0)
{
info ("Connect failed: %s:%d\n", address, UE9_COMMAND_PORT);
goto out0;
}
goto out1;
ret = 0;
out1:
/* Close */
ue9_close (fd);
out0:
return ret;
}
/*
* Labjack Tools
* Copyright (c) 2003-2007 Jim Paris <jim@jtan.com>
*
* This is free software; you can redistribute it and/or modify it and
* it is provided under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation; see COPYING.
*/
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "debug.h"
#include "ue9.h"
#include "compat.h"
int
main (int argc, char *argv[])
{
int fd_cmd;
struct ue9Calibration calib;
verb_count = 2;
fd_cmd = ue9_open ("192.168.1.209", 52360);
if (fd_cmd < 0)
{
fprintf (stderr, "ue9_open: %s\n", compat_strerror (errno));
return 1;
}
if (ue9_get_calibration (fd_cmd, &calib) < 0)
{
fprintf (stderr, "ue9_get_calibration: %s\n", compat_strerror (errno));
return 1;
}
printf ("double unipolarSlope[0] = %lf\n", calib.unipolarSlope[0]);
printf ("double unipolarSlope[1] = %lf\n", calib.unipolarSlope[1]);
printf ("double unipolarSlope[2] = %lf\n", calib.unipolarSlope[2]);
printf ("double unipolarSlope[3] = %lf\n", calib.unipolarSlope[3]);
printf ("double unipolarOffset[0] = %lf\n", calib.unipolarOffset[0]);
printf ("double unipolarOffset[1] = %lf\n", calib.unipolarOffset[1]);
printf ("double unipolarOffset[2] = %lf\n", calib.unipolarOffset[2]);
printf ("double unipolarOffset[3] = %lf\n", calib.unipolarOffset[3]);
printf ("double bipolarSlope = %lf\n", calib.bipolarSlope);
printf ("double bipolarOffset = %lf\n", calib.bipolarOffset);
printf ("double DACSlope[0] = %lf\n", calib.DACSlope[0]);
printf ("double DACSlope[1] = %lf\n", calib.DACSlope[1]);
printf ("double DACOffset[0] = %lf\n", calib.DACOffset[0]);
printf ("double DACOffset[1] = %lf\n", calib.DACOffset[1]);
printf ("double tempSlope = %lf\n", calib.tempSlope);
printf ("double tempSlopeLow = %lf\n", calib.tempSlopeLow);
printf ("double calTemp = %lf\n", calib.calTemp);
printf ("double Vref = %lf\n", calib.Vref);
printf ("double VrefDiv2 = %lf\n", calib.VrefDiv2);
printf ("double VsSlope = %lf\n", calib.VsSlope);
printf ("double hiResUnipolarSlope = %lf\n", calib.hiResUnipolarSlope);
printf ("double hiResUnipolarOffset = %lf\n", calib.hiResUnipolarOffset);
printf ("double hiResBipolarSlope = %lf\n", calib.hiResBipolarSlope);
printf ("double hiResBipolarOffset = %lf\n", calib.hiResBipolarOffset);
ue9_close (fd_cmd);
return 0;
}
...@@ -30,14 +30,13 @@ ...@@ -30,14 +30,13 @@
#define NERD_HEADER_SIZE 8 #define NERD_HEADER_SIZE 8
#define MAX_SOCKETS 32 #define MAX_SOCKETS 32
typedef struct __attribute__ ((__packed__)) typedef struct __attribute__ ((__packed__)) {
{ unsigned char headerone;
unsigned char headerone; unsigned char headertwo;
unsigned char headertwo; unsigned short packetNumber;
unsigned short packetNumber; unsigned short adcused;
unsigned short adcused; unsigned short packetsready;
unsigned short packetsready; signed short data[NERDJACK_NUM_SAMPLES];
signed short data[NERDJACK_NUM_SAMPLES];
} dataPacket; } dataPacket;
struct discovered_socket { struct discovered_socket {
...@@ -54,34 +53,33 @@ struct discover_t { ...@@ -54,34 +53,33 @@ struct discover_t {
/* Choose the best ScanConfig and ScanInterval parameters for the /* Choose the best ScanConfig and ScanInterval parameters for the
desired scanrate. Returns -1 if no valid config found */ desired scanrate. Returns -1 if no valid config found */
int int
nerdjack_choose_scan (double desired_rate, double *actual_rate, nerdjack_choose_scan(double desired_rate, double *actual_rate,
unsigned long *period) unsigned long *period)
{ {
//The ffffe is because of a silicon bug. The last bit is unusable in all //The ffffe is because of a silicon bug. The last bit is unusable in all
//devices so far. It is worked around on the chip, but giving it exactly //devices so far. It is worked around on the chip, but giving it exactly
//0xfffff would cause the workaround code to roll over. //0xfffff would cause the workaround code to roll over.
*period = floor ((double) NERDJACK_CLOCK_RATE / desired_rate); *period = floor((double)NERDJACK_CLOCK_RATE / desired_rate);
if (*period > 0x0ffffe) if (*period > 0x0ffffe) {
{ info("Cannot sample that slowly\n");
info ("Cannot sample that slowly\n"); *actual_rate = (double)NERDJACK_CLOCK_RATE / (double)0x0ffffe;
*actual_rate = (double) NERDJACK_CLOCK_RATE / (double) 0x0ffffe; *period = 0x0ffffe;
*period = 0x0ffffe; return -1;
return -1; }
} //Period holds the period register for the NerdJack, so it needs to be right
//Period holds the period register for the NerdJack, so it needs to be right *actual_rate = (double)NERDJACK_CLOCK_RATE / (double)*period;
*actual_rate = (double) NERDJACK_CLOCK_RATE / (double) *period; if (*actual_rate != desired_rate) {
if (*actual_rate != desired_rate) return -1;
{ }
return -1; return 0;
}
return 0;
} }
/** /**
* Create a discovered socket and add it to the socket list structure. * Create a discovered socket and add it to the socket list structure.
* All sockets in the structure should be created, bound, and ready for broadcasting * All sockets in the structure should be created, bound, and ready for broadcasting
*/ */
static int discovered_sock_create(struct discover_t *ds, uint32_t local_ip, uint32_t subnet_mask) static int discovered_sock_create(struct discover_t *ds, uint32_t local_ip,
uint32_t subnet_mask)
{ {
if (ds->sock_count >= MAX_SOCKETS) { if (ds->sock_count >= MAX_SOCKETS) {
return 0; return 0;
...@@ -95,14 +93,14 @@ static int discovered_sock_create(struct discover_t *ds, uint32_t local_ip, uint ...@@ -95,14 +93,14 @@ static int discovered_sock_create(struct discover_t *ds, uint32_t local_ip, uint
/* Allow broadcast. */ /* Allow broadcast. */
int sock_opt = 1; int sock_opt = 1;
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt, sizeof(sock_opt)); setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt,
sizeof(sock_opt));
/* Set nonblocking */ /* Set nonblocking */
if (soblock (sock, 0) < 0) if (soblock(sock, 0) < 0) {
{ verb("can't set nonblocking\n");
verb ("can't set nonblocking\n"); return 0;
return 0; }
}
/* Bind socket. */ /* Bind socket. */
struct sockaddr_in sock_addr; struct sockaddr_in sock_addr;
...@@ -123,13 +121,15 @@ static int discovered_sock_create(struct discover_t *ds, uint32_t local_ip, uint ...@@ -123,13 +121,15 @@ static int discovered_sock_create(struct discover_t *ds, uint32_t local_ip, uint
return 1; return 1;
} }
/** /**
* Enumerate all interfaces we can find and open sockets on each * Enumerate all interfaces we can find and open sockets on each
*/ */
#if defined(USE_IPHLPAPI) #if defined(USE_IPHLPAPI)
static void enumerate_interfaces(struct discover_t *ds) static void enumerate_interfaces(struct discover_t *ds)
{ {
PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof(IP_ADAPTER_INFO)); PIP_ADAPTER_INFO pAdapterInfo =
(IP_ADAPTER_INFO *) malloc(sizeof(IP_ADAPTER_INFO));
ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO); ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
DWORD Ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen); DWORD Ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
...@@ -138,7 +138,7 @@ static void enumerate_interfaces(struct discover_t *ds) ...@@ -138,7 +138,7 @@ static void enumerate_interfaces(struct discover_t *ds)
if (Ret != ERROR_BUFFER_OVERFLOW) { if (Ret != ERROR_BUFFER_OVERFLOW) {
return; return;
} }
pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen); pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen);
Ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen); Ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
if (Ret != NO_ERROR) { if (Ret != NO_ERROR) {
free(pAdapterInfo); free(pAdapterInfo);
...@@ -150,8 +150,10 @@ static void enumerate_interfaces(struct discover_t *ds) ...@@ -150,8 +150,10 @@ static void enumerate_interfaces(struct discover_t *ds)
while (pAdapter) { while (pAdapter) {
IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList;
while (pIPAddr) { while (pIPAddr) {
uint32_t local_ip = ntohl(inet_addr(pIPAddr->IpAddress.String)); uint32_t local_ip =
uint32_t mask = ntohl(inet_addr(pIPAddr->IpMask.String)); ntohl(inet_addr(pIPAddr->IpAddress.String));
uint32_t mask =
ntohl(inet_addr(pIPAddr->IpMask.String));
if (local_ip == 0) { if (local_ip == 0) {
pIPAddr = pIPAddr->Next; pIPAddr = pIPAddr->Next;
...@@ -169,9 +171,10 @@ static void enumerate_interfaces(struct discover_t *ds) ...@@ -169,9 +171,10 @@ static void enumerate_interfaces(struct discover_t *ds)
} }
#else #else
static void enumerate_interfaces(struct discover_t *ds) { static void enumerate_interfaces(struct discover_t *ds)
{
int fd = socket(AF_INET, SOCK_DGRAM, 0); int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1) { if (fd == -1) {
return; return;
} }
...@@ -187,8 +190,8 @@ static void enumerate_interfaces(struct discover_t *ds) { ...@@ -187,8 +190,8 @@ static void enumerate_interfaces(struct discover_t *ds) {
return; return;
} }
uint8_t *ptr = (uint8_t *)ifc.ifc_req; uint8_t *ptr = (uint8_t *) ifc.ifc_req;
uint8_t *end = (uint8_t *)&ifc.ifc_buf[ifc.ifc_len]; uint8_t *end = (uint8_t *) & ifc.ifc_buf[ifc.ifc_len];
while (ptr <= end) { while (ptr <= end) {
struct ifreq *ifr = (struct ifreq *)ptr; struct ifreq *ifr = (struct ifreq *)ptr;
...@@ -197,7 +200,8 @@ static void enumerate_interfaces(struct discover_t *ds) { ...@@ -197,7 +200,8 @@ static void enumerate_interfaces(struct discover_t *ds) {
if (ioctl(fd, SIOCGIFADDR, ifr) != 0) { if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
continue; continue;
} }
struct sockaddr_in *addr_in = (struct sockaddr_in *)&(ifr->ifr_addr); struct sockaddr_in *addr_in =
(struct sockaddr_in *)&(ifr->ifr_addr);
uint32_t local_ip = ntohl(addr_in->sin_addr.s_addr); uint32_t local_ip = ntohl(addr_in->sin_addr.s_addr);
if (local_ip == 0) { if (local_ip == 0) {
continue; continue;
...@@ -207,7 +211,8 @@ static void enumerate_interfaces(struct discover_t *ds) { ...@@ -207,7 +211,8 @@ static void enumerate_interfaces(struct discover_t *ds) {
continue; continue;
} }
struct sockaddr_in *mask_in = (struct sockaddr_in *)&(ifr->ifr_addr); struct sockaddr_in *mask_in =
(struct sockaddr_in *)&(ifr->ifr_addr);
uint32_t mask = ntohl(mask_in->sin_addr.s_addr); uint32_t mask = ntohl(mask_in->sin_addr.s_addr);
discovered_sock_create(ds, local_ip, mask); discovered_sock_create(ds, local_ip, mask);
...@@ -228,55 +233,51 @@ static void destroy_socks(struct discover_t *ds) ...@@ -228,55 +233,51 @@ static void destroy_socks(struct discover_t *ds)
free(ds); free(ds);
} }
/* Perform autodetection. Returns 0 on success, -1 on error /* Perform autodetection. Returns 0 on success, -1 on error
* Sets ipAddress to the detected address * Sets ipAddress to the detected address
*/ */
int int nerdjack_detect(char *ipAddress)
nerdjack_detect (char *ipAddress)
{ {
int32_t receivesock; int32_t receivesock;
struct sockaddr_in sa, receiveaddr, sFromAddr; struct sockaddr_in sa, receiveaddr, sFromAddr;
int buffer_length; int buffer_length;
char buffer[200]; char buffer[200];
char incomingData[10]; char incomingData[10];
unsigned int lFromLen; unsigned int lFromLen;
sprintf (buffer, "TEST"); sprintf(buffer, "TEST");
buffer_length = strlen (buffer) + 1; buffer_length = strlen(buffer) + 1;
net_init (); net_init();
receivesock = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP); receivesock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
/* Set nonblocking */ /* Set nonblocking */
if (soblock (receivesock, 0) < 0) if (soblock(receivesock, 0) < 0) {
{ verb("can't set nonblocking\n");
verb ("can't set nonblocking\n"); return -1;
return -1; }
}
if (-1 == receivesock) { /* if socket failed to initialize, exit */
if (-1 == receivesock) /* if socket failed to initialize, exit */ verb("Error Creating Socket\n");
{ return -1;
verb ("Error Creating Socket\n"); }
return -1; //Setup family for both sockets
} sa.sin_family = PF_INET;
receiveaddr.sin_family = PF_INET;
//Setup family for both sockets
sa.sin_family = PF_INET; //Setup ports to send on DATA and receive on RECEIVE
receiveaddr.sin_family = PF_INET; receiveaddr.sin_port = htons(NERDJACK_UDP_RECEIVE_PORT);
sa.sin_port = htons(NERDJACK_DATA_PORT);
//Setup ports to send on DATA and receive on RECEIVE
receiveaddr.sin_port = htons (NERDJACK_UDP_RECEIVE_PORT); //Receive from any IP address
sa.sin_port = htons (NERDJACK_DATA_PORT); receiveaddr.sin_addr.s_addr = INADDR_ANY;
//Receive from any IP address bind(receivesock, (struct sockaddr *)&receiveaddr,
receiveaddr.sin_addr.s_addr = INADDR_ANY; sizeof(struct sockaddr_in));
bind (receivesock, (struct sockaddr *) &receiveaddr, struct discover_t *ds =
sizeof (struct sockaddr_in)); (struct discover_t *)calloc(1, sizeof(struct discover_t));
struct discover_t *ds = (struct discover_t *)calloc(1, sizeof(struct discover_t));
if (!ds) { if (!ds) {
return -1; return -1;
} }
...@@ -298,406 +299,400 @@ nerdjack_detect (char *ipAddress) ...@@ -298,406 +299,400 @@ nerdjack_detect (char *ipAddress)
for (i = 0; i < ds->sock_count; i++) { for (i = 0; i < ds->sock_count; i++) {
struct discovered_socket *dss = &ds->socks[i]; struct discovered_socket *dss = &ds->socks[i];
uint32_t target_ip = dss->local_ip | ~dss->subnet_mask; uint32_t target_ip = dss->local_ip | ~dss->subnet_mask;
sa.sin_addr.s_addr = htonl(target_ip); sa.sin_addr.s_addr = htonl(target_ip);
sendto (dss->sock, buffer, buffer_length, 0, (struct sockaddr *) &sa, sendto(dss->sock, buffer, buffer_length, 0,
sizeof (struct sockaddr_in)); (struct sockaddr *)&sa, sizeof(struct sockaddr_in));
} }
destroy_socks(ds); destroy_socks(ds);
lFromLen = sizeof (sFromAddr); lFromLen = sizeof(sFromAddr);
if (0 > if (0 >
recvfrom_timeout (receivesock, incomingData, sizeof (incomingData), 0, recvfrom_timeout(receivesock, incomingData, sizeof(incomingData), 0,
(struct sockaddr *) &sFromAddr, &lFromLen, (struct sockaddr *)&sFromAddr, &lFromLen,
&(struct timeval) &(struct timeval) {
{ .tv_sec = NERDJACK_TIMEOUT})) {
.tv_sec = NERDJACK_TIMEOUT})) close(receivesock);
{ return -1;
close(receivesock); }
return -1;
}
ipAddress = malloc (INET_ADDRSTRLEN); ipAddress = malloc(INET_ADDRSTRLEN);
//It isn't ipv6 friendly, but inet_ntop isn't on Windows... //It isn't ipv6 friendly, but inet_ntop isn't on Windows...
strcpy (ipAddress, inet_ntoa (sFromAddr.sin_addr)); strcpy(ipAddress, inet_ntoa(sFromAddr.sin_addr));
close (receivesock); close(receivesock);
return 0; return 0;
}
/*
* Get the NerdJack version string and print it
*/
int nerd_get_version(const char *address)
{
int ret, fd_command;
char buf[200];
fd_command = nerd_open(address, NERDJACK_COMMAND_PORT);
if (fd_command < 0) {
info("Connect failed: %s:%d\n", address, NERDJACK_COMMAND_PORT);
return -2;
}
/* Send request */
ret = send_all_timeout(fd_command, "VERS", 4, 0, &(struct timeval) {
.tv_sec = NERDJACK_TIMEOUT});
if (ret < 0) {
verb("short send %d\n", (int)ret);
return -1;
}
ret = recv_all_timeout(fd_command, buf, 200, 0, &(struct timeval) {
.tv_sec = NERDJACK_TIMEOUT});
nerd_close_conn(fd_command);
if (ret < 0) {
verb("Error receiving command\n");
return -1;
}
//Slice off the "OK" from the string
buf[strlen(buf) - 2] = '\0';
printf("%s\n", buf);
return 0;
} }
/* Send the given command to address. The command should be something /* Send the given command to address. The command should be something
* of the specified length. This expects the NerdJack to reply with OK * of the specified length. This expects the NerdJack to reply with OK
* or NO * or NO
*/ */
int int nerd_send_command(const char *address, void *command, int length)
nerd_send_command (const char *address, void *command, int length)
{ {
int ret, fd_command; int ret, fd_command;
char buf[3]; char buf[3];
fd_command = nerd_open (address, NERDJACK_COMMAND_PORT); fd_command = nerd_open(address, NERDJACK_COMMAND_PORT);
if (fd_command < 0) if (fd_command < 0) {
{ info("Connect failed: %s:%d\n", address, NERDJACK_COMMAND_PORT);
info ("Connect failed: %s:%d\n", address, NERDJACK_COMMAND_PORT); return -2;
return -2; }
}
/* Send request */
/* Send request */ ret = send_all_timeout(fd_command, command, length, 0, &(struct timeval) {
ret = send_all_timeout (fd_command, command, length, 0, &(struct timeval) .tv_sec = NERDJACK_TIMEOUT});
{ if (ret < 0 || ret != length) {
.tv_sec = NERDJACK_TIMEOUT}); verb("short send %d\n", (int)ret);
if (ret < 0 || ret != length) return -1;
{ }
verb ("short send %d\n", (int) ret);
return -1;
}
ret = recv_all_timeout (fd_command, buf, 3, 0, &(struct timeval)
{
.tv_sec = NERDJACK_TIMEOUT});
nerd_close_conn (fd_command);
if (ret < 0 || ret != 3)
{
verb ("Error receiving OK for command\n");
return -1;
}
if (0 != strcmp ("OK", buf))
{
verb ("Did not receive OK. Received %s\n", buf);
return -4;
}
return 0;
}
ret = recv_all_timeout(fd_command, buf, 3, 0, &(struct timeval) {
.tv_sec = NERDJACK_TIMEOUT});
nerd_close_conn(fd_command);
if (ret < 0 || ret != 3) {
verb("Error receiving OK for command\n");
return -1;
}
if (0 != strcmp("OK", buf)) {
verb("Did not receive OK. Received %s\n", buf);
return -4;
}
return 0;
}
int int
nerd_data_stream (int data_fd, int numChannels, int *channel_list, nerd_data_stream(int data_fd, int numChannels, int *channel_list,
int precision, int convert, int lines, int showmem, int precision, int convert, int lines, int showmem,
unsigned short *currentcount, unsigned int period, unsigned short *currentcount, unsigned int period,
int wasreset) int wasreset)
{ {
//Variables that should persist across retries //Variables that should persist across retries
static dataPacket buf; static dataPacket buf;
static int linesleft = 0; static int linesleft = 0;
static int linesdumped = 0; static int linesdumped = 0;
//Variables essential to packet processing
signed short datapoint = 0;
int i;
int numChannelsSampled = channel_list[0] + 1;
//The number sampled will be the highest channel requested plus 1
//(i.e. channel 0 requested means 1 sampled)
for (i = 0; i < numChannels; i++) {
if (channel_list[i] + 1 > numChannelsSampled)
numChannelsSampled = channel_list[i] + 1;
}
double voltline[numChannels];
unsigned short dataline[numChannels];
unsigned short packetsready = 0;
unsigned short adcused = 0;
unsigned short tempshort = 0;
int charsread = 0;
//Variables essential to packet processing int numgroupsProcessed = 0;
signed short datapoint = 0; double volts;
int i;
int numChannelsSampled = channel_list[0] + 1; //The timeout should be the expected time plus 60 seconds
//This permits slower speeds to work properly
unsigned int expectedtimeout =
(period * NERDJACK_NUM_SAMPLES / NERDJACK_CLOCK_RATE) + 60;
//The number sampled will be the highest channel requested plus 1 //Check to see if we're trying to resume
//(i.e. channel 0 requested means 1 sampled) //Don't blow away linesleft in that case
for (i = 0; i < numChannels; i++) if (lines != 0 && linesleft == 0) {
{ linesleft = lines;
if (channel_list[i] + 1 > numChannelsSampled) }
numChannelsSampled = channel_list[i] + 1; //If there was a reset, we still need to dump a line because of faulty PDCA start
} if (wasreset) {
linesdumped = 0;
}
double voltline[numChannels]; //If this is the first time called, warn the user if we're too fast
if (linesdumped == 0) {
unsigned short dataline[numChannels]; if (period < (numChannelsSampled * 200 + 600)) {
info("You are sampling close to the limit of NerdJack\n");
unsigned short packetsready = 0; info("Sample fewer channels or sample slower\n");
unsigned short adcused = 0; }
unsigned short tempshort = 0; }
int charsread = 0; //Now destination structure array is set as well as numDuplicates.
int numgroupsProcessed = 0; int totalGroups = NERDJACK_NUM_SAMPLES / numChannelsSampled;
double volts;
//Loop forever to grab data
//The timeout should be the expected time plus 60 seconds while ((charsread =
//This permits slower speeds to work properly recv_all_timeout(data_fd, &buf, NERDJACK_PACKET_SIZE, 0,
unsigned int expectedtimeout = &(struct timeval) {
(period * NERDJACK_NUM_SAMPLES / NERDJACK_CLOCK_RATE) + 60; .tv_sec = expectedtimeout}))) {
//Check to see if we're trying to resume if (charsread != NERDJACK_PACKET_SIZE) {
//Don't blow away linesleft in that case //There was a problem getting data. Probably a closed
if (lines != 0 && linesleft == 0) //connection.
{ info("Packet timed out or was too short\n");
linesleft = lines; return -2;
}
//If there was a reset, we still need to dump a line because of faulty PDCA start
if (wasreset)
{
linesdumped = 0;
}
//If this is the first time called, warn the user if we're too fast
if (linesdumped == 0)
{
if (period < (numChannelsSampled * 200 + 600))
{
info ("You are sampling close to the limit of NerdJack\n");
info ("Sample fewer channels or sample slower\n");
}
}
//Now destination structure array is set as well as numDuplicates.
int totalGroups = NERDJACK_NUM_SAMPLES / numChannelsSampled;
//Loop forever to grab data
while ((charsread =
recv_all_timeout (data_fd, &buf, NERDJACK_PACKET_SIZE, 0,
&(struct timeval)
{
.tv_sec = expectedtimeout})))
{
if (charsread != NERDJACK_PACKET_SIZE)
{
//There was a problem getting data. Probably a closed
//connection.
info ("Packet timed out or was too short\n");
return -2;
}
//First check the header info
if (buf.headerone != 0xF0 || buf.headertwo != 0xAA)
{
info ("No Header info\n");
return -1;
}
//Check counter info to make sure not out of order
tempshort = ntohs (buf.packetNumber);
if (tempshort != *currentcount)
{
info ("Count wrong. Expected %hd but got %hd\n", *currentcount,
tempshort);
return -1;
}
//Increment number of packets received
*currentcount = *currentcount + 1;
adcused = ntohs (buf.adcused);
packetsready = ntohs (buf.packetsready);
numgroupsProcessed = 0;
if (showmem)
{
printf ("%hd %hd\n", adcused, packetsready);
continue;
}
//While there is still more data in the packet, process it
while (numgroupsProcessed < totalGroups)
{
//Poison the data structure
switch (convert)
{
case CONVERT_VOLTS:
memset (voltline, 0, numChannels * sizeof (double));
break;
default:
case CONVERT_HEX:
case CONVERT_DEC:
memset (dataline, 0, numChannels * sizeof (unsigned char));
}
//Read in each group
for (i = 0; i < numChannels; i++)
{
//Get the datapoint associated with the desired channel
datapoint =
ntohs (buf.
data[channel_list[i] +
numgroupsProcessed * numChannelsSampled]);
//Place it into the line
switch (convert)
{
case CONVERT_VOLTS:
if (channel_list[i] <= 5)
{
volts =
(double) (datapoint / 32767.0) *
((precision & 0x01) ? 5.0 : 10.0);
}
else
{
volts =
(double) (datapoint / 32767.0) *
((precision & 0x02) ? 5.0 : 10.0);
}
voltline[i] = volts;
break;
default:
case CONVERT_HEX:
case CONVERT_DEC:
dataline[i] = (unsigned short) (datapoint - INT16_MIN);
break;
} }
} //First check the header info
//We want to dump the first line because it's usually spurious if (buf.headerone != 0xF0 || buf.headertwo != 0xAA) {
if (linesdumped != 0) info("No Header info\n");
{ return -1;
//Now print the group
switch (convert)
{
case CONVERT_VOLTS:
for (i = 0; i < numChannels; i++)
{
if (printf ("%lf ", voltline[i]) < 0)
goto bad;
}
break;
case CONVERT_HEX:
for (i = 0; i < numChannels; i++)
{
if (printf ("%04hX", dataline[i]) < 0)
goto bad;
}
break;
default:
case CONVERT_DEC:
for (i = 0; i < numChannels; i++)
{
if (printf ("%hu ", dataline[i]) < 0)
goto bad;
}
break;
} }
if (printf ("\n") < 0) //Check counter info to make sure not out of order
goto bad; tempshort = ntohs(buf.packetNumber);
if (tempshort != *currentcount) {
//If we're counting lines, decrement them info("Count wrong. Expected %hd but got %hd\n",
if (lines != 0) *currentcount, tempshort);
{ return -1;
linesleft--;
if (linesleft == 0)
{
return 0;
}
} }
//Increment number of packets received
*currentcount = *currentcount + 1;
} adcused = ntohs(buf.adcused);
else packetsready = ntohs(buf.packetsready);
{ numgroupsProcessed = 0;
linesdumped = linesdumped + 1;
}
//We've processed this group, so advance the counter if (showmem) {
numgroupsProcessed++; printf("%hd %hd\n", adcused, packetsready);
continue;
}
//While there is still more data in the packet, process it
while (numgroupsProcessed < totalGroups) {
//Poison the data structure
switch (convert) {
case CONVERT_VOLTS:
memset(voltline, 0,
numChannels * sizeof(double));
break;
default:
case CONVERT_HEX:
case CONVERT_DEC:
memset(dataline, 0,
numChannels * sizeof(unsigned char));
}
//Read in each group
for (i = 0; i < numChannels; i++) {
//Get the datapoint associated with the desired channel
datapoint =
ntohs(buf.data[channel_list[i] +
numgroupsProcessed *
numChannelsSampled]);
//Place it into the line
switch (convert) {
case CONVERT_VOLTS:
if (channel_list[i] <= 5) {
volts =
(double)(datapoint /
32767.0) *
((precision & 0x01) ? 5.0 :
10.0);
} else {
volts =
(double)(datapoint /
32767.0) *
((precision & 0x02) ? 5.0 :
10.0);
}
voltline[i] = volts;
break;
default:
case CONVERT_HEX:
case CONVERT_DEC:
dataline[i] =
(unsigned short)(datapoint -
INT16_MIN);
break;
}
}
//We want to dump the first line because it's usually spurious
if (linesdumped != 0) {
//Now print the group
switch (convert) {
case CONVERT_VOLTS:
for (i = 0; i < numChannels; i++) {
if (printf("%lf ", voltline[i])
< 0)
goto bad;
}
break;
case CONVERT_HEX:
for (i = 0; i < numChannels; i++) {
if (printf("%04hX", dataline[i])
< 0)
goto bad;
}
break;
default:
case CONVERT_DEC:
for (i = 0; i < numChannels; i++) {
if (printf("%hu ", dataline[i])
< 0)
goto bad;
}
break;
}
if (printf("\n") < 0)
goto bad;
//If we're counting lines, decrement them
if (lines != 0) {
linesleft--;
if (linesleft == 0) {
return 0;
}
}
} else {
linesdumped = linesdumped + 1;
}
//We've processed this group, so advance the counter
numgroupsProcessed++;
}
} }
}
return 0; return 0;
bad: bad:
info ("Output error (disk full?)\n"); info("Output error (disk full?)\n");
return -3; return -3;
} }
/* Open a connection to the NerdJack */ /* Open a connection to the NerdJack */
int int nerd_open(const char *address, int port)
nerd_open (const char *address, int port)
{ {
struct hostent *he; struct hostent *he;
net_init (); net_init();
int32_t i32SocketFD = socket (PF_INET, SOCK_STREAM, 0); int32_t i32SocketFD = socket(PF_INET, SOCK_STREAM, 0);
if (-1 == i32SocketFD) if (-1 == i32SocketFD) {
{ verb("cannot create socket");
verb ("cannot create socket"); return -1;
return -1; }
}
/* Set nonblocking */
/* Set nonblocking */ if (soblock(i32SocketFD, 0) < 0) {
if (soblock (i32SocketFD, 0) < 0) verb("can't set nonblocking\n");
{ return -1;
verb ("can't set nonblocking\n"); }
return -1;
} struct sockaddr_in stSockAddr;
memset(&stSockAddr, 0, sizeof(stSockAddr));
struct sockaddr_in stSockAddr;
memset (&stSockAddr, 0, sizeof (stSockAddr)); stSockAddr.sin_family = AF_INET;
stSockAddr.sin_port = htons(port);
stSockAddr.sin_family = AF_INET;
stSockAddr.sin_port = htons (port); he = gethostbyname(address);
if (he == NULL) {
he = gethostbyname (address); verb("gethostbyname(\"%s\") failed\n", address);
if (he == NULL) return -1;
{ }
verb ("gethostbyname(\"%s\") failed\n", address); stSockAddr.sin_addr = *((struct in_addr *)he->h_addr);
return -1;
} debug("Resolved %s -> %s\n", address, inet_ntoa(stSockAddr.sin_addr));
stSockAddr.sin_addr = *((struct in_addr *) he->h_addr);
/* Connect */
debug ("Resolved %s -> %s\n", address, inet_ntoa (stSockAddr.sin_addr)); if (connect_timeout
(i32SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr),
/* Connect */ &(struct timeval) {
if (connect_timeout .tv_sec = 3}) < 0) {
(i32SocketFD, (struct sockaddr *) &stSockAddr, sizeof (stSockAddr), verb("connection to %s:%d failed: %s\n",
&(struct timeval) inet_ntoa(stSockAddr.sin_addr), port,
{ compat_strerror(errno));
.tv_sec = 3}) < 0) return -1;
{ }
verb ("connection to %s:%d failed: %s\n",
inet_ntoa (stSockAddr.sin_addr), port, compat_strerror (errno)); return i32SocketFD;
return -1;
}
return i32SocketFD;
} }
//Generate an appropriate sample initiation command //Generate an appropriate sample initiation command
int int
nerd_generate_command (getPacket * command, int *channel_list, nerd_generate_command(getPacket * command, int *channel_list,
int channel_count, int precision, unsigned long period) int channel_count, int precision, unsigned long period)
{ {
short channelbit = 0; short channelbit = 0;
int i; int i;
int highestchannel = 0; int highestchannel = 0;
for (i = 0; i < channel_count; i++) for (i = 0; i < channel_count; i++) {
{ if (channel_list[i] > highestchannel) {
if (channel_list[i] > highestchannel) highestchannel = channel_list[i];
{ }
highestchannel = channel_list[i]; //channelbit = channelbit | (0x1 << channel_list[i]);
} }
//channelbit = channelbit | (0x1 << channel_list[i]);
}
for (i = 0; i <= highestchannel; i++) for (i = 0; i <= highestchannel; i++) {
{ channelbit = channelbit | (0x01 << i);
channelbit = channelbit | (0x01 << i); }
}
command->word[0] = 'G'; command->word[0] = 'G';
command->word[1] = 'E'; command->word[1] = 'E';
command->word[2] = 'T'; command->word[2] = 'T';
command->word[3] = 'D'; command->word[3] = 'D';
command->channelbit = htons (channelbit); command->channelbit = htons(channelbit);
command->precision = precision; command->precision = precision;
command->period = htonl (period); command->period = htonl(period);
command->prescaler = 0; command->prescaler = 0;
return 0; return 0;
} }
int int nerd_close_conn(int data_fd)
nerd_close_conn (int data_fd)
{ {
shutdown (data_fd, 2); shutdown(data_fd, 2);
close (data_fd); close(data_fd);
return 0; return 0;
} }
...@@ -25,39 +25,41 @@ ...@@ -25,39 +25,41 @@
#define NERDJACK_NUM_SAMPLES 726 #define NERDJACK_NUM_SAMPLES 726
/* Packet structure used in message to start sampling on NerdJack */ /* Packet structure used in message to start sampling on NerdJack */
typedef struct __attribute__ ((__packed__)) typedef struct __attribute__ ((__packed__)) {
{ char word[4];
char word[4]; unsigned long period;
unsigned long period; unsigned short channelbit;
unsigned short channelbit; unsigned char precision;
unsigned char precision; unsigned char prescaler;
unsigned char prescaler;
} getPacket; } getPacket;
/* Open/close TCP/IP connection to the NerdJack */ /* Open/close TCP/IP connection to the NerdJack */
int nerd_open (const char *address, int port); int nerd_open(const char *address, int port);
int nerd_close_conn (int data_fd); int nerd_close_conn(int data_fd);
/* Generate the command word for the NerdJack */ /* Generate the command word for the NerdJack */
int nerd_generate_command (getPacket * command, int *channel_list, int nerd_generate_command(getPacket * command, int *channel_list,
int channel_count, int precision, int channel_count, int precision,
unsigned long period); unsigned long period);
/* Send given command to NerdJack */ /* Send given command to NerdJack */
int nerd_send_command (const char *address, void *command, int length); int nerd_send_command(const char *address, void *command, int length);
/* Get the version string from NerdJack */
int nerd_get_version(const char *address);
/* Stream data out of the NerdJack */ /* Stream data out of the NerdJack */
int nerd_data_stream (int data_fd, int numChannels, int *channel_list, int nerd_data_stream(int data_fd, int numChannels, int *channel_list,
int precision, int convert, int lines, int showmem, int precision, int convert, int lines, int showmem,
unsigned short *currentcount, unsigned int period, unsigned short *currentcount, unsigned int period,
int wasreset); int wasreset);
/* Detect the IP Address of the NerdJack and return in ipAddress */ /* Detect the IP Address of the NerdJack and return in ipAddress */
int nerdjack_detect (char *ipAddress); int nerdjack_detect(char *ipAddress);
/* Choose the best ScanConfig and ScanInterval parameters for the /* Choose the best ScanConfig and ScanInterval parameters for the
desired scanrate. Returns -1 if no valid config found */ desired scanrate. Returns -1 if no valid config found */
int nerdjack_choose_scan (double desired_rate, double *actual_rate, int nerdjack_choose_scan(double desired_rate, double *actual_rate,
unsigned long *period); unsigned long *period);
#endif #endif
...@@ -5,262 +5,244 @@ ...@@ -5,262 +5,244 @@
#include <stdio.h> #include <stdio.h>
/* Initialize networking */ /* Initialize networking */
void void net_init(void)
net_init (void)
{ {
#ifdef __WIN32__ #ifdef __WIN32__
WSADATA blah; WSADATA blah;
WSAStartup (0x0101, &blah); WSAStartup(0x0101, &blah);
#endif #endif
} }
/* Set socket blocking/nonblocking */ /* Set socket blocking/nonblocking */
int int soblock(int socket, int blocking)
soblock (int socket, int blocking)
{ {
#ifdef __WIN32__ #ifdef __WIN32__
unsigned long arg = blocking ? 0 : 1; unsigned long arg = blocking ? 0 : 1;
if (ioctlsocket (socket, FIONBIO, &arg) != 0) if (ioctlsocket(socket, FIONBIO, &arg) != 0)
return -1; return -1;
return 0; return 0;
#else #else
int sockopt; int sockopt;
/* Get flags */ /* Get flags */
sockopt = fcntl (socket, F_GETFL); sockopt = fcntl(socket, F_GETFL);
if (sockopt == -1) if (sockopt == -1) {
{ return -1;
return -1; }
}
/* Modify */
/* Modify */ if (blocking)
if (blocking) sockopt &= ~O_NONBLOCK;
sockopt &= ~O_NONBLOCK; else
else sockopt |= O_NONBLOCK;
sockopt |= O_NONBLOCK;
/* Set flags */
/* Set flags */ if (fcntl(socket, F_SETFL, sockopt) != 0)
if (fcntl (socket, F_SETFL, sockopt) != 0) return -1;
return -1;
return 0;
return 0;
#endif #endif
} }
/* Like connect(2), but with a timeout. Socket must be non-blocking. */ /* Like connect(2), but with a timeout. Socket must be non-blocking. */
int int
connect_timeout (int s, const struct sockaddr *serv_addr, socklen_t addrlen, connect_timeout(int s, const struct sockaddr *serv_addr, socklen_t addrlen,
struct timeval *timeout) struct timeval *timeout)
{ {
int ret; int ret;
fd_set writefds; fd_set writefds;
fd_set exceptfds; fd_set exceptfds;
int optval; int optval;
socklen_t optlen; socklen_t optlen;
/* Start connect */ /* Start connect */
ret = connect (s, serv_addr, addrlen); ret = connect(s, serv_addr, addrlen);
if (ret == 0) if (ret == 0) {
{ /* Success */
/* Success */ return 0;
return 0; }
}
/* Check for immediate failure */
/* Check for immediate failure */
#ifdef __WIN32__ #ifdef __WIN32__
errno = WSAGetLastError (); errno = WSAGetLastError();
if (ret < 0 && errno != WSAEWOULDBLOCK && errno != WSAEINVAL) if (ret < 0 && errno != WSAEWOULDBLOCK && errno != WSAEINVAL)
return -1; return -1;
#else #else
if (ret < 0 && errno != EINPROGRESS && errno != EALREADY) if (ret < 0 && errno != EINPROGRESS && errno != EALREADY)
return -1; return -1;
#endif #endif
/* In progress, wait for result. */ /* In progress, wait for result. */
FD_ZERO (&writefds); FD_ZERO(&writefds);
FD_SET (s, &writefds); FD_SET(s, &writefds);
FD_ZERO (&exceptfds); FD_ZERO(&exceptfds);
FD_SET (s, &exceptfds); FD_SET(s, &exceptfds);
ret = select (s + 1, NULL, &writefds, &exceptfds, timeout); ret = select(s + 1, NULL, &writefds, &exceptfds, timeout);
if (ret < 0) if (ret < 0) {
{ /* Error */
/* Error */ return -1;
return -1; }
} if (ret == 0) {
if (ret == 0) /* Timed out */
{ errno = ETIMEDOUT;
/* Timed out */ return -1;
errno = ETIMEDOUT; }
return -1;
} /* Check the socket state */
optlen = sizeof(optval);
/* Check the socket state */ if (getsockopt(s, SOL_SOCKET, SO_ERROR, (void *)&optval, &optlen) != 0)
optlen = sizeof (optval); return -1;
if (getsockopt (s, SOL_SOCKET, SO_ERROR, (void *) &optval, &optlen) != 0)
return -1; if (optval != 0) {
/* Connection failed. */
if (optval != 0) errno = optval;
{ return -1;
/* Connection failed. */ }
errno = optval;
return -1; /* On Windows, SO_ERROR sometimes shows no error but the connection
} still failed. Sigh. */
if (FD_ISSET(s, &exceptfds) || !FD_ISSET(s, &writefds)) {
/* On Windows, SO_ERROR sometimes shows no error but the connection errno = EIO;
still failed. Sigh. */ return -1;
if (FD_ISSET (s, &exceptfds) || !FD_ISSET (s, &writefds)) }
{
errno = EIO; /* Success */
return -1; return 0;
}
/* Success */
return 0;
} }
/* Like send(2), but with a timeout. Socket must be non-blocking. /* Like send(2), but with a timeout. Socket must be non-blocking.
The timeout only applies if no data at all is sent -- this function The timeout only applies if no data at all is sent -- this function
may still send less than requested. */ may still send less than requested. */
ssize_t ssize_t
send_timeout (int s, const void *buf, size_t len, int flags, send_timeout(int s, const void *buf, size_t len, int flags,
struct timeval * timeout) struct timeval * timeout)
{ {
fd_set writefds; fd_set writefds;
int ret; int ret;
FD_ZERO (&writefds); FD_ZERO(&writefds);
FD_SET (s, &writefds); FD_SET(s, &writefds);
ret = select (s + 1, NULL, &writefds, NULL, timeout); ret = select(s + 1, NULL, &writefds, NULL, timeout);
if (ret == 0) if (ret == 0) {
{ /* Timed out */
/* Timed out */ errno = ETIMEDOUT;
errno = ETIMEDOUT; return -1;
return -1; }
} if (ret != 1) {
if (ret != 1) /* Error */
{ return -1;
/* Error */ }
return -1;
} return send(s, buf, len, flags);
return send (s, buf, len, flags);
} }
/* Like recv(2), but with a timeout. Socket must be non-blocking. /* Like recv(2), but with a timeout. Socket must be non-blocking.
The timeout only applies if no data at all is received -- this The timeout only applies if no data at all is received -- this
function may still return less than requested. */ function may still return less than requested. */
ssize_t ssize_t
recv_timeout (int s, void *buf, size_t len, int flags, recv_timeout(int s, void *buf, size_t len, int flags, struct timeval * timeout)
struct timeval * timeout)
{ {
fd_set readfds; fd_set readfds;
int ret; int ret;
FD_ZERO (&readfds); FD_ZERO(&readfds);
FD_SET (s, &readfds); FD_SET(s, &readfds);
ret = select (s + 1, &readfds, NULL, NULL, timeout); ret = select(s + 1, &readfds, NULL, NULL, timeout);
if (ret == 0) if (ret == 0) {
{ /* Timed out */
/* Timed out */ errno = ETIMEDOUT;
errno = ETIMEDOUT; return -1;
return -1; }
} if (ret != 1) {
if (ret != 1) /* Error */
{ return -1;
/* Error */ }
return -1;
} return recv(s, buf, len, flags);
return recv (s, buf, len, flags);
} }
/* Like recvfrom(2), but with a timeout. Socket must be non-blocking. /* Like recvfrom(2), but with a timeout. Socket must be non-blocking.
The timeout only applies if no data at all is received -- this The timeout only applies if no data at all is received -- this
function may still return less than requested. */ function may still return less than requested. */
ssize_t ssize_t
recvfrom_timeout (int s, void *buf, size_t len, int flags, recvfrom_timeout(int s, void *buf, size_t len, int flags,
struct sockaddr * address, socklen_t * address_len, struct sockaddr * address, socklen_t * address_len,
struct timeval * timeout) struct timeval * timeout)
{ {
fd_set readfds; fd_set readfds;
int ret; int ret;
FD_ZERO (&readfds); FD_ZERO(&readfds);
FD_SET (s, &readfds); FD_SET(s, &readfds);
ret = select (s + 1, &readfds, NULL, NULL, timeout); ret = select(s + 1, &readfds, NULL, NULL, timeout);
if (ret == 0) if (ret == 0) {
{ /* Timed out */
/* Timed out */ errno = ETIMEDOUT;
errno = ETIMEDOUT; return -1;
return -1; }
} if (ret != 1) {
if (ret != 1) /* Error */
{ return -1;
/* Error */ }
return -1;
} return recvfrom(s, buf, len, flags, address, address_len);
return recvfrom (s, buf, len, flags, address, address_len);
} }
/* Like send_timeout, but retries (with the same timeout) in case of /* Like send_timeout, but retries (with the same timeout) in case of
partial transfers. This is a stronger attempt to send all partial transfers. This is a stronger attempt to send all
requested data. */ requested data. */
ssize_t ssize_t
send_all_timeout (int s, const void *buf, size_t len, int flags, send_all_timeout(int s, const void *buf, size_t len, int flags,
struct timeval * timeout) struct timeval * timeout)
{ {
struct timeval tv; struct timeval tv;
size_t left = len; size_t left = len;
ssize_t ret; ssize_t ret;
while (left > 0) while (left > 0) {
{ tv.tv_sec = timeout->tv_sec;
tv.tv_sec = timeout->tv_sec; tv.tv_usec = timeout->tv_usec;
tv.tv_usec = timeout->tv_usec; ret = send_timeout(s, buf, left, flags, &tv);
ret = send_timeout (s, buf, left, flags, &tv);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (ret == 0) if (ret == 0)
break; break;
left -= ret; left -= ret;
buf += ret; buf += ret;
} }
return len - left; return len - left;
} }
/* Like recv_timeout, but retries (with the same timeout) in case of /* Like recv_timeout, but retries (with the same timeout) in case of
partial transfers. This is a stronger attempt to recv all partial transfers. This is a stronger attempt to recv all
requested data. */ requested data. */
ssize_t ssize_t
recv_all_timeout (int s, void *buf, size_t len, int flags, recv_all_timeout(int s, void *buf, size_t len, int flags,
struct timeval * timeout) struct timeval * timeout)
{ {
struct timeval tv; struct timeval tv;
size_t left = len; size_t left = len;
ssize_t ret; ssize_t ret;
while (left > 0) while (left > 0) {
{ tv.tv_sec = timeout->tv_sec;
tv.tv_sec = timeout->tv_sec; tv.tv_usec = timeout->tv_usec;
tv.tv_usec = timeout->tv_usec; ret = recv_timeout(s, buf, left, flags, &tv);
ret = recv_timeout (s, buf, left, flags, &tv);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (ret == 0) if (ret == 0)
break; break;
left -= ret; left -= ret;
buf += ret; buf += ret;
} }
return len - left; return len - left;
} }
...@@ -27,28 +27,28 @@ ...@@ -27,28 +27,28 @@
#endif #endif
/* Initialize networking */ /* Initialize networking */
void net_init (void); void net_init(void);
/* Set socket blocking/nonblocking */ /* Set socket blocking/nonblocking */
int soblock (int socket, int blocking); int soblock(int socket, int blocking);
/* Like send(2), recv(2), connect(2), but with timeouts. /* Like send(2), recv(2), connect(2), but with timeouts.
Socket must be O_NONBLOCK. */ Socket must be O_NONBLOCK. */
int connect_timeout (int s, const struct sockaddr *serv_addr, int connect_timeout(int s, const struct sockaddr *serv_addr,
socklen_t addrlen, struct timeval *timeout); socklen_t addrlen, struct timeval *timeout);
ssize_t send_timeout (int s, const void *buf, size_t len, int flags, ssize_t send_timeout(int s, const void *buf, size_t len, int flags,
struct timeval *timeout); struct timeval *timeout);
ssize_t recv_timeout (int s, void *buf, size_t len, int flags, ssize_t recv_timeout(int s, void *buf, size_t len, int flags,
struct timeval *timeout); struct timeval *timeout);
ssize_t recvfrom_timeout (int s, void *buf, size_t len, int flags, ssize_t recvfrom_timeout(int s, void *buf, size_t len, int flags,
struct sockaddr *address, socklen_t * address_len, struct sockaddr *address, socklen_t * address_len,
struct timeval *timeout); struct timeval *timeout);
/* Like send_timeout and recv_timeout, but they retry (with the same timeout) /* Like send_timeout and recv_timeout, but they retry (with the same timeout)
in case of partial transfers, in order to try to transfer all data. */ in case of partial transfers, in order to try to transfer all data. */
ssize_t send_all_timeout (int s, const void *buf, size_t len, int flags, ssize_t send_all_timeout(int s, const void *buf, size_t len, int flags,
struct timeval *timeout); struct timeval *timeout);
ssize_t recv_all_timeout (int s, void *buf, size_t len, int flags, ssize_t recv_all_timeout(int s, void *buf, size_t len, int flags,
struct timeval *timeout); struct timeval *timeout);
#endif #endif
...@@ -11,98 +11,87 @@ ...@@ -11,98 +11,87 @@
#include <string.h> #include <string.h>
#include "opt.h" #include "opt.h"
void void opt_init(int *optind)
opt_init (int *optind)
{ {
*optind = 0; *optind = 0;
} }
char char
opt_parse (int argc, char **argv, int *optind, char **optarg, opt_parse(int argc, char **argv, int *optind, char **optarg,
struct options *opt) struct options *opt)
{ {
char c; char c;
int i; int i;
(*optind)++; (*optind)++;
if (*optind >= argc) if (*optind >= argc)
return 0; return 0;
if (argv[*optind][0] == '-' && if (argv[*optind][0] == '-' &&
argv[*optind][1] != '-' && argv[*optind][1] != 0) argv[*optind][1] != '-' && argv[*optind][1] != 0) {
{ /* Short option (or a bunch of 'em) */
/* Short option (or a bunch of 'em) */ /* Save this and shift others over */
/* Save this and shift others over */ c = argv[*optind][1];
c = argv[*optind][1]; for (i = 2; argv[*optind][i] != 0; i++)
for (i = 2; argv[*optind][i] != 0; i++) argv[*optind][i - 1] = argv[*optind][i];
argv[*optind][i - 1] = argv[*optind][i]; argv[*optind][i - 1] = 0;
argv[*optind][i - 1] = 0; if (argv[*optind][1] != 0)
if (argv[*optind][1] != 0) (*optind)--;
(*optind)--; /* Now find it */
/* Now find it */ for (i = 0; opt[i].shortopt != 0; i++)
for (i = 0; opt[i].shortopt != 0; i++) if (opt[i].shortopt == c)
if (opt[i].shortopt == c) break;
break; if (opt[i].shortopt == 0) {
if (opt[i].shortopt == 0) fprintf(stderr, "Error: unknown option '-%c'\n", c);
{ return '?';
fprintf (stderr, "Error: unknown option '-%c'\n", c); }
return '?'; if (opt[i].arg == NULL)
return c;
(*optind)++;
if (*optind >= argc || (argv[*optind][0] == '-' &&
argv[*optind][1] != 0)) {
fprintf(stderr, "Error: option '-%c' requires an "
"argument\n", c);
return '?';
}
(*optarg) = argv[*optind];
return c;
} else if (argv[*optind][0] == '-' &&
argv[*optind][1] == '-' && argv[*optind][2] != 0) {
/* Long option */
for (i = 0; (c = opt[i].shortopt) != 0; i++)
if (strcmp(opt[i].longopt, argv[*optind] + 2) == 0)
break;
if (opt[i].shortopt == 0) {
fprintf(stderr, "Error: unknown option '%s'\n",
argv[*optind]);
return '?';
}
if (opt[i].arg == NULL)
return c;
(*optind)++;
if (*optind >= argc || (argv[*optind][0] == '-' &&
argv[*optind][1] != 0)) {
fprintf(stderr, "Error: option '%s' requires an "
"argument\n", argv[*optind - 1]);
return '?';
}
(*optarg) = argv[*optind];
return c;
} else {
/* End of options */
return 0;
} }
if (opt[i].arg == NULL)
return c;
(*optind)++;
if (*optind >= argc || (argv[*optind][0] == '-' &&
argv[*optind][1] != 0))
{
fprintf (stderr, "Error: option '-%c' requires an "
"argument\n", c);
return '?';
}
(*optarg) = argv[*optind];
return c;
}
else if (argv[*optind][0] == '-' &&
argv[*optind][1] == '-' && argv[*optind][2] != 0)
{
/* Long option */
for (i = 0; (c = opt[i].shortopt) != 0; i++)
if (strcmp (opt[i].longopt, argv[*optind] + 2) == 0)
break;
if (opt[i].shortopt == 0)
{
fprintf (stderr, "Error: unknown option '%s'\n", argv[*optind]);
return '?';
}
if (opt[i].arg == NULL)
return c;
(*optind)++;
if (*optind >= argc || (argv[*optind][0] == '-' &&
argv[*optind][1] != 0))
{
fprintf (stderr, "Error: option '%s' requires an "
"argument\n", argv[*optind - 1]);
return '?';
}
(*optarg) = argv[*optind];
return c;
}
else
{
/* End of options */
return 0;
}
} }
void void opt_help(struct options *opt, FILE * out)
opt_help (struct options *opt, FILE * out)
{ {
int i; int i;
int printed; int printed;
for (i = 0; opt[i].shortopt != 0; i++) for (i = 0; opt[i].shortopt != 0; i++) {
{ fprintf(out, " -%c, --%s%n", opt[i].shortopt,
fprintf (out, " -%c, --%s%n", opt[i].shortopt, opt[i].longopt, &printed);
opt[i].longopt, &printed); fprintf(out, " %-*s%s\n", 30 - printed,
fprintf (out, " %-*s%s\n", 30 - printed, opt[i].arg ? opt[i].arg : "", opt[i].help);
opt[i].arg ? opt[i].arg : "", opt[i].help); }
}
} }
...@@ -11,19 +11,18 @@ ...@@ -11,19 +11,18 @@
#include <stdlib.h> #include <stdlib.h>
struct options struct options {
{ char shortopt;
char shortopt; char *longopt;
char *longopt; char *arg;
char *arg; char *help;
char *help;
}; };
void opt_init (int *optind); void opt_init(int *optind);
char opt_parse (int argc, char **argv, int *optind, char **optarg, char opt_parse(int argc, char **argv, int *optind, char **optarg,
struct options *opt); struct options *opt);
void opt_help (struct options *opt, FILE * out); void opt_help(struct options *opt, FILE * out);
#endif #endif
...@@ -28,722 +28,695 @@ ...@@ -28,722 +28,695 @@
#define UE9_TIMEOUT 5 /* Timeout for connect/send/recv, in seconds */ #define UE9_TIMEOUT 5 /* Timeout for connect/send/recv, in seconds */
/* Fill checksums in data buffers, with "normal" checksum format */ /* Fill checksums in data buffers, with "normal" checksum format */
void void ue9_checksum_normal(uint8_t * buffer, size_t len)
ue9_checksum_normal (uint8_t * buffer, size_t len)
{ {
uint16_t sum = 0; uint16_t sum = 0;
if (len < 1) if (len < 1) {
{ fprintf(stderr, "ue9_checksum_normal: len too short\n");
fprintf (stderr, "ue9_checksum_normal: len too short\n"); exit(1);
exit (1); }
}
while (--len >= 1)
while (--len >= 1) sum += (uint16_t) buffer[len];
sum += (uint16_t) buffer[len]; sum = (sum / 256) + (sum % 256);
sum = (sum / 256) + (sum % 256); sum = (sum / 256) + (sum % 256);
sum = (sum / 256) + (sum % 256); buffer[0] = (uint8_t) sum;
buffer[0] = (uint8_t) sum;
} }
/* Fill checksums in data buffers, with "extended" checksum format */ /* Fill checksums in data buffers, with "extended" checksum format */
void void ue9_checksum_extended(uint8_t * buffer, size_t len)
ue9_checksum_extended (uint8_t * buffer, size_t len)
{ {
uint16_t sum = 0; uint16_t sum = 0;
if (len < 6) if (len < 6) {
{ fprintf(stderr, "ue9_checksum_extended: len too short\n");
fprintf (stderr, "ue9_checksum_extended: len too short\n"); exit(1);
exit (1); }
}
/* 16-bit extended checksum */
/* 16-bit extended checksum */ while (--len >= 6)
while (--len >= 6) sum += (uint16_t) buffer[len];
sum += (uint16_t) buffer[len]; buffer[4] = (uint8_t) (sum & 0xff);
buffer[4] = (uint8_t) (sum & 0xff); buffer[5] = (uint8_t) (sum >> 8);
buffer[5] = (uint8_t) (sum >> 8);
/* 8-bit normal checksum over first 6 bytes */
/* 8-bit normal checksum over first 6 bytes */ ue9_checksum_normal(buffer, 6);
ue9_checksum_normal (buffer, 6);
} }
/* Verify checksums in data buffers, with "normal" checksum format. */ /* Verify checksums in data buffers, with "normal" checksum format. */
int int ue9_verify_normal(uint8_t * buffer, size_t len)
ue9_verify_normal (uint8_t * buffer, size_t len)
{ {
uint8_t saved, new; uint8_t saved, new;
if (len < 1) if (len < 1) {
{ fprintf(stderr, "ue9_verify_normal: len too short\n");
fprintf (stderr, "ue9_verify_normal: len too short\n"); exit(1);
exit (1); }
}
saved = buffer[0];
saved = buffer[0]; ue9_checksum_normal(buffer, len);
ue9_checksum_normal (buffer, len); new = buffer[0];
new = buffer[0]; buffer[0] = saved;
buffer[0] = saved;
if (new != saved) {
if (new != saved) verb("got %02x, expected %02x\n", saved, new);
{ return 0;
verb ("got %02x, expected %02x\n", saved, new); }
return 0;
} return 1;
return 1;
} }
/* Verify checksums in data buffers, with "extended" checksum format. */ /* Verify checksums in data buffers, with "extended" checksum format. */
int int ue9_verify_extended(uint8_t * buffer, size_t len)
ue9_verify_extended (uint8_t * buffer, size_t len)
{ {
uint8_t saved[3], new[3]; uint8_t saved[3], new[3];
if (len < 6) if (len < 6) {
{ fprintf(stderr, "ue9_verify_extended: len too short\n");
fprintf (stderr, "ue9_verify_extended: len too short\n"); exit(1);
exit (1); }
}
saved[0] = buffer[0];
saved[0] = buffer[0]; saved[1] = buffer[4];
saved[1] = buffer[4]; saved[2] = buffer[5];
saved[2] = buffer[5]; ue9_checksum_extended(buffer, len);
ue9_checksum_extended (buffer, len); new[0] = buffer[0];
new[0] = buffer[0]; new[1] = buffer[4];
new[1] = buffer[4]; new[2] = buffer[5];
new[2] = buffer[5]; buffer[0] = saved[0];
buffer[0] = saved[0]; buffer[4] = saved[1];
buffer[4] = saved[1]; buffer[5] = saved[2];
buffer[5] = saved[2];
if (saved[0] != new[0] || saved[1] != new[1] || saved[2] != new[2]) {
if (saved[0] != new[0] || saved[1] != new[1] || saved[2] != new[2]) verb("got %02x %02x %02x, expected %02x %02x %02x\n",
{ saved[0], saved[1], saved[2], new[0], new[1], new[2]);
verb ("got %02x %02x %02x, expected %02x %02x %02x\n", return 0;
saved[0], saved[1], saved[2], new[0], new[1], new[2]); }
return 0;
} return 1;
return 1;
} }
/* Data conversion. If calib is NULL, use uncalibrated conversions. */ /* Data conversion. If calib is NULL, use uncalibrated conversions. */
double double
ue9_binary_to_analog (struct ue9Calibration *calib, ue9_binary_to_analog(struct ue9Calibration *calib,
uint8_t gain, uint8_t resolution, uint16_t data) uint8_t gain, uint8_t resolution, uint16_t data)
{ {
double slope = 0, offset; double slope = 0, offset;
if (calib == NULL) if (calib == NULL) {
{ double uncal[9] = { 5.08, 2.54, 1.27, 0.63, 0, 0, 0, 0, 10.25 };
double uncal[9] = { 5.08, 2.54, 1.27, 0.63, 0, 0, 0, 0, 10.25 }; if (gain >= ARRAY_SIZE(uncal) || uncal[gain] == 0) {
if (gain >= ARRAY_SIZE (uncal) || uncal[gain] == 0) fprintf(stderr, "ue9_binary_to_analog: bad gain\n");
{ exit(1);
fprintf (stderr, "ue9_binary_to_analog: bad gain\n"); }
exit (1); return data * uncal[gain] / 65536.0;
} }
return data * uncal[gain] / 65536.0;
} if (resolution < 18) {
if (gain <= 3) {
if (resolution < 18) slope = calib->unipolarSlope[gain];
{ offset = calib->unipolarOffset[gain];
if (gain <= 3) } else if (gain == 8) {
{ slope = calib->bipolarSlope;
slope = calib->unipolarSlope[gain]; offset = calib->bipolarOffset;
offset = calib->unipolarOffset[gain]; }
} } else {
else if (gain == 8) if (gain == 0) {
{ slope = calib->hiResUnipolarSlope;
slope = calib->bipolarSlope; offset = calib->hiResUnipolarOffset;
offset = calib->bipolarOffset; } else if (gain == 8) {
} slope = calib->hiResBipolarSlope;
} offset = calib->hiResBipolarOffset;
else }
{ }
if (gain == 0)
{ if (slope == 0) {
slope = calib->hiResUnipolarSlope; fprintf(stderr, "ue9_binary_to_analog: bad gain\n");
offset = calib->hiResUnipolarOffset; exit(1);
} }
else if (gain == 8)
{ return data * slope + offset;
slope = calib->hiResBipolarSlope;
offset = calib->hiResBipolarOffset;
}
}
if (slope == 0)
{
fprintf (stderr, "ue9_binary_to_analog: bad gain\n");
exit (1);
}
return data * slope + offset;
} }
/* Execute a command on the UE9. Returns -1 on error. Fills the /* Execute a command on the UE9. Returns -1 on error. Fills the
checksums on the outgoing packets, and verifies them on the checksums on the outgoing packets, and verifies them on the
incoming packets. Data in "out" is transmitted, data in "in" is incoming packets. Data in "out" is transmitted, data in "in" is
received. */ received. */
int int ue9_command(int fd, uint8_t * out, uint8_t * in, int inlen)
ue9_command (int fd, uint8_t * out, uint8_t * in, int inlen)
{ {
int extended = 0, outlen; int extended = 0, outlen;
uint8_t saved_1, saved_3; uint8_t saved_1, saved_3;
ssize_t ret; ssize_t ret;
if ((out[1] & 0x78) == 0x78) if ((out[1] & 0x78) == 0x78)
extended = 1; extended = 1;
/* Figure out length of data payload, and fill checksums. */ /* Figure out length of data payload, and fill checksums. */
if (extended) if (extended) {
{ outlen = 6 + (out[2]) * 2;
outlen = 6 + (out[2]) * 2; ue9_checksum_extended(out, outlen);
ue9_checksum_extended (out, outlen); } else {
} outlen = 2 + (out[1] & 7) * 2;
else ue9_checksum_normal(out, outlen);
{ }
outlen = 2 + (out[1] & 7) * 2;
ue9_checksum_normal (out, outlen); /* Send request */
} ret = send_all_timeout(fd, out, outlen, 0, &(struct timeval) {
.tv_sec = UE9_TIMEOUT});
/* Send request */ if (ret < 0 || ret != outlen) {
ret = send_all_timeout (fd, out, outlen, 0, &(struct timeval) verb("short send %d\n", (int)ret);
{ return -1;
.tv_sec = UE9_TIMEOUT}); }
if (ret < 0 || ret != outlen)
{ /* Save a few bytes that we'll want to compare against later,
verb ("short send %d\n", (int) ret); in case the caller passed the same buffer twice. */
return -1; saved_1 = out[1];
} if (extended)
saved_3 = out[3];
/* Save a few bytes that we'll want to compare against later,
in case the caller passed the same buffer twice. */ /* Receive result */
saved_1 = out[1]; ret = recv_all_timeout(fd, in, inlen, 0, &(struct timeval) {
if (extended) .tv_sec = UE9_TIMEOUT});
saved_3 = out[3]; if (ret < 0 || ret != inlen) {
verb("short recv %d\n", (int)ret);
/* Receive result */ return -1;
ret = recv_all_timeout (fd, in, inlen, 0, &(struct timeval) }
{
.tv_sec = UE9_TIMEOUT});
if (ret < 0 || ret != inlen)
{
verb ("short recv %d\n", (int) ret);
return -1;
}
/* Verify it */
if ((in[1] & 0xF8) != (saved_1 & 0xF8))
verb ("returned command doesn't match\n");
else if (extended && (in[3] != saved_3))
verb ("extended command doesn't match\n");
else if (extended && (inlen != (6 + (in[2]) * 2)))
verb ("returned extended data is the wrong len\n");
else if (!extended && (inlen != (2 + (in[1] & 7) * 2)))
verb ("returned data is the wrong len\n");
else if (extended && !ue9_verify_extended (in, inlen))
verb ("extended checksum is invalid\n");
else if (!ue9_verify_normal (in, extended ? 6 : inlen))
verb ("normal checksum is invalid\n");
else
return 0; /* looks good */
return -1;
}
/* Verify it */
if ((in[1] & 0xF8) != (saved_1 & 0xF8))
verb("returned command doesn't match\n");
else if (extended && (in[3] != saved_3))
verb("extended command doesn't match\n");
else if (extended && (inlen != (6 + (in[2]) * 2)))
verb("returned extended data is the wrong len\n");
else if (!extended && (inlen != (2 + (in[1] & 7) * 2)))
verb("returned data is the wrong len\n");
else if (extended && !ue9_verify_extended(in, inlen))
verb("extended checksum is invalid\n");
else if (!ue9_verify_normal(in, extended ? 6 : inlen))
verb("normal checksum is invalid\n");
else
return 0; /* looks good */
return -1;
}
/* Read a memory block from the device. Returns -1 on error. */ /* Read a memory block from the device. Returns -1 on error. */
int int ue9_memory_read(int fd, int blocknum, uint8_t * buffer, int len)
ue9_memory_read (int fd, int blocknum, uint8_t * buffer, int len)
{ {
uint8_t sendbuf[8], recvbuf[136]; uint8_t sendbuf[8], recvbuf[136];
if (len != 128) if (len != 128) {
{ fprintf(stderr, "ue9_memory_read: buffer length must be 128\n");
fprintf (stderr, "ue9_memory_read: buffer length must be 128\n"); exit(1);
exit (1); }
}
/* Request memory block */
/* Request memory block */ sendbuf[1] = 0xf8;
sendbuf[1] = 0xf8; sendbuf[2] = 0x01;
sendbuf[2] = 0x01; sendbuf[3] = 0x2a;
sendbuf[3] = 0x2a; sendbuf[6] = 0x00;
sendbuf[6] = 0x00; sendbuf[7] = blocknum;
sendbuf[7] = blocknum;
if (ue9_command(fd, sendbuf, recvbuf, sizeof(recvbuf)) < 0) {
if (ue9_command (fd, sendbuf, recvbuf, sizeof (recvbuf)) < 0) verb("command failed\n");
{ return -1;
verb ("command failed\n"); }
return -1;
} /* Got it */
memcpy(buffer, recvbuf + 8, len);
/* Got it */
memcpy (buffer, recvbuf + 8, len); return 0;
return 0;
} }
/* Convert 64-bit fixed point to double type */ /* Convert 64-bit fixed point to double type */
double double ue9_fp64_to_double(uint8_t * data)
ue9_fp64_to_double (uint8_t * data)
{ {
int32_t a; int32_t a;
uint32_t b; uint32_t b;
a = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4]; a = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4];
b = (data[3] << 24) | (data[2] << 16) | (data[1] << 8) | data[0]; b = (data[3] << 24) | (data[2] << 16) | (data[1] << 8) | data[0];
return (double) a + (double) b / (double) 4294967296.0L; return (double)a + (double)b / (double)4294967296.0L;
} }
/* Retrieve calibration data from the device. Returns -1 on error. */ /* Retrieve calibration data from the device. Returns -1 on error. */
int int ue9_get_calibration(int fd, struct ue9Calibration *calib)
ue9_get_calibration (int fd, struct ue9Calibration *calib)
{ {
uint8_t buf[128]; uint8_t buf[128];
/* Block 0 */ /* Block 0 */
if (ue9_memory_read (fd, 0, buf, 128) < 0) if (ue9_memory_read(fd, 0, buf, 128) < 0)
return -1; return -1;
calib->unipolarSlope[0] = ue9_fp64_to_double (buf + 0); calib->unipolarSlope[0] = ue9_fp64_to_double(buf + 0);
calib->unipolarOffset[0] = ue9_fp64_to_double (buf + 8); calib->unipolarOffset[0] = ue9_fp64_to_double(buf + 8);
calib->unipolarSlope[1] = ue9_fp64_to_double (buf + 16); calib->unipolarSlope[1] = ue9_fp64_to_double(buf + 16);
calib->unipolarOffset[1] = ue9_fp64_to_double (buf + 24); calib->unipolarOffset[1] = ue9_fp64_to_double(buf + 24);
calib->unipolarSlope[2] = ue9_fp64_to_double (buf + 32); calib->unipolarSlope[2] = ue9_fp64_to_double(buf + 32);
calib->unipolarOffset[2] = ue9_fp64_to_double (buf + 40); calib->unipolarOffset[2] = ue9_fp64_to_double(buf + 40);
calib->unipolarSlope[3] = ue9_fp64_to_double (buf + 48); calib->unipolarSlope[3] = ue9_fp64_to_double(buf + 48);
calib->unipolarOffset[3] = ue9_fp64_to_double (buf + 56); calib->unipolarOffset[3] = ue9_fp64_to_double(buf + 56);
/* Block 1 */ /* Block 1 */
if (ue9_memory_read (fd, 1, buf, 128) < 0) if (ue9_memory_read(fd, 1, buf, 128) < 0)
return -1; return -1;
calib->bipolarSlope = ue9_fp64_to_double (buf + 0); calib->bipolarSlope = ue9_fp64_to_double(buf + 0);
calib->bipolarOffset = ue9_fp64_to_double (buf + 8); calib->bipolarOffset = ue9_fp64_to_double(buf + 8);
/* Block 2 */ /* Block 2 */
if (ue9_memory_read (fd, 2, buf, 128) < 0) if (ue9_memory_read(fd, 2, buf, 128) < 0)
return -1; return -1;
calib->DACSlope[0] = ue9_fp64_to_double (buf + 0); calib->DACSlope[0] = ue9_fp64_to_double(buf + 0);
calib->DACOffset[0] = ue9_fp64_to_double (buf + 8); calib->DACOffset[0] = ue9_fp64_to_double(buf + 8);
calib->DACSlope[1] = ue9_fp64_to_double (buf + 16); calib->DACSlope[1] = ue9_fp64_to_double(buf + 16);
calib->DACOffset[1] = ue9_fp64_to_double (buf + 24); calib->DACOffset[1] = ue9_fp64_to_double(buf + 24);
calib->tempSlope = ue9_fp64_to_double (buf + 32); calib->tempSlope = ue9_fp64_to_double(buf + 32);
calib->tempSlopeLow = ue9_fp64_to_double (buf + 48); calib->tempSlopeLow = ue9_fp64_to_double(buf + 48);
calib->calTemp = ue9_fp64_to_double (buf + 64); calib->calTemp = ue9_fp64_to_double(buf + 64);
calib->Vref = ue9_fp64_to_double (buf + 72); calib->Vref = ue9_fp64_to_double(buf + 72);
calib->VrefDiv2 = ue9_fp64_to_double (buf + 88); calib->VrefDiv2 = ue9_fp64_to_double(buf + 88);
calib->VsSlope = ue9_fp64_to_double (buf + 96); calib->VsSlope = ue9_fp64_to_double(buf + 96);
/* Block 3 */ /* Block 3 */
if (ue9_memory_read (fd, 3, buf, 128) < 0) if (ue9_memory_read(fd, 3, buf, 128) < 0)
return -1; return -1;
calib->hiResUnipolarSlope = ue9_fp64_to_double (buf + 0); calib->hiResUnipolarSlope = ue9_fp64_to_double(buf + 0);
calib->hiResUnipolarOffset = ue9_fp64_to_double (buf + 8); calib->hiResUnipolarOffset = ue9_fp64_to_double(buf + 8);
/* Block 4 */ /* Block 4 */
if (ue9_memory_read (fd, 4, buf, 128) < 0) if (ue9_memory_read(fd, 4, buf, 128) < 0)
return -1; return -1;
calib->hiResBipolarSlope = ue9_fp64_to_double (buf + 0); calib->hiResBipolarSlope = ue9_fp64_to_double(buf + 0);
calib->hiResBipolarOffset = ue9_fp64_to_double (buf + 8); calib->hiResBipolarOffset = ue9_fp64_to_double(buf + 8);
/* All done */ /* All done */
return 1; return 1;
} }
/* Retrieve comm config, returns -1 on error */ /* Retrieve comm config, returns -1 on error */
int int ue9_get_comm_config(int fd, struct ue9CommConfig *config)
ue9_get_comm_config (int fd, struct ue9CommConfig *config)
{ {
uint8_t sendbuf[18]; uint8_t sendbuf[18];
uint8_t recvbuf[24]; uint8_t recvbuf[24];
memset (sendbuf, 0, sizeof (sendbuf)); memset(sendbuf, 0, sizeof(sendbuf));
memset (config, 0, sizeof (struct ue9CommConfig)); memset(config, 0, sizeof(struct ue9CommConfig));
sendbuf[1] = 0xf8; sendbuf[1] = 0xf8;
sendbuf[2] = 0x09; sendbuf[2] = 0x09;
sendbuf[3] = 0x08; sendbuf[3] = 0x08;
if (ue9_command (fd, sendbuf, recvbuf, sizeof (recvbuf)) < 0) if (ue9_command(fd, sendbuf, recvbuf, sizeof(recvbuf)) < 0) {
{ verb("command failed\n");
verb ("command failed\n"); return -1;
return -1; }
} verb("todo\n");
verb ("todo\n"); return -1;
return -1;
} }
/* Retrieve control config, returns -1 on error */ /* Retrieve control config, returns -1 on error */
int int ue9_get_control_config(int fd, struct ue9ControlConfig *config)
ue9_get_control_config (int fd, struct ue9ControlConfig *config)
{ {
uint8_t sendbuf[18]; uint8_t sendbuf[18];
uint8_t recvbuf[24]; uint8_t recvbuf[24];
memset (sendbuf, 0, sizeof (sendbuf)); memset(sendbuf, 0, sizeof(sendbuf));
memset (config, 0, sizeof (struct ue9ControlConfig)); memset(config, 0, sizeof(struct ue9ControlConfig));
sendbuf[1] = 0xf8; sendbuf[1] = 0xf8;
sendbuf[2] = 0x06; sendbuf[2] = 0x06;
sendbuf[3] = 0x08; sendbuf[3] = 0x08;
if (ue9_command (fd, sendbuf, recvbuf, sizeof (recvbuf)) < 0) if (ue9_command(fd, sendbuf, recvbuf, sizeof(recvbuf)) < 0) {
{ verb("command failed\n");
verb ("command failed\n"); return -1;
return -1; }
} verb("todo\n");
verb ("todo\n"); return -1;
return -1;
} }
/* Open TCP/IP connection to the UE9 */ /* Open TCP/IP connection to the UE9 */
int int ue9_open(const char *host, int port)
ue9_open (const char *host, int port)
{ {
int fd; int fd;
struct sockaddr_in address; struct sockaddr_in address;
struct hostent *he; struct hostent *he;
int window_size = 128 * 1024; int window_size = 128 * 1024;
net_init (); net_init();
/* Create socket */ /* Create socket */
fd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) if (fd < 0) {
{ verb("socket returned %d\n", fd);
verb ("socket returned %d\n", fd); return -1;
return -1; }
}
/* Set nonblocking */
/* Set nonblocking */ if (soblock(fd, 0) < 0) {
if (soblock (fd, 0) < 0) verb("can't set nonblocking\n");
{ return -1;
verb ("can't set nonblocking\n"); }
return -1;
} /* Set initial window size hint to workaround LabJack firmware bug */
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void *)&window_size,
/* Set initial window size hint to workaround LabJack firmware bug */ sizeof(window_size));
setsockopt (fd, SOL_SOCKET, SO_SNDBUF, (void *) &window_size, setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *)&window_size,
sizeof (window_size)); sizeof(window_size));
setsockopt (fd, SOL_SOCKET, SO_RCVBUF, (void *) &window_size,
sizeof (window_size)); /* Resolve host */
address.sin_family = AF_INET;
/* Resolve host */ address.sin_port = htons(port);
address.sin_family = AF_INET; he = gethostbyname(host);
address.sin_port = htons (port); if (he == NULL) {
he = gethostbyname (host); verb("gethostbyname(\"%s\") failed\n", host);
if (he == NULL) return -1;
{ }
verb ("gethostbyname(\"%s\") failed\n", host); address.sin_addr = *((struct in_addr *)he->h_addr);
return -1;
} debug("Resolved %s -> %s\n", host, inet_ntoa(address.sin_addr));
address.sin_addr = *((struct in_addr *) he->h_addr);
/* Connect */
debug ("Resolved %s -> %s\n", host, inet_ntoa (address.sin_addr)); if (connect_timeout(fd, (struct sockaddr *)&address, sizeof(address),
&(struct timeval) {
/* Connect */ .tv_sec = UE9_TIMEOUT}) < 0) {
if (connect_timeout (fd, (struct sockaddr *) &address, sizeof (address), verb("connection to %s:%d failed: %s\n",
&(struct timeval) inet_ntoa(address.sin_addr), port, compat_strerror(errno));
{ return -1;
.tv_sec = UE9_TIMEOUT}) < 0) }
{
verb ("connection to %s:%d failed: %s\n", return fd;
inet_ntoa (address.sin_addr), port, compat_strerror (errno));
return -1;
}
return fd;
} }
/* Close connection to the UE9 */ /* Close connection to the UE9 */
void void ue9_close(int fd)
ue9_close (int fd)
{ {
/* does anyone actually call shutdown these days? */ /* does anyone actually call shutdown these days? */
shutdown (fd, 2 /* SHUT_RDWR */ ); shutdown(fd, 2 /* SHUT_RDWR */ );
close (fd); close(fd);
} }
/* Compute scanrate based on the provided values. */ /* Compute scanrate based on the provided values. */
double double ue9_compute_rate(uint8_t scanconfig, uint16_t scaninterval)
ue9_compute_rate (uint8_t scanconfig, uint16_t scaninterval)
{ {
double clock; double clock;
/* A "scan" is across all channels. Each scan is triggered at /* A "scan" is across all channels. Each scan is triggered at
a fixed rate, and not affected by the number of channels. a fixed rate, and not affected by the number of channels.
Channels are scanned as quickly as possible. */ Channels are scanned as quickly as possible. */
switch ((scanconfig >> 3) & 3) switch ((scanconfig >> 3) & 3) {
{ case 0:
case 0: clock = 4e6;
clock = 4e6; break;
break; case 1:
case 1: clock = 48e6;
clock = 48e6; break;
break; case 2:
case 2: clock = 750e3;
clock = 750e3; break;
break; case 3:
case 3: clock = 24e6;
clock = 24e6; break;
break; }
}
if (scanconfig & 0x2)
if (scanconfig & 0x2) clock /= 256;
clock /= 256;
if (scaninterval == 0)
if (scaninterval == 0) return 0;
return 0;
return clock / scaninterval;
return clock / scaninterval;
} }
/* Choose the best ScanConfig and ScanInterval parameters for the /* Choose the best ScanConfig and ScanInterval parameters for the
desired scanrate. Returns -1 if no valid config found */ desired scanrate. Returns -1 if no valid config found */
int int
ue9_choose_scan (double desired_rate, double *actual_rate, ue9_choose_scan(double desired_rate, double *actual_rate,
uint8_t * scanconfig, uint16_t * scaninterval) uint8_t * scanconfig, uint16_t * scaninterval)
{ {
int i; int i;
struct struct {
{ double clock;
double clock; uint8_t config;
uint8_t config; } valid[] = {
} valid[] = {
{ 48e6, 0x08}, {
{ 24e6, 0x18}, {
48e6, 0x08}, 4e6, 0x00}, {
{ 750e3, 0x10}, {
24e6, 0x18}, 48e6 / 256, 0x0a}, {
{ 24e6 / 256, 0x1a}, {
4e6, 0x00}, 4e6 / 256, 0x02}, {
{ 750e3 / 256, 0x12}, {
750e3, 0x10}, 0, 0}};
{
48e6 / 256, 0x0a}, /* Start with the fastest clock frequency. If the
{ scaninterval would be too large, knock it down until it
24e6 / 256, 0x1a}, fits. */
{ for (i = 0; valid[i].clock != 0; i++) {
4e6 / 256, 0x02}, double interval = valid[i].clock / desired_rate;
{
750e3 / 256, 0x12}, debug("Considering clock %lf (interval %lf)\n",
{ valid[i].clock, interval);
0, 0}};
if (interval >= 0.5 && interval < 65535.5) {
/* Start with the fastest clock frequency. If the
scaninterval would be too large, knock it down until it *scaninterval = floor(interval + 0.5);
fits. */
for (i = 0; valid[i].clock != 0; i++) *scanconfig = valid[i].config;
{ *actual_rate =
double interval = valid[i].clock / desired_rate; ue9_compute_rate(*scanconfig, *scaninterval);
debug ("Considering clock %lf (interval %lf)\n", debug("Config 0x%02x, desired %lf, actual %lf\n",
valid[i].clock, interval); *scanconfig, desired_rate, *actual_rate);
if (interval >= 0.5 && interval < 65535.5) return 0;
{ }
}
*scaninterval = floor (interval + 0.5);
return -1;
*scanconfig = valid[i].config;
*actual_rate = ue9_compute_rate (*scanconfig, *scaninterval);
debug ("Config 0x%02x, desired %lf, actual %lf\n",
*scanconfig, desired_rate, *actual_rate);
return 0;
}
}
return -1;
} }
/* Flush data buffers */ /* Flush data buffers */
void void ue9_buffer_flush(int fd)
ue9_buffer_flush (int fd)
{ {
uint8_t sendbuf[2], recvbuf[2]; uint8_t sendbuf[2], recvbuf[2];
sendbuf[1] = 0x08; /* FlushBuffer */ sendbuf[1] = 0x08; /* FlushBuffer */
if (ue9_command (fd, sendbuf, recvbuf, sizeof (recvbuf)) < 0) if (ue9_command(fd, sendbuf, recvbuf, sizeof(recvbuf)) < 0) {
{ verb("command failed\n");
verb ("command failed\n"); }
}
} }
/* Stop stream. Returns < 0 on failure. */ /* Stop stream. Returns < 0 on failure. */
int int ue9_stream_stop(int fd)
ue9_stream_stop (int fd)
{ {
uint8_t sendbuf[2], recvbuf[4]; uint8_t sendbuf[2], recvbuf[4];
sendbuf[1] = 0xB0; sendbuf[1] = 0xB0;
if (ue9_command (fd, sendbuf, recvbuf, sizeof (recvbuf)) < 0) if (ue9_command(fd, sendbuf, recvbuf, sizeof(recvbuf)) < 0) {
{ verb("command failed\n");
verb ("command failed\n"); return -1;
return -1; }
}
if (recvbuf[2] == STREAM_NOT_RUNNING || recvbuf[2] == 0) if (recvbuf[2] == STREAM_NOT_RUNNING || recvbuf[2] == 0)
return 0; return 0;
debug ("error %s\n", ue9_error (recvbuf[2])); debug("error %s\n", ue9_error(recvbuf[2]));
return -recvbuf[2]; return -recvbuf[2];
} }
/* Start stream. Returns < 0 on failure. */ /* Start stream. Returns < 0 on failure. */
int int ue9_stream_start(int fd)
ue9_stream_start (int fd)
{ {
uint8_t sendbuf[2], recvbuf[4]; uint8_t sendbuf[2], recvbuf[4];
sendbuf[1] = 0xA8; sendbuf[1] = 0xA8;
if (ue9_command (fd, sendbuf, recvbuf, sizeof (recvbuf)) < 0) if (ue9_command(fd, sendbuf, recvbuf, sizeof(recvbuf)) < 0) {
{ verb("command failed\n");
verb ("command failed\n"); return -1;
return -1; }
}
if (recvbuf[2] == 0) if (recvbuf[2] == 0)
return 0; return 0;
debug ("error %s\n", ue9_error (recvbuf[2])); debug("error %s\n", ue9_error(recvbuf[2]));
return -recvbuf[2]; return -recvbuf[2];
} }
/* "Simple" stream configuration, assumes the channels are all /* "Simple" stream configuration, assumes the channels are all
configured with the same gain. */ configured with the same gain. */
int int
ue9_streamconfig_simple (int fd, int *channel_list, int channel_count, ue9_streamconfig_simple(int fd, int *channel_list, int channel_count,
uint8_t scanconfig, uint16_t scaninterval, uint8_t scanconfig, uint16_t scaninterval, uint8_t gain)
uint8_t gain)
{ {
int i; int i;
uint8_t buf[256]; uint8_t buf[256];
/* Set up StreamConfig command with channels and scan options */ /* Set up StreamConfig command with channels and scan options */
buf[1] = 0xF8; /* Extended command */ buf[1] = 0xF8; /* Extended command */
buf[2] = channel_count + 3; /* Command data words */ buf[2] = channel_count + 3; /* Command data words */
buf[3] = 0x11; /* StreamConfig */ buf[3] = 0x11; /* StreamConfig */
buf[6] = channel_count; /* Number of channels */ buf[6] = channel_count; /* Number of channels */
buf[7] = 12; /* Bit resolution */ buf[7] = 12; /* Bit resolution */
buf[8] = 0; /* Extra settling time */ buf[8] = 0; /* Extra settling time */
buf[9] = scanconfig; buf[9] = scanconfig;
buf[10] = scaninterval & 0xff; buf[10] = scaninterval & 0xff;
buf[11] = scaninterval >> 8; buf[11] = scaninterval >> 8;
for (i = 0; i < channel_count; i++) for (i = 0; i < channel_count; i++) {
{ buf[12 + 2 * i] = channel_list[i]; /* Channel number */
buf[12 + 2 * i] = channel_list[i]; /* Channel number */ buf[13 + 2 * i] = gain; /* Gain/bipolar setup */
buf[13 + 2 * i] = gain; /* Gain/bipolar setup */ }
}
/* Send StreamConfig */
/* Send StreamConfig */ if (ue9_command(fd, buf, buf, 8) < 0) {
if (ue9_command (fd, buf, buf, 8) < 0) debug("command failed\n");
{ return -1;
debug ("command failed\n"); }
return -1;
} if (buf[6] != 0) {
verb("returned error %s\n", ue9_error(buf[6]));
if (buf[6] != 0) return -1;
{ }
verb ("returned error %s\n", ue9_error (buf[6]));
return -1; return 0;
} }
return 0; /* Timer configuration */
int ue9_timer_config(int fd, int *mode_list, int mode_count, int divisor)
{
int i;
uint8_t buf[256];
if (mode_count < 0 || mode_count > 6) {
verb("invalid count\n");
return -1;
}
/* Set up TimerConfig command */
buf[1] = 0xF8; /* Extended command */
buf[2] = 0x0C; /* Command data words */
buf[3] = 0x18; /* TimerConfig */
buf[6] = divisor; /* TimerClockDivisor */
buf[7] = 0x80 | mode_count; /* Number of timers enabled, UpdateConfig=1 */
buf[8] = 0x01; /* TimerClockBase = System 48MHz */
buf[9] = 0x00; /* Don't reset */
for (i = 0; i < 6; i++) {
if (i < mode_count)
buf[10 + 3 * i] = mode_list[i];
else
buf[10 + 3 * i] = 0;
buf[11 + 3 * i] = 0;
buf[12 + 3 * i] = 0;
}
buf[28] = 0;
buf[29] = 0;
/* Send StreamConfig */
if (ue9_command(fd, buf, buf, 40) < 0) {
debug("command failed\n");
return -1;
}
if (buf[6] != 0) {
verb("returned error %s\n", ue9_error(buf[6]));
return -1;
}
debug("timer EnableStatus=0x%02x\n", buf[7]);
return 0;
} }
/* Stream data and pass it to the data callback. If callback returns /* Stream data and pass it to the data callback. If callback returns
negative, stops reading and returns 0. Returns < 0 on error. */ negative, stops reading and returns 0. Returns < 0 on error. */
int int
ue9_stream_data (int fd, int channels, ue9_stream_data(int fd, int channels, ue9_stream_cb_t callback, void *context)
ue9_stream_cb_t callback, void *context)
{ {
int ret; int ret;
uint8_t buf[46]; uint8_t buf[46];
uint8_t packet = 0; uint8_t packet = 0;
int channel = 0; int channel = 0;
int i; int i;
uint16_t data[channels]; uint16_t data[channels];
for (;;) for (;;) {
{ /* Receive data */
/* Receive data */ ret = recv_all_timeout(fd, buf, 46, 0, &(struct timeval) {
ret = recv_all_timeout (fd, buf, 46, 0, &(struct timeval) .tv_sec = UE9_TIMEOUT});
{
.tv_sec = UE9_TIMEOUT}); /* Verify packet format */
if (ret != 46) {
/* Verify packet format */ verb("short recv %d\n", (int)ret);
if (ret != 46) return -1;
{ }
verb ("short recv %d\n", (int) ret);
return -1; if (!ue9_verify_extended(buf, 46) || !ue9_verify_normal(buf, 6)) {
} verb("bad checksum\n");
return -2;
if (!ue9_verify_extended (buf, 46) || !ue9_verify_normal (buf, 6)) }
{
verb ("bad checksum\n"); if (buf[1] != 0xF9 || buf[2] != 0x14 || buf[3] != 0xC0) {
return -2; verb("bad command bytes\n");
} return -3;
}
if (buf[1] != 0xF9 || buf[2] != 0x14 || buf[3] != 0xC0)
{ if (buf[11] != 0) {
verb ("bad command bytes\n"); verb("stream error: %s\n", ue9_error(buf[11]));
return -3; return -4;
} }
if (buf[11] != 0) /* Check for dropped packets. */
{ if (buf[10] != packet) {
verb ("stream error: %s\n", ue9_error (buf[11])); verb("expected packet %d, but received packet %d\n",
return -4; packet, buf[10]);
} return -5;
}
/* Check for dropped packets. */ packet++;
if (buf[10] != packet)
{ /* Check comm processor backlog (up to 512 kB) */
verb ("expected packet %d, but received packet %d\n", if (buf[45] & 0x80) {
packet, buf[10]); verb("buffer overflow in CommBacklog, aborting\n");
return -5; return -6;
} }
packet++; if ((buf[45] & 0x7f) > 112)
debug("warning: CommBacklog is high (%d bytes)\n",
/* Check comm processor backlog (up to 512 kB) */ (buf[45] & 0x7f) * 4096);
if (buf[45] & 0x80)
{ /* Check control processor backlog (up to 256 bytes). */
verb ("buffer overflow in CommBacklog, aborting\n"); if (buf[44] == 255) {
return -6; verb("ControlBacklog is maxed out, aborting\n");
} return -7;
if ((buf[45] & 0x7f) > 112) }
debug ("warning: CommBacklog is high (%d bytes)\n", if (buf[44] > 224)
(buf[45] & 0x7f) * 4096); debug("warning: ControlBacklog is high (%d bytes)\n",
buf[44]);
/* Check control processor backlog (up to 256 bytes). */
if (buf[44] == 255) /* Read samples from the buffer */
{ for (i = 12; i <= 42; i += 2) {
verb ("ControlBacklog is maxed out, aborting\n"); data[channel++] = buf[i] + (buf[i + 1] << 8);
return -7; if (channel < channels)
} continue;
if (buf[44] > 224)
debug ("warning: ControlBacklog is high (%d bytes)\n", buf[44]); /* Received a full scan, send to callback */
channel = 0;
/* Read samples from the buffer */ if ((*callback) (channels, data, context) < 0) {
for (i = 12; i <= 42; i += 2) /* We're done */
{ return 0;
data[channel++] = buf[i] + (buf[i + 1] << 8); }
if (channel < channels) }
continue; }
/* Received a full scan, send to callback */
channel = 0;
if ((*callback) (channels, data, context) < 0)
{
/* We're done */
return 0;
}
}
}
} }
/*
Local variables:
c-basic-offset: 2
End:
*/
...@@ -16,59 +16,56 @@ ...@@ -16,59 +16,56 @@
#include "netutil.h" #include "netutil.h"
/* Calibration data */ /* Calibration data */
struct ue9Calibration struct ue9Calibration {
{ double unipolarSlope[4];
double unipolarSlope[4]; double unipolarOffset[4];
double unipolarOffset[4]; double bipolarSlope;
double bipolarSlope; double bipolarOffset;
double bipolarOffset; double DACSlope[2];
double DACSlope[2]; double DACOffset[2];
double DACOffset[2]; double tempSlope;
double tempSlope; double tempSlopeLow;
double tempSlopeLow; double calTemp;
double calTemp; double Vref;
double Vref; double VrefDiv2;
double VrefDiv2; double VsSlope;
double VsSlope; double hiResUnipolarSlope;
double hiResUnipolarSlope; double hiResUnipolarOffset;
double hiResUnipolarOffset; double hiResBipolarSlope;
double hiResBipolarSlope; double hiResBipolarOffset;
double hiResBipolarOffset;
}; };
/* Comm config */ /* Comm config */
struct ue9CommConfig struct ue9CommConfig {
{ uint8_t local_id;
uint8_t local_id; uint8_t power_level;
uint8_t power_level; in_addr_t address;
in_addr_t address; in_addr_t gateway;
in_addr_t gateway; in_addr_t subnet;
in_addr_t subnet; in_port_t portA;
in_port_t portA; in_port_t portB;
in_port_t portB; uint8_t dhcp_enabled;
uint8_t dhcp_enabled; uint8_t product_id;
uint8_t product_id; uint8_t mac_address[6];
uint8_t mac_address[6]; double hw_version;
double hw_version; double comm_fw_version;
double comm_fw_version;
}; };
/* Control config */ /* Control config */
struct ue9ControlConfig struct ue9ControlConfig {
{ uint8_t power_level;
uint8_t power_level; uint8_t reset_source;
uint8_t reset_source; double control_fw_version;
double control_fw_version; double control_bl_version;
double control_bl_version; uint8_t hires;
uint8_t hires; uint8_t fio_dir;
uint8_t fio_dir; uint8_t fio_state;
uint8_t fio_state; uint8_t eio_dir;
uint8_t eio_dir; uint8_t eio_state;
uint8_t eio_state; uint8_t cio_dirstate;;
uint8_t cio_dirstate;; uint8_t mio_dirstate;
uint8_t mio_dirstate; uint16_t dac0;
uint16_t dac0; uint16_t dac1;
uint16_t dac1;
}; };
#define UE9_UNIPOLAR_GAIN1 0x00 #define UE9_UNIPOLAR_GAIN1 0x00
...@@ -77,68 +74,73 @@ struct ue9ControlConfig ...@@ -77,68 +74,73 @@ struct ue9ControlConfig
#define UE9_UNIPOLAR_GAIN8 0x03 #define UE9_UNIPOLAR_GAIN8 0x03
#define UE9_BIPOLAR_GAIN1 0x08 #define UE9_BIPOLAR_GAIN1 0x08
#define UE9_CHANNELS 14 #define UE9_MAX_CHANNEL_COUNT 128
#define UE9_MAX_CHANNEL 255
#define UE9_TIMERS 6
/* Fill checksums in data buffers */ /* Fill checksums in data buffers */
void ue9_checksum_normal (uint8_t * buffer, size_t len); void ue9_checksum_normal(uint8_t * buffer, size_t len);
void ue9_checksum_extended (uint8_t * buffer, size_t len); void ue9_checksum_extended(uint8_t * buffer, size_t len);
/* Verify checksums in data buffers. Returns 0 on error. */ /* Verify checksums in data buffers. Returns 0 on error. */
int ue9_verify_normal (uint8_t * buffer, size_t len); int ue9_verify_normal(uint8_t * buffer, size_t len);
int ue9_verify_extended (uint8_t * buffer, size_t len); int ue9_verify_extended(uint8_t * buffer, size_t len);
/* Open/close TCP/IP connection to the UE9 */ /* Open/close TCP/IP connection to the UE9 */
int ue9_open (const char *host, int port); int ue9_open(const char *host, int port);
void ue9_close (int fd); void ue9_close(int fd);
/* Read a memory block from the device. Returns -1 on error. */ /* Read a memory block from the device. Returns -1 on error. */
int ue9_memory_read (int fd, int blocknum, uint8_t * buffer, int len); int ue9_memory_read(int fd, int blocknum, uint8_t * buffer, int len);
/* Convert 64-bit fixed point to double type */ /* Convert 64-bit fixed point to double type */
double ue9_fp64_to_double (uint8_t * data); double ue9_fp64_to_double(uint8_t * data);
/* Retrieve calibration data or configuration from the device */ /* Retrieve calibration data or configuration from the device */
int ue9_get_calibration (int fd, struct ue9Calibration *calib); int ue9_get_calibration(int fd, struct ue9Calibration *calib);
int ue9_get_comm_config (int fd, struct ue9CommConfig *config); int ue9_get_comm_config(int fd, struct ue9CommConfig *config);
int ue9_get_control_config (int fd, struct ue9ControlConfig *config); int ue9_get_control_config(int fd, struct ue9ControlConfig *config);
/* Data conversion. If calib is NULL, use uncalibrated conversions. */ /* Data conversion. If calib is NULL, use uncalibrated conversions. */
double ue9_binary_to_analog (struct ue9Calibration *calib, double ue9_binary_to_analog(struct ue9Calibration *calib,
uint8_t gain, uint8_t resolution, uint16_t data); uint8_t gain, uint8_t resolution, uint16_t data);
/* Compute scanrate based on the provided values. */ /* Compute scanrate based on the provided values. */
double ue9_compute_rate (uint8_t scanconfig, uint16_t scaninterval); double ue9_compute_rate(uint8_t scanconfig, uint16_t scaninterval);
/* Choose the best ScanConfig and ScanInterval parameters for the /* Choose the best ScanConfig and ScanInterval parameters for the
desired scanrate. Returns 0 if nothing can be chosen. */ desired scanrate. Returns 0 if nothing can be chosen. */
int ue9_choose_scan (double desired_rate, double *actual_rate, int ue9_choose_scan(double desired_rate, double *actual_rate,
uint8_t * scanconfig, uint16_t * scaninterval); uint8_t * scanconfig, uint16_t * scaninterval);
/* Flush data buffers */ /* Flush data buffers */
void ue9_buffer_flush (int fd); void ue9_buffer_flush(int fd);
/* Stop stream. Returns < 0 on failure. */ /* Stop stream. Returns < 0 on failure. */
int ue9_stream_stop (int fd); int ue9_stream_stop(int fd);
/* Start stream. Returns < 0 on failure. */ /* Start stream. Returns < 0 on failure. */
int ue9_stream_start (int fd); int ue9_stream_start(int fd);
/* Execute a command on the UE9. Returns -1 on error. Fills the /* Execute a command on the UE9. Returns -1 on error. Fills the
checksums on the outgoing packets, and verifies them on the checksums on the outgoing packets, and verifies them on the
incoming packets. Data in "out" is transmitted, data in "in" is incoming packets. Data in "out" is transmitted, data in "in" is
received. */ received. */
int ue9_command (int fd, uint8_t * out, uint8_t * in, int inlen); int ue9_command(int fd, uint8_t * out, uint8_t * in, int inlen);
/* "Simple" stream configuration, assumes the channels are all /* "Simple" stream configuration, assumes the channels are all
configured with the same gain. */ configured with the same gain. */
int ue9_streamconfig_simple (int fd, int *channel_list, int channel_count, int ue9_streamconfig_simple(int fd, int *channel_list, int channel_count,
uint8_t scanconfig, uint16_t scaninterval, uint8_t scanconfig, uint16_t scaninterval,
uint8_t gain); uint8_t gain);
/* Timer configuration */
int ue9_timer_config(int fd, int *mode_list, int mode_count, int divisor);
/* Stream data and pass it to the data callback. If callback returns /* Stream data and pass it to the data callback. If callback returns
negative, stops reading and returns 0. Returns < 0 on error. */ negative, stops reading and returns 0. Returns < 0 on error. */
typedef int (*ue9_stream_cb_t) (int channels, uint16_t * data, void *context); typedef int (*ue9_stream_cb_t) (int channels, uint16_t * data, void *context);
int ue9_stream_data (int fd, int channels, int ue9_stream_data(int fd, int channels,
ue9_stream_cb_t callback, void *context); ue9_stream_cb_t callback, void *context);
#endif #endif
#include "ue9error.h" #include "ue9error.h"
const char *ue9_error_text[] = { const char *ue9_error_text[] = {
[0] = "(no error)", [0] = "(no error)",
[SCRATCH_WRT_FAIL] = "SCRATCH_WRT_FAIL", [SCRATCH_WRT_FAIL] = "SCRATCH_WRT_FAIL",
[SCRATCH_ERASE_FAIL] = "SCRATCH_ERASE_FAIL", [SCRATCH_ERASE_FAIL] = "SCRATCH_ERASE_FAIL",
[DATA_BUFFER_OVERFLOW] = "DATA_BUFFER_OVERFLOW", [DATA_BUFFER_OVERFLOW] = "DATA_BUFFER_OVERFLOW",
[ADC0_BUFFER_OVERFLOW] = "ADC0_BUFFER_OVERFLOW", [ADC0_BUFFER_OVERFLOW] = "ADC0_BUFFER_OVERFLOW",
[FUNCTION_INVALID] = "FUNCTION_INVALID", [FUNCTION_INVALID] = "FUNCTION_INVALID",
[SWDT_TIME_INVALID] = "SWDT_TIME_INVALID", [SWDT_TIME_INVALID] = "SWDT_TIME_INVALID",
[FLASH_WRITE_FAIL] = "FLASH_WRITE_FAIL", [FLASH_WRITE_FAIL] = "FLASH_WRITE_FAIL",
[FLASH_ERASE_FAIL] = "FLASH_ERASE_FAIL", [FLASH_ERASE_FAIL] = "FLASH_ERASE_FAIL",
[FLASH_JMP_FAIL] = "FLASH_JMP_FAIL", [FLASH_JMP_FAIL] = "FLASH_JMP_FAIL",
[FLASH_PSP_TIMEOUT] = "FLASH_PSP_TIMEOUT", [FLASH_PSP_TIMEOUT] = "FLASH_PSP_TIMEOUT",
[FLASH_ABORT_RECEIVED] = "FLASH_ABORT_RECEIVED", [FLASH_ABORT_RECEIVED] = "FLASH_ABORT_RECEIVED",
[FLASH_PAGE_MISMATCH] = "FLASH_PAGE_MISMATCH", [FLASH_PAGE_MISMATCH] = "FLASH_PAGE_MISMATCH",
[FLASH_BLOCK_MISMATCH] = "FLASH_BLOCK_MISMATCH", [FLASH_BLOCK_MISMATCH] = "FLASH_BLOCK_MISMATCH",
[FLASH_PAGE_NOT_IN_CODE_AREA] = "FLASH_PAGE_NOT_IN_CODE_AREA", [FLASH_PAGE_NOT_IN_CODE_AREA] = "FLASH_PAGE_NOT_IN_CODE_AREA",
[MEM_ILLEGAL_ADDRESS] = "MEM_ILLEGAL_ADDRESS", [MEM_ILLEGAL_ADDRESS] = "MEM_ILLEGAL_ADDRESS",
[FLASH_LOCKED] = "FLASH_LOCKED", [FLASH_LOCKED] = "FLASH_LOCKED",
[INVALID_BLOCK] = "INVALID_BLOCK", [INVALID_BLOCK] = "INVALID_BLOCK",
[FLASH_ILLEGAL_PAGE] = "FLASH_ILLEGAL_PAGE", [FLASH_ILLEGAL_PAGE] = "FLASH_ILLEGAL_PAGE",
[STREAM_IS_ACTIVE] = "STREAM_IS_ACTIVE", [STREAM_IS_ACTIVE] = "STREAM_IS_ACTIVE",
[STREAM_TABLE_INVALID] = "STREAM_TABLE_INVALID", [STREAM_TABLE_INVALID] = "STREAM_TABLE_INVALID",
[STREAM_CONFIG_INVALID] = "STREAM_CONFIG_INVALID", [STREAM_CONFIG_INVALID] = "STREAM_CONFIG_INVALID",
[STREAM_BAD_TRIGGER_SOURCE] = "STREAM_BAD_TRIGGER_SOURCE", [STREAM_BAD_TRIGGER_SOURCE] = "STREAM_BAD_TRIGGER_SOURCE",
[STREAM_NOT_RUNNING] = "STREAM_NOT_RUNNING", [STREAM_NOT_RUNNING] = "STREAM_NOT_RUNNING",
[STREAM_INVALID_TRIGGER] = "STREAM_INVALID_TRIGGER", [STREAM_INVALID_TRIGGER] = "STREAM_INVALID_TRIGGER",
[STREAM_CONTROL_BUFFER_OVERFLOW] = "STREAM_CONTROL_BUFFER_OVERFLOW", [STREAM_CONTROL_BUFFER_OVERFLOW] = "STREAM_CONTROL_BUFFER_OVERFLOW",
[STREAM_SCAN_OVERLAP] = "STREAM_SCAN_OVERLAP", [STREAM_SCAN_OVERLAP] = "STREAM_SCAN_OVERLAP",
[STREAM_SAMPLE_NUM_INVALID] = "STREAM_SAMPLE_NUM_INVALID", [STREAM_SAMPLE_NUM_INVALID] = "STREAM_SAMPLE_NUM_INVALID",
[STREAM_BIPOLAR_GAIN_INVALID] = "STREAM_BIPOLAR_GAIN_INVALID", [STREAM_BIPOLAR_GAIN_INVALID] = "STREAM_BIPOLAR_GAIN_INVALID",
[STREAM_SCAN_RATE_INVALID] = "STREAM_SCAN_RATE_INVALID", [STREAM_SCAN_RATE_INVALID] = "STREAM_SCAN_RATE_INVALID",
[TIMER_INVALID_MODE] = "TIMER_INVALID_MODE", [TIMER_INVALID_MODE] = "TIMER_INVALID_MODE",
[TIMER_QUADRATURE_AB_ERROR] = "TIMER_QUADRATURE_AB_ERROR", [TIMER_QUADRATURE_AB_ERROR] = "TIMER_QUADRATURE_AB_ERROR",
[TIMER_QUAD_PULSE_SEQUENCE] = "TIMER_QUAD_PULSE_SEQUENCE", [TIMER_QUAD_PULSE_SEQUENCE] = "TIMER_QUAD_PULSE_SEQUENCE",
[TIMER_BAD_CLOCK_SOURCE] = "TIMER_BAD_CLOCK_SOURCE", [TIMER_BAD_CLOCK_SOURCE] = "TIMER_BAD_CLOCK_SOURCE",
[TIMER_STREAM_ACTIVE] = "TIMER_STREAM_ACTIVE", [TIMER_STREAM_ACTIVE] = "TIMER_STREAM_ACTIVE",
[TIMER_PWMSTOP_MODULE_ERROR] = "TIMER_PWMSTOP_MODULE_ERROR", [TIMER_PWMSTOP_MODULE_ERROR] = "TIMER_PWMSTOP_MODULE_ERROR",
[EXT_OSC_NOT_STABLE] = "EXT_OSC_NOT_STABLE", [EXT_OSC_NOT_STABLE] = "EXT_OSC_NOT_STABLE",
[INVALID_POWER_SETTING] = "INVALID_POWER_SETTING", [INVALID_POWER_SETTING] = "INVALID_POWER_SETTING",
[PLL_NOT_LOCKED] = "PLL_NOT_LOCKED" [PLL_NOT_LOCKED] = "PLL_NOT_LOCKED"
}; };
const char * const char *ue9_error(int errorcode)
ue9_error (int errorcode)
{ {
if (errorcode > ARRAY_SIZE (ue9_error_text)) if (errorcode > ARRAY_SIZE(ue9_error_text))
return "(invalid errorcode)"; return "(invalid errorcode)";
else else
return ue9_error_text[errorcode]; return ue9_error_text[errorcode];
} }
...@@ -44,6 +44,6 @@ ...@@ -44,6 +44,6 @@
extern const char *ue9_error_text[]; extern const char *ue9_error_text[];
const char *ue9_error (int errorcode); const char *ue9_error(int errorcode);
#endif #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment