git-svn-id: https://bucket.mit.edu/svn/nilm/acquisition/ethstream@7089 ddd99763-3ecb-0310-9145-efcb8ce7c51ftags/ethstream-1.0
@@ -0,0 +1,12 @@ | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of version 2 of the GNU General Public License as | |||
published by the Free Software Foundation, 59 Temple Place, Suite 330, | |||
Boston, MA 02111-1307 USA | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU General Public License for more details. | |||
For the complete license, please see http://www.fsf.org/licenses/gpl.txt | |||
or request a copy from the author of this program. |
@@ -0,0 +1,109 @@ | |||
# | |||
# Labjack/Nerdjack Tools | |||
# Copyright (c) 2007-2009 | |||
# Jim Paris <jim@jtan.com>, Zach Clifford <zacharyc@mit.edu> | |||
# | |||
# 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. | |||
# | |||
# For Solaris, use: gmake CC=gcc LDFLAGS="-lsocket -lnsl" | |||
# For Windows, build with "make win" | |||
# Build options | |||
CFLAGS += -Wall -g #-pg | |||
LDFLAGS += -lm #-pg | |||
PREFIX = /usr/local | |||
MANPATH = ${PREFIX}/man/man1 | |||
BINPATH = ${PREFIX}/bin | |||
WINCC = i386-mingw32-gcc | |||
WINCFLAGS += $(CFLAGS) | |||
WINLDFLAGS += $(LDFLAGS) -lws2_32 -s | |||
# Targets | |||
.PHONY: default | |||
default: lin | |||
.PHONY: all | |||
all: lin win | |||
.PHONY: lin | |||
lin: ljtest ethstream ljconfig \ | |||
ethstream.1 ljconfig.1 | |||
.PHONY: win | |||
win: ljtest.exe ethstream.exe ljconfig.exe | |||
version.h: VERSION | |||
echo "/* This file was automatically generated. */" >version.h | |||
echo "#define VERSION \"`cat VERSION` (`date +%Y-%m-%d`)\"" >>version.h | |||
# Object files for each executable | |||
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-ljtest = ljtest.o $(obj-common) | |||
ljconfig: $(obj-ljconfig) | |||
ethstream: $(obj-ethstream) | |||
ljtest: $(obj-ljtest) | |||
ljconfig.exe: $(obj-ljconfig:.o=.obj) compat-win32.obj | |||
ethstream.exe: $(obj-ethstream:.o=.obj) compat-win32.obj | |||
ljtest.exe: $(obj-ljtest:.o=.obj) compat-win32.obj | |||
# Manpages | |||
%.1: % | |||
if ! help2man -N --output=$@ ./$< ; then \ | |||
echo "No manual page available." > $@ ; fi | |||
%.txt: %.1 | |||
nroff -man $< | colcrt | sed s/$$/\\r/ > $@ | |||
# Install/uninstall targets for Linux | |||
.PHONY: install | |||
install: ethstream.1 ethstream | |||
install -m 0755 ethstream ${BINPATH} | |||
install -m 0644 ethstream.1 ${MANPATH} | |||
.PHONY: uninstall | |||
uninstall: | |||
rm -f ${BINPATH}/ethstream ${MANPATH}/ethstream.1 | |||
# Packaging | |||
PACKAGE=labjack-`cat VERSION` | |||
.PHONY: dist | |||
dist: version.h | |||
mkdir -p ${PACKAGE} | |||
cp [A-Z]* *.[ch] ${PACKAGE} | |||
tar cvzf ${PACKAGE}.tar.gz ${PACKAGE} | |||
rm -r ${PACKAGE} | |||
# Maintenance | |||
.PHONY: clean distclean | |||
clean distclean: | |||
rm -f *.o *.obj *.exe ethstream ljtest ljconfig core *.d *.1 *.txt | |||
# Dependency tracking: | |||
allsources = $(wildcard *.c) | |||
-include $(allsources:.c=.d) | |||
%.o : %.c | |||
$(COMPILE.c) -MP -MMD -MT '$*.obj' -o $@ $< | |||
%.obj : %.c | |||
$(WINCC) $(WINCFLAGS) -MP -MMD -MT '$*.o' -c -o $@ $< | |||
# Win32 executable | |||
%.exe : %.obj | |||
$(WINCC) -o $@ $^ $(WINLDFLAGS) |
@@ -0,0 +1,6 @@ | |||
Labjack/Nerdjack Tools | |||
by Jim Paris <jim@jtan.com> | |||
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: | |||
http://www.labjack.com/labjack_ue9.php |
@@ -0,0 +1 @@ | |||
1.0 |
@@ -0,0 +1,83 @@ | |||
#include <unistd.h> | |||
#include <stdio.h> | |||
#include "compat.h" | |||
#include <windows.h> | |||
unsigned int sleep(unsigned int seconds) | |||
{ | |||
Sleep(seconds * 1000); | |||
return 0; | |||
} | |||
static struct { | |||
int num; | |||
char *msg; | |||
} win32_error[] = { | |||
/* Errors that we might vaguely expect to see */ | |||
{ WSAEINTR, "Winsock: Interrupted system call" }, | |||
{ WSAEBADF, "Winsock: Bad file number" }, | |||
{ WSAEFAULT, "Winsock: Bad address" }, | |||
{ WSAEINVAL, "Winsock: Invalid argument" }, | |||
{ WSAEMFILE, "Winsock: Too many open files" }, | |||
{ WSAEWOULDBLOCK, "Winsock: Operation would block" }, | |||
{ WSAEINPROGRESS, "Winsock: Operation now in progress" }, | |||
{ WSAEALREADY, "Winsock: Operation already in progress" }, | |||
{ WSAENOTSOCK, "Winsock: Socket operation on nonsocket" }, | |||
{ WSAEADDRINUSE, "Winsock: Address already in use" }, | |||
{ WSAEADDRNOTAVAIL, "Winsock: Cannot assign requested address" }, | |||
{ WSAENETDOWN, "Winsock: Network is down" }, | |||
{ 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; | |||
static char buf[128]; | |||
for (i = 0; win32_error[i].num != -1; i++) | |||
if (errnum == win32_error[i].num) | |||
return win32_error[i].msg; | |||
if (errnum >= 10000) { | |||
sprintf(buf, "Winsock: unknown error %d\n", errnum); | |||
return buf; | |||
} | |||
return strerror(errnum); | |||
} | |||
#ifdef __WIN32__ | |||
/*const char *compat_inet_ntop(int af, const void *src, char *dst, socklen_t cnt) | |||
{ | |||
if (af == AF_INET) | |||
{ | |||
struct sockaddr_in in; | |||
memset(&in, 0, sizeof(in)); | |||
in.sin_family = AF_INET; | |||
memcpy(&in.sin_addr, src, sizeof(struct in_addr)); | |||
getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST); | |||
return dst; | |||
} | |||
else if (af == AF_INET6) | |||
{ | |||
struct sockaddr_in6 in; | |||
memset(&in, 0, sizeof(in)); | |||
in.sin6_family = AF_INET6; | |||
memcpy(&in.sin6_addr, src, sizeof(struct in_addr6)); | |||
getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST); | |||
return dst; | |||
} | |||
return NULL; | |||
} | |||
*/ | |||
#endif | |||
@@ -0,0 +1,15 @@ | |||
#ifndef COMPAT_H | |||
#define COMPAT_H | |||
#ifdef __WIN32__ | |||
unsigned int sleep(unsigned int seconds); | |||
char *compat_strerror(int errnum); | |||
//const char *inet_ntop(int af, void *src, const char *dst, socklen_t cnt); | |||
#define INET_ADDRSTRLEN 16 | |||
#define ETIMEDOUT 110 | |||
#define ENOTCONN 107 | |||
#else | |||
#define compat_strerror strerror | |||
#endif | |||
#endif |
@@ -0,0 +1,18 @@ | |||
#include "debug.h" | |||
#include <stdio.h> | |||
#include <stdarg.h> | |||
int verb_count = 0; | |||
int func_fprintf(const char *func, FILE *stream, const char *format, ...) | |||
{ | |||
va_list ap; | |||
int ret; | |||
fprintf(stream, "%s: ", func); | |||
va_start(ap, format); | |||
ret = vfprintf(stream, format, ap); | |||
va_end(ap); | |||
return ret; | |||
} | |||
@@ -0,0 +1,35 @@ | |||
/* | |||
* 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. | |||
*/ | |||
#ifndef DEBUG_H | |||
#define DEBUG_H | |||
extern int verb_count; | |||
#include <stdio.h> | |||
int func_fprintf(const char *func, FILE *stream, const char *format, | |||
...) __attribute__ ((format (printf, 3, 4))); | |||
#define debug(x...) ({ \ | |||
if(verb_count >= 2) \ | |||
func_fprintf(__func__, stderr,x); \ | |||
}) | |||
#define verb(x...) ({ \ | |||
if(verb_count >= 1) \ | |||
func_fprintf(__func__, stderr,x); \ | |||
}) | |||
#define info(x...) ({ \ | |||
if(verb_count >= 0) \ | |||
fprintf(stderr,x); \ | |||
}) | |||
#endif |
@@ -0,0 +1,465 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* 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 <stdlib.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <errno.h> | |||
#include <sys/time.h> | |||
#include <time.h> | |||
#include <sys/stat.h> | |||
#include <signal.h> | |||
#include <unistd.h> | |||
#include "debug.h" | |||
#include "ue9.h" | |||
#include "ue9error.h" | |||
#include "nerdjack.h" | |||
#include "opt.h" | |||
#include "version.h" | |||
#include "compat.h" | |||
#define DEFAULT_HOST "192.168.1.209" | |||
#define UE9_COMMAND_PORT 52360 | |||
#define UE9_DATA_PORT 52361 | |||
struct callbackInfo { | |||
struct ue9Calibration calib; | |||
int convert; | |||
int maxlines; | |||
}; | |||
struct options opt[] = { | |||
{ 'a', "address", "string", "host/address of UE9 (192.168.1.209)" }, | |||
{ 'n', "numchannels", "n", "sample the first N ADC channels (2)" }, | |||
{ 'N', "nerdjack", NULL, "Use NerdJack device instead" }, | |||
{ 'd', "detect", NULL, "Detect NerdJack IP address" }, | |||
{ 'p', "precision", "0-3", "Set precision on NerdJack (0 - max range, 1 - max precision)"}, | |||
{ 'C', "channels", "a,b,c", "sample channels a, b, and c" }, | |||
{ 'r', "rate", "hz", "sample each channel at this rate (8000.0)" }, | |||
{ 'o', "oneshot", NULL, "don't retry in case of errors" }, | |||
{ 'f', "forceretry", NULL, "retry no matter what happens" }, | |||
{ 'c', "convert", NULL, "display output in volts" }, | |||
{ 'l', "lines", "num", "if set, output this many lines and quit" }, | |||
{ 'h', "help", NULL, "this help" }, | |||
{ 'v', "verbose", NULL, "be verbose" }, | |||
{ 'V', "version", NULL, "show version number and exit" }, | |||
{ 0, NULL, NULL, NULL } | |||
}; | |||
int doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval, | |||
int *channel_list, int channel_count, int convert, int maxlines); | |||
int nerdDoStream(const char *address, int *channel_list, int channel_count, int precision, | |||
unsigned short period, int convert, int lines); | |||
int data_callback(int channels, uint16_t *data, void *context); | |||
int columns_left = 0; | |||
void handle_sig(int sig) | |||
{ | |||
while (columns_left--) { | |||
printf(" 0"); | |||
} | |||
fflush(stdout); | |||
exit(0); | |||
} | |||
int main(int argc, char *argv[]) | |||
{ | |||
int optind; | |||
char *optarg, *endp; | |||
char c; | |||
int tmp, i; | |||
FILE *help = stderr; | |||
char *address = strdup(DEFAULT_HOST); | |||
double desired_rate = 8000.0; | |||
int lines = 0; | |||
double actual_rate; | |||
int oneshot = 0; | |||
int forceretry = 0; | |||
int convert = 0; | |||
uint8_t scanconfig; | |||
uint16_t scaninterval; | |||
#if UE9_CHANNELS > NERDJACK_CHANNELS | |||
int channel_list[UE9_CHANNELS]; | |||
#else | |||
int channel_list[NERDJACK_CHANNELS]; | |||
#endif | |||
int channel_count = 0; | |||
int nerdjack = 0; | |||
int detect = 0; | |||
int precision = 0; | |||
int period = NERDJACK_CLOCK_RATE / desired_rate; | |||
/* 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 'n': | |||
channel_count = 0; | |||
tmp = strtol(optarg, &endp, 0); | |||
if (*endp || tmp < 1 || tmp > UE9_CHANNELS) { | |||
info("bad number of channels: %s\n", optarg); | |||
goto printhelp; | |||
} | |||
for (i = 0; i < tmp; i++) | |||
channel_list[channel_count++] = i; | |||
break; | |||
case 'C': | |||
channel_count = 0; | |||
do { | |||
tmp = strtol(optarg, &endp, 0); | |||
if (*endp != '\0' && *endp != ',') { | |||
//|| tmp < 0 || tmp >= UE9_CHANNELS) { | |||
info("bad channel number: %s\n", optarg); | |||
goto printhelp; | |||
} | |||
//We do not want to overflow channel_list, so we need the check here | |||
//The rest of the sanity checking can come later after we know whether this is a | |||
//LabJack or a NerdJack | |||
#if UE9_CHANNELS > NERDJACK_CHANNELS | |||
if (channel_count >= UE9_CHANNELS) { | |||
#else | |||
if (channel_count >= NERDJACK_CHANNELS) { | |||
#endif | |||
info("error: too many channels specified\n"); | |||
goto printhelp; | |||
} | |||
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 'p': | |||
precision++; | |||
break; | |||
case 'N': | |||
nerdjack++; | |||
break; | |||
case 'd': | |||
detect++; | |||
break; | |||
case 'o': | |||
oneshot++; | |||
break; | |||
case 'f': | |||
forceretry++; | |||
break; | |||
case 'c': | |||
convert++; | |||
break; | |||
case 'v': | |||
verb_count++; | |||
break; | |||
case 'V': | |||
printf("ljstream " 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, "Read data from the specified Labjack UE9" | |||
" via Ethernet. See README for details.\n"); | |||
return (help == stdout) ? 0 : 1; | |||
} | |||
} | |||
if (nerdjack) { | |||
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 (channel_list[i] >= UE9_CHANNELS) { | |||
info("Channel is out of LabJack range: %d\n",channel_list[i]); | |||
goto printhelp; | |||
} | |||
} | |||
} | |||
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 (nerdjack) { | |||
if (nerdjack_choose_scan(desired_rate, &actual_rate, &period) < 0) { | |||
info("error: can't achieve requested scan rate (%lf Hz)\n", | |||
desired_rate); | |||
return 1; | |||
} | |||
} else { | |||
if (ue9_choose_scan(desired_rate, &actual_rate, | |||
&scanconfig, &scaninterval) < 0) { | |||
info("error: can't achieve requested scan rate (%lf Hz)\n", | |||
desired_rate); | |||
return 1; | |||
} | |||
} | |||
if ((desired_rate != actual_rate) || verb_count) | |||
info("Actual scanrate is %lf Hz\n", actual_rate); | |||
if (verb_count && lines) { | |||
info("Stopping capture after %d lines\n", lines); | |||
} | |||
signal(SIGINT, handle_sig); | |||
signal(SIGTERM, handle_sig); | |||
if (detect) { | |||
info("Autodetecting NerdJack address\n"); | |||
free(address); | |||
if(nerdjack_detect(address) < 0) { | |||
info("Error with autodetection\n"); | |||
} else { | |||
info("Found NerdJack at address: %s\n",address); | |||
} | |||
} | |||
for (;;) { | |||
int ret; | |||
if(nerdjack) { | |||
ret = nerdDoStream(address, channel_list, channel_count, precision, period, convert, lines); | |||
verb("nerdDoStream returned %d\n", ret); | |||
} else { | |||
ret = doStream(address, scanconfig, scaninterval, | |||
channel_list, channel_count, convert, | |||
lines); | |||
verb("doStream returned %d\n", ret); | |||
} | |||
if (oneshot) | |||
break; | |||
if (ret == 0) | |||
break; | |||
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 nerdDoStream(const char *address, int *channel_list, int channel_count, int precision, | |||
unsigned short period, int convert, int lines) | |||
{ | |||
int retval = -EAGAIN; | |||
int fd_data; | |||
static int first_call = 1; | |||
char command[13]; | |||
/* Open connection. If this fails, and this is the | |||
first attempt, return a different error code so we give up. */ | |||
fd_data = nerd_open(address, NERDJACK_DATA_PORT); | |||
if (fd_data < 0) { | |||
info("Connect failed: %s:%d\n", address, NERDJACK_DATA_PORT); | |||
if (first_call) | |||
retval = -ENOTCONN; | |||
goto out; | |||
} | |||
first_call = 0; | |||
if (nerd_generate_command(command, channel_list, channel_count, precision, period) < 0) { | |||
info("Failed to create configuration command\n"); | |||
goto out1; | |||
} | |||
if (nerd_data_stream(fd_data, command, channel_count, channel_list, precision, convert, lines) < 0) { | |||
info("Failed to open data stream\n"); | |||
goto out1; | |||
} | |||
info("Stream finished\n"); | |||
retval = 0; | |||
out1: | |||
nerd_close_conn(fd_data); | |||
out: | |||
return retval; | |||
} | |||
int doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval, | |||
int *channel_list, int channel_count, int convert, int lines) | |||
{ | |||
int retval = -EAGAIN; | |||
int fd_cmd, fd_data; | |||
int ret; | |||
static int first_call = 1; | |||
struct callbackInfo ci = { | |||
.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 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++) { | |||
if (ci->convert) | |||
printf("%lf", ue9_binary_to_analog( | |||
&ci->calib, UE9_BIPOLAR_GAIN1, 12, | |||
data[i])); | |||
else | |||
printf("%d", data[i]); | |||
columns_left--; | |||
if (i < (channels - 1)) { | |||
putchar(' '); | |||
} else { | |||
putchar('\n'); | |||
lines++; | |||
if (ci->maxlines && lines >= ci->maxlines) | |||
return -1; | |||
} | |||
} | |||
return 0; | |||
} |
@@ -0,0 +1,98 @@ | |||
/* | |||
* 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; | |||
} | |||
@@ -0,0 +1,67 @@ | |||
/* | |||
* 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; | |||
} |
@@ -0,0 +1,331 @@ | |||
/* | |||
* 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 <errno.h> | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include <unistd.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <sys/types.h> | |||
#include <math.h> | |||
#include "netutil.h" | |||
#include "compat.h" | |||
#include "debug.h" | |||
#include "nerdjack.h" | |||
#include "util.h" | |||
#include "netutil.h" | |||
#define NERDJACK_TIMEOUT 5 /* Timeout for connect/send/recv, in seconds */ | |||
/* Choose the best ScanConfig and ScanInterval parameters for the | |||
desired scanrate. Returns -1 if no valid config found */ | |||
int nerdjack_choose_scan(double desired_rate, double *actual_rate, int *period) | |||
{ | |||
*period = round((double) NERDJACK_CLOCK_RATE / desired_rate); | |||
* actual_rate = (double) NERDJACK_CLOCK_RATE / (double) *period; | |||
if(*actual_rate != desired_rate) { | |||
return -1; | |||
} | |||
return 0; | |||
} | |||
int nerdjack_detect(char * ipAddress) { | |||
int32_t sock, receivesock; | |||
struct sockaddr_in sa, receiveaddr, sFromAddr; | |||
int bytes_sent, buffer_length; | |||
char buffer[200]; | |||
char incomingData[10]; | |||
unsigned int lFromLen; | |||
sprintf(buffer, "TEST"); | |||
buffer_length = strlen(buffer) + 1; | |||
net_init(); | |||
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); | |||
receivesock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); | |||
/* Set nonblocking */ | |||
if (soblock(sock, 0) < 0) { | |||
verb("can't set nonblocking\n"); | |||
return -1; | |||
} | |||
/* Set nonblocking */ | |||
if (soblock(receivesock, 0) < 0) { | |||
verb("can't set nonblocking\n"); | |||
return -1; | |||
} | |||
int opt = 1; | |||
setsockopt(sock,SOL_SOCKET,SO_BROADCAST,(void *) &opt,sizeof(int)); | |||
if((-1 == sock) || (-1 == receivesock)) /* if socket failed to initialize, exit */ | |||
{ | |||
printf("Error Creating Socket\n"); | |||
return -1; | |||
} | |||
//Setup family for both sockets | |||
sa.sin_family = PF_INET; | |||
receiveaddr.sin_family = PF_INET; | |||
//Setup ports to send on DATA and receive on RECEIVE | |||
receiveaddr.sin_port = htons(NERDJACK_UDP_RECEIVE_PORT); | |||
sa.sin_port = htons(NERDJACK_DATA_PORT); | |||
//Receive from any IP address, Will send to broadcast | |||
receiveaddr.sin_addr.s_addr = INADDR_ANY; | |||
sa.sin_addr.s_addr = INADDR_BROADCAST; | |||
bind(receivesock,(struct sockaddr*) &receiveaddr, sizeof(struct sockaddr_in)); | |||
bytes_sent = sendto(sock, buffer, buffer_length, 0,(struct sockaddr*) &sa, sizeof(struct sockaddr_in) ); | |||
if(bytes_sent < 0) { | |||
printf("Error sending packet: %s\n", strerror(errno) ); | |||
return -1; | |||
} | |||
lFromLen = sizeof(sFromAddr); | |||
if(0 > recvfrom_timeout(receivesock, incomingData, sizeof(incomingData),0,(struct sockaddr *) &sFromAddr, &lFromLen, | |||
& (struct timeval) { .tv_sec = NERDJACK_TIMEOUT })) { | |||
return -1; | |||
} | |||
ipAddress = malloc(INET_ADDRSTRLEN); | |||
//It isn't ipv6 friendly, but inet_ntop isn't on Windows... | |||
strcpy(ipAddress, inet_ntoa(sFromAddr.sin_addr)); | |||
close(sock); /* close the socket */ | |||
close(receivesock); | |||
return 0; | |||
} | |||
int nerd_data_stream(int data_fd, char * command, int numChannels, int *channel_list, int precision, int convert, int lines) | |||
{ | |||
unsigned char buf[NERDJACK_PACKET_SIZE]; | |||
int numGroups = NERDJACK_NUM_SAMPLES / numChannels; | |||
int index = 0; | |||
int totalread = 0; | |||
int ret = 0; | |||
int alignment = 0; | |||
signed short datapoint = 0; | |||
signed short dataline[NERDJACK_CHANNELS]; | |||
long double voltline[NERDJACK_CHANNELS]; | |||
int destination[NERDJACK_CHANNELS]; | |||
unsigned short currentcount = 0; | |||
unsigned long memused = 0; | |||
unsigned short packetsready = 0; | |||
unsigned short adcused = 0; | |||
unsigned short tempshort = 0; | |||
int charsread = 0; | |||
int charsleft = 0; | |||
int additionalread = 0; | |||
int linesleft = lines; | |||
int numgroups = 0; | |||
long double volts; | |||
int channels_left = numChannels; | |||
int channelprocessing = 0; | |||
int currentalign = 0; | |||
int i; | |||
//Loop through channel_list until all channels recognized | |||
//destination holds the index where each channel should go for reordering | |||
do { | |||
for(i = 0; i < numChannels; i++) { | |||
if(channelprocessing == channel_list[i]) { | |||
destination[currentalign] = i; | |||
currentalign++; | |||
channels_left--; | |||
break; | |||
} | |||
} | |||
channelprocessing++; | |||
} while(channels_left > 0); | |||
/* Send request */ | |||
ret = send_all_timeout(data_fd, command, strlen(command), 0, | |||
& (struct timeval) { .tv_sec = NERDJACK_TIMEOUT }); | |||
if (ret < 0 || ret != strlen(command)) { | |||
verb("short send %d\n", (int)ret); | |||
return -1; | |||
} | |||
//Loop forever to grab data | |||
while((charsread = recv_all_timeout(data_fd,buf,NERDJACK_PACKET_SIZE,0, | |||
& (struct timeval) { .tv_sec = NERDJACK_TIMEOUT }))){ | |||
//We want a complete packet, so take the chars so far and keep waiting | |||
if(charsread != NERDJACK_PACKET_SIZE) { | |||
charsleft = NERDJACK_PACKET_SIZE - charsread; | |||
while(charsleft != 0){ | |||
additionalread = recv_all_timeout(data_fd,buf+charsread,charsleft,0, | |||
& (struct timeval) { .tv_sec = NERDJACK_TIMEOUT }); | |||
charsread = charsread + additionalread; | |||
charsleft = NERDJACK_PACKET_SIZE - charsread; | |||
} | |||
} | |||
//First check the header info | |||
if(buf[0] != 0xF0 || buf[1] != 0xAA) { | |||
printf("No Header info\n"); | |||
return -1; | |||
} | |||
//Check counter info to make sure not out of order | |||
tempshort = (buf[2] << 8) | buf[3]; | |||
if(tempshort != currentcount ){ | |||
printf("Count wrong. Expected %hd but got %hd\n", currentcount, tempshort); | |||
return -1; | |||
} | |||
//Increment number of packets received | |||
currentcount++; | |||
//Process the rest of the header and update the index value to be pointing after it | |||
index = 12; | |||
memused = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | (buf[7]); | |||
adcused = (buf[8] << 8) | (buf[9]); | |||
packetsready = (buf[10] << 8) | (buf[11]); | |||
alignment = 0; | |||
numgroups = 0; | |||
//While there is still more data in the packet, process it | |||
while(charsread > index) { | |||
datapoint = (buf[index] << 8 | buf[index+1]); | |||
if(convert) { | |||
if(alignment <= 5) { | |||
volts = (long double) ( datapoint / 32767.0 ) * ((precision & 0x01) ? 5.0 : 10.0); | |||
} else { | |||
volts = (long double) (datapoint / 32767.0 ) * ((precision & 0x02) ? 5.0 : 10.0); | |||
} | |||
voltline[destination[alignment]] = volts; | |||
} else { | |||
dataline[destination[alignment]] = datapoint; | |||
} | |||
//Each point is two bytes, so increment index and total bytes read | |||
index++; | |||
index++; | |||
totalread++; | |||
alignment++; | |||
//Since channel data is packed, we need to know when to insert a newline | |||
if(alignment == numChannels){ | |||
if(convert) { | |||
for(i = 0; i < numChannels; i++) { | |||
printf("%Lf ",voltline[i]); | |||
} | |||
} else { | |||
for(i = 0; i < numChannels; i++) { | |||
printf("%hd ",dataline[i]); | |||
} | |||
} | |||
printf("\n"); | |||
alignment = 0; | |||
numgroups++; | |||
if(lines != 0) { | |||
linesleft--; | |||
if(linesleft == 0) { | |||
return 0; | |||
} | |||
} | |||
//If numgroups so far is equal to the numGroups in a packet, this packet is done | |||
if(numgroups == numGroups) { | |||
break; | |||
} | |||
} | |||
} | |||
index = 0; | |||
} | |||
return 0; | |||
} | |||
int nerd_open(const char *address,int port) { | |||
struct hostent *he; | |||
net_init(); | |||
int32_t i32SocketFD = socket(PF_INET, SOCK_STREAM, 0); | |||
if(-1 == i32SocketFD) | |||
{ | |||
printf("cannot create socket"); | |||
return -1; | |||
} | |||
/* Set nonblocking */ | |||
if (soblock(i32SocketFD, 0) < 0) { | |||
verb("can't set nonblocking\n"); | |||
return -1; | |||
} | |||
struct sockaddr_in stSockAddr; | |||
memset(&stSockAddr, 0, sizeof(stSockAddr)); | |||
stSockAddr.sin_family = AF_INET; | |||
stSockAddr.sin_port = htons(port); | |||
he = gethostbyname(address); | |||
if (he == NULL) { | |||
verb("gethostbyname(\"%s\") failed\n", address); | |||
return -1; | |||
} | |||
stSockAddr.sin_addr = *((struct in_addr *) he->h_addr); | |||
debug("Resolved %s -> %s\n", address, inet_ntoa(stSockAddr.sin_addr)); | |||
/* Connect */ | |||
if (connect_timeout(i32SocketFD, (struct sockaddr *) &stSockAddr, sizeof(stSockAddr), | |||
& (struct timeval) { .tv_sec = NERDJACK_TIMEOUT }) < 0) { | |||
verb("connection to %s:%d failed: %s\n", | |||
inet_ntoa(stSockAddr.sin_addr), port, compat_strerror(errno)); | |||
return -1; | |||
} | |||
return i32SocketFD; | |||
} | |||
int nerd_generate_command(char * command, int * channel_list, int channel_count, int precision, | |||
unsigned short period) { | |||
int channelbit = 0; | |||
int i; | |||
for( i = 0; i < channel_count; i++) { | |||
channelbit = channelbit | (0x1 << channel_list[i]); | |||
} | |||
sprintf(command,"GET%3.3X%d%5.5d", channelbit,precision,period); | |||
return 0; | |||
} | |||
int nerd_close_conn(int data_fd) | |||
{ | |||
shutdown(data_fd, 2); | |||
close(data_fd); | |||
return 0; | |||
} |
@@ -0,0 +1,44 @@ | |||
/* | |||
* 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. | |||
*/ | |||
#ifndef NERDJACK_H | |||
#define NERDJACK_H | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include "netutil.h" | |||
#define NERDJACK_CHANNELS 12 | |||
#define NERDJACK_CLOCK_RATE 54000000 | |||
#define NERDJACK_DATA_PORT 49155 | |||
#define NERDJACK_UDP_RECEIVE_PORT 49156 | |||
#define NERDJACK_PACKET_SIZE 1460 | |||
#define NERDJACK_NUM_SAMPLES 724 | |||
/* Open/close TCP/IP connection to the NerdJack */ | |||
int nerd_open(const char *address,int port); | |||
int nerd_close_conn(int data_fd); | |||
/* Generate the command word for the NerdJack */ | |||
int nerd_generate_command(char * command, int * channel_list, int channel_count, int precision, | |||
unsigned short period); | |||
/* Stream data out of the NerdJack */ | |||
int nerd_data_stream(int data_fd, char * command, int numChannels, int * channel_list, int precision, int convert, int lines); | |||
/* Detect the IP Address of the NerdJack and return in ipAddress */ | |||
int nerdjack_detect(char * ipAddress); | |||
/* Choose the best ScanConfig and ScanInterval parameters for the | |||
desired scanrate. Returns -1 if no valid config found */ | |||
int nerdjack_choose_scan(double desired_rate, double *actual_rate, int *period); | |||
#endif |
@@ -0,0 +1,243 @@ | |||
#include "netutil.h" | |||
#include "compat.h" | |||
#include <errno.h> | |||
#include <sys/types.h> | |||
#include <stdio.h> | |||
/* Initialize networking */ | |||
void net_init(void) | |||
{ | |||
#ifdef __WIN32__ | |||
WSADATA blah; | |||
WSAStartup(0x0101, &blah); | |||
#endif | |||
} | |||
/* Set socket blocking/nonblocking */ | |||
int soblock(int socket, int blocking) | |||
{ | |||
#ifdef __WIN32__ | |||
unsigned long arg = blocking ? 0 : 1; | |||
if (ioctlsocket(socket, FIONBIO, &arg) != 0) | |||
return -1; | |||
return 0; | |||
#else | |||
int sockopt; | |||
/* Get flags */ | |||
sockopt = fcntl(socket, F_GETFL); | |||
if (sockopt == -1) { | |||
return -1; | |||
} | |||
/* Modify */ | |||
if (blocking) | |||
sockopt &= ~O_NONBLOCK; | |||
else | |||
sockopt |= O_NONBLOCK; | |||
/* Set flags */ | |||
if (fcntl(socket, F_SETFL, sockopt) != 0) | |||
return -1; | |||
return 0; | |||
#endif | |||
} | |||
/* Like connect(2), but with a timeout. Socket must be non-blocking. */ | |||
int connect_timeout(int s, const struct sockaddr *serv_addr, socklen_t addrlen, | |||
struct timeval *timeout) | |||
{ | |||
int ret; | |||
fd_set writefds; | |||
fd_set exceptfds; | |||
int optval; | |||
socklen_t optlen; | |||
/* Start connect */ | |||
ret = connect(s, serv_addr, addrlen); | |||
if (ret == 0) { | |||
/* Success */ | |||
return 0; | |||
} | |||
/* Check for immediate failure */ | |||
#ifdef __WIN32__ | |||
errno = WSAGetLastError(); | |||
if (ret < 0 && errno != WSAEWOULDBLOCK && errno != WSAEINVAL) | |||
return -1; | |||
#else | |||
if (ret < 0 && errno != EINPROGRESS && errno != EALREADY) | |||
return -1; | |||
#endif | |||
/* In progress, wait for result. */ | |||
FD_ZERO(&writefds); | |||
FD_SET(s, &writefds); | |||
FD_ZERO(&exceptfds); | |||
FD_SET(s, &exceptfds); | |||
ret = select(s + 1, NULL, &writefds, &exceptfds, timeout); | |||
if (ret < 0) { | |||
/* Error */ | |||
return -1; | |||
} | |||
if (ret == 0) { | |||
/* Timed out */ | |||
errno = ETIMEDOUT; | |||
return -1; | |||
} | |||
/* Check the socket state */ | |||
optlen = sizeof(optval); | |||
if (getsockopt(s, SOL_SOCKET, SO_ERROR, (void *)&optval, &optlen) != 0) | |||
return -1; | |||
if (optval != 0) { | |||
/* 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)) { | |||
errno = EIO; | |||
return -1; | |||
} | |||
/* Success */ | |||
return 0; | |||
} | |||
/* 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 | |||
may still send less than requested. */ | |||
ssize_t send_timeout(int s, const void *buf, size_t len, int flags, | |||
struct timeval *timeout) | |||
{ | |||
fd_set writefds; | |||
int ret; | |||
FD_ZERO(&writefds); | |||
FD_SET(s, &writefds); | |||
ret = select(s + 1, NULL, &writefds, NULL, timeout); | |||
if (ret == 0) { | |||
/* Timed out */ | |||
errno = ETIMEDOUT; | |||
return -1; | |||
} | |||
if (ret != 1) { | |||
/* Error */ | |||
return -1; | |||
} | |||
return send(s, buf, len, flags); | |||
} | |||
/* Like recv(2), but with a timeout. Socket must be non-blocking. | |||
The timeout only applies if no data at all is received -- this | |||
function may still return less than requested. */ | |||
ssize_t recv_timeout(int s, void *buf, size_t len, int flags, | |||
struct timeval *timeout) | |||
{ | |||
fd_set readfds; | |||
int ret; | |||
FD_ZERO(&readfds); | |||
FD_SET(s, &readfds); | |||
ret = select(s + 1, &readfds, NULL, NULL, timeout); | |||
if (ret == 0) { | |||
/* Timed out */ | |||
errno = ETIMEDOUT; | |||
return -1; | |||
} | |||
if (ret != 1) { | |||
/* Error */ | |||
return -1; | |||
} | |||
return recv(s, buf, len, flags); | |||
} | |||
/* Like recvfrom(2), but with a timeout. Socket must be non-blocking. | |||
The timeout only applies if no data at all is received -- this | |||
function may still return less than requested. */ | |||
ssize_t recvfrom_timeout(int s, void *buf, size_t len, int flags, struct sockaddr *address, socklen_t *address_len, | |||
struct timeval *timeout) | |||
{ | |||
fd_set readfds; | |||
int ret; | |||
FD_ZERO(&readfds); | |||
FD_SET(s, &readfds); | |||
ret = select(s + 1, &readfds, NULL, NULL, timeout); | |||
if (ret == 0) { | |||
/* Timed out */ | |||
errno = ETIMEDOUT; | |||
return -1; | |||
} | |||
if (ret != 1) { | |||
/* Error */ | |||
return -1; | |||
} | |||
return recvfrom(s, buf, len, flags, address, address_len); | |||
} | |||
/* Like send_timeout, but retries (with the same timeout) in case of | |||
partial transfers. This is a stronger attempt to send all | |||
requested data. */ | |||
ssize_t send_all_timeout(int s, const void *buf, size_t len, int flags, | |||
struct timeval *timeout) | |||
{ | |||
struct timeval tv; | |||
size_t left = len; | |||
ssize_t ret; | |||
while (left > 0) { | |||
tv.tv_sec = timeout->tv_sec; | |||
tv.tv_usec = timeout->tv_usec; | |||
ret = send_timeout(s, buf, left, flags, &tv); | |||
if (ret < 0) | |||
return ret; | |||
if (ret == 0) | |||
break; | |||
left -= ret; | |||
buf += ret; | |||
} | |||
return len - left; | |||
} | |||
/* Like recv_timeout, but retries (with the same timeout) in case of | |||
partial transfers. This is a stronger attempt to recv all | |||
requested data. */ | |||
ssize_t recv_all_timeout(int s, void *buf, size_t len, int flags, | |||
struct timeval *timeout) | |||
{ | |||
struct timeval tv; | |||
size_t left = len; | |||
ssize_t ret; | |||
while (left > 0) { | |||
tv.tv_sec = timeout->tv_sec; | |||
tv.tv_usec = timeout->tv_usec; | |||
ret = recv_timeout(s, buf, left, flags, &tv); | |||
if (ret < 0) | |||
return ret; | |||
if (ret == 0) | |||
break; | |||
left -= ret; | |||
buf += ret; | |||
} | |||
return len - left; | |||
} |
@@ -0,0 +1,45 @@ | |||
#ifndef NETUTIL_H | |||
#define NETUTIL_H | |||
#include <sys/types.h> | |||
#include <sys/time.h> | |||
#include <fcntl.h> | |||
#ifdef __WIN32__ | |||
# include <winsock2.h> | |||
# include <ws2tcpip.h> | |||
# define socklen_t int | |||
# define in_addr_t uint32_t | |||
# define in_port_t uint16_t | |||
#else | |||
# include <sys/socket.h> | |||
# include <netinet/in.h> | |||
# include <arpa/inet.h> | |||
# include <netdb.h> | |||
#endif | |||
/* Initialize networking */ | |||
void net_init(void); | |||
/* Set socket blocking/nonblocking */ | |||
int soblock(int socket, int blocking); | |||
/* Like send(2), recv(2), connect(2), but with timeouts. | |||
Socket must be O_NONBLOCK. */ | |||
int connect_timeout(int s, const struct sockaddr *serv_addr, socklen_t addrlen, | |||
struct timeval *timeout); | |||
ssize_t send_timeout(int s, const void *buf, size_t len, int flags, | |||
struct timeval *timeout); | |||
ssize_t recv_timeout(int s, void *buf, size_t len, int flags, | |||
struct timeval *timeout); | |||
ssize_t recvfrom_timeout(int s, void *buf, size_t len, int flags, struct sockaddr *address, socklen_t *address_len, | |||
struct timeval *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. */ | |||
ssize_t send_all_timeout(int s, const void *buf, size_t len, int flags, | |||
struct timeval *timeout); | |||
ssize_t recv_all_timeout(int s, void *buf, size_t len, int flags, | |||
struct timeval *timeout); | |||
#endif |
@@ -0,0 +1,95 @@ | |||
/* | |||
* 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 <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "opt.h" | |||
void opt_init(int *optind) { | |||
*optind=0; | |||
} | |||
char opt_parse(int argc, char **argv, int *optind, char **optarg, | |||
struct options *opt) { | |||
char c; | |||
int i; | |||
(*optind)++; | |||
if(*optind>=argc) | |||
return 0; | |||
if(argv[*optind][0]=='-' && | |||
argv[*optind][1]!='-' && | |||
argv[*optind][1]!=0) { | |||
/* Short option (or a bunch of 'em) */ | |||
/* Save this and shift others over */ | |||
c=argv[*optind][1]; | |||
for(i=2;argv[*optind][i]!=0;i++) | |||
argv[*optind][i-1]=argv[*optind][i]; | |||
argv[*optind][i-1]=0; | |||
if(argv[*optind][1]!=0) | |||
(*optind)--; | |||
/* Now find it */ | |||
for(i=0;opt[i].shortopt!=0;i++) | |||
if(opt[i].shortopt==c) | |||
break; | |||
if(opt[i].shortopt==0) { | |||
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; | |||
} | |||
} | |||
void opt_help(struct options *opt, FILE *out) { | |||
int i; | |||
int printed; | |||
for(i=0;opt[i].shortopt!=0;i++) { | |||
fprintf(out," -%c, --%s%n",opt[i].shortopt, | |||
opt[i].longopt,&printed); | |||
fprintf(out," %-*s%s\n",30-printed, | |||
opt[i].arg?opt[i].arg:"",opt[i].help); | |||
} | |||
} |
@@ -0,0 +1,28 @@ | |||
/* | |||
* 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. | |||
*/ | |||
#ifndef OPT_H | |||
#define OPT_H | |||
#include <stdlib.h> | |||
struct options { | |||
char shortopt; | |||
char *longopt; | |||
char *arg; | |||
char *help; | |||
}; | |||
void opt_init(int *optind); | |||
char opt_parse(int argc, char **argv, int *optind, char **optarg, | |||
struct options *opt); | |||
void opt_help(struct options *opt, FILE *out); | |||
#endif |
@@ -0,0 +1,653 @@ | |||
/* | |||
* 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 <errno.h> | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include <unistd.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <sys/types.h> | |||
#include <math.h> | |||
#include "netutil.h" | |||
#include "compat.h" | |||
#include "debug.h" | |||
#include "ue9.h" | |||
#include "ue9error.h" | |||
#include "util.h" | |||
#include "netutil.h" | |||
#define UE9_TIMEOUT 5 /* Timeout for connect/send/recv, in seconds */ | |||
/* Fill checksums in data buffers, with "normal" checksum format */ | |||
void ue9_checksum_normal(uint8_t *buffer, size_t len) | |||
{ | |||
uint16_t sum = 0; | |||
if (len < 1) { | |||
fprintf(stderr, "ue9_checksum_normal: len too short\n"); | |||
exit(1); | |||
} | |||
while (--len >= 1) | |||
sum += (uint16_t) buffer[len]; | |||
sum = (sum / 256) + (sum % 256); | |||
sum = (sum / 256) + (sum % 256); | |||
buffer[0] = (uint8_t) sum; | |||
} | |||
/* Fill checksums in data buffers, with "extended" checksum format */ | |||
void ue9_checksum_extended(uint8_t *buffer, size_t len) | |||
{ | |||
uint16_t sum = 0; | |||
if (len < 6) { | |||
fprintf(stderr, "ue9_checksum_extended: len too short\n"); | |||
exit(1); | |||
} | |||
/* 16-bit extended checksum */ | |||
while (--len >= 6) | |||
sum += (uint16_t) buffer[len]; | |||
buffer[4] = (uint8_t) (sum & 0xff); | |||
buffer[5] = (uint8_t) (sum >> 8); | |||
/* 8-bit normal checksum over first 6 bytes */ | |||
ue9_checksum_normal(buffer, 6); | |||
} | |||
/* Verify checksums in data buffers, with "normal" checksum format. */ | |||
int ue9_verify_normal(uint8_t *buffer, size_t len) | |||
{ | |||
uint8_t saved, new; | |||
if (len < 1) { | |||
fprintf(stderr, "ue9_verify_normal: len too short\n"); | |||
exit(1); | |||
} | |||
saved = buffer[0]; | |||
ue9_checksum_normal(buffer, len); | |||
new = buffer[0]; | |||
buffer[0] = saved; | |||
if (new != saved) { | |||
verb("got %02x, expected %02x\n", | |||
saved, new); | |||
return 0; | |||
} | |||
return 1; | |||
} | |||
/* Verify checksums in data buffers, with "extended" checksum format. */ | |||
int ue9_verify_extended(uint8_t *buffer, size_t len) | |||
{ | |||
uint8_t saved[3], new[3]; | |||
if (len < 6) { | |||
fprintf(stderr, "ue9_verify_extended: len too short\n"); | |||
exit(1); | |||
} | |||
saved[0] = buffer[0]; | |||
saved[1] = buffer[4]; | |||
saved[2] = buffer[5]; | |||
ue9_checksum_extended(buffer, len); | |||
new[0] = buffer[0]; | |||
new[1] = buffer[4]; | |||
new[2] = buffer[5]; | |||
buffer[0] = saved[0]; | |||
buffer[4] = saved[1]; | |||
buffer[5] = saved[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]); | |||
return 0; | |||
} | |||
return 1; | |||
} | |||
/* Data conversion. If calib is NULL, use uncalibrated conversions. */ | |||
double ue9_binary_to_analog(struct ue9Calibration *calib, | |||
uint8_t gain, uint8_t resolution, uint16_t data) | |||
{ | |||
double slope = 0, offset; | |||
if (calib == NULL) { | |||
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) { | |||
fprintf(stderr, "ue9_binary_to_analog: bad gain\n"); | |||
exit(1); | |||
} | |||
return data * uncal[gain] / 65536.0; | |||
} | |||
if (resolution < 18) { | |||
if (gain <= 3) { | |||
slope = calib->unipolarSlope[gain]; | |||
offset = calib->unipolarOffset[gain]; | |||
} else if (gain == 8) { | |||
slope = calib->bipolarSlope; | |||
offset = calib->bipolarOffset; | |||
} | |||
} else { | |||
if (gain == 0) { | |||
slope = calib->hiResUnipolarSlope; | |||
offset = calib->hiResUnipolarOffset; | |||
} else if (gain == 8) { | |||
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 | |||
checksums on the outgoing packets, and verifies them on the | |||
incoming packets. Data in "out" is transmitted, data in "in" is | |||
received. */ | |||
int ue9_command(int fd, uint8_t *out, uint8_t *in, int inlen) | |||
{ | |||
int extended = 0, outlen; | |||
uint8_t saved_1, saved_3; | |||
ssize_t ret; | |||
if ((out[1] & 0x78) == 0x78) | |||
extended = 1; | |||
/* Figure out length of data payload, and fill checksums. */ | |||
if (extended) { | |||
outlen = 6 + (out[2]) * 2; | |||
ue9_checksum_extended(out, outlen); | |||
} else { | |||
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 }); | |||
if (ret < 0 || ret != outlen) { | |||
verb("short send %d\n", (int)ret); | |||
return -1; | |||
} | |||
/* Save a few bytes that we'll want to compare against later, | |||
in case the caller passed the same buffer twice. */ | |||
saved_1 = out[1]; | |||
if (extended) | |||
saved_3 = out[3]; | |||
/* Receive result */ | |||
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; | |||
} | |||
/* Read a memory block from the device. Returns -1 on error. */ | |||
int ue9_memory_read(int fd, int blocknum, uint8_t *buffer, int len) | |||
{ | |||
uint8_t sendbuf[8], recvbuf[136]; | |||
if (len != 128) { | |||
fprintf(stderr,"ue9_memory_read: buffer length must be 128\n"); | |||
exit(1); | |||
} | |||
/* Request memory block */ | |||
sendbuf[1] = 0xf8; | |||
sendbuf[2] = 0x01; | |||
sendbuf[3] = 0x2a; | |||
sendbuf[6] = 0x00; | |||
sendbuf[7] = blocknum; | |||
if (ue9_command(fd, sendbuf, recvbuf, sizeof(recvbuf)) < 0) { | |||
verb("command failed\n"); | |||
return -1; | |||
} | |||
/* Got it */ | |||
memcpy(buffer, recvbuf + 8, len); | |||
return 0; | |||
} | |||
/* Convert 64-bit fixed point to double type */ | |||
double ue9_fp64_to_double(uint8_t *data) | |||
{ | |||
int32_t a; | |||
uint32_t b; | |||
a = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4]; | |||
b = (data[3] << 24) | (data[2] << 16) | (data[1] << 8) | data[0]; | |||
return (double)a + (double)b / (double)4294967296.0L; | |||
} | |||
/* Retrieve calibration data from the device. Returns -1 on error. */ | |||
int ue9_get_calibration(int fd, struct ue9Calibration *calib) | |||
{ | |||
uint8_t buf[128]; | |||
/* Block 0 */ | |||
if (ue9_memory_read(fd, 0, buf, 128) < 0) return -1; | |||
calib->unipolarSlope[0] = ue9_fp64_to_double(buf + 0); | |||
calib->unipolarOffset[0] = ue9_fp64_to_double(buf + 8); | |||
calib->unipolarSlope[1] = ue9_fp64_to_double(buf + 16); | |||
calib->unipolarOffset[1] = ue9_fp64_to_double(buf + 24); | |||
calib->unipolarSlope[2] = ue9_fp64_to_double(buf + 32); | |||
calib->unipolarOffset[2] = ue9_fp64_to_double(buf + 40); | |||
calib->unipolarSlope[3] = ue9_fp64_to_double(buf + 48); | |||
calib->unipolarOffset[3] = ue9_fp64_to_double(buf + 56); | |||
/* Block 1 */ | |||
if (ue9_memory_read(fd, 1, buf, 128) < 0) return -1; | |||
calib->bipolarSlope = ue9_fp64_to_double(buf + 0); | |||
calib->bipolarOffset = ue9_fp64_to_double(buf + 8); | |||
/* Block 2 */ | |||
if (ue9_memory_read(fd, 2, buf, 128) < 0) return -1; | |||
calib->DACSlope[0] = ue9_fp64_to_double(buf + 0); | |||
calib->DACOffset[0] = ue9_fp64_to_double(buf + 8); | |||
calib->DACSlope[1] = ue9_fp64_to_double(buf + 16); | |||
calib->DACOffset[1] = ue9_fp64_to_double(buf + 24); | |||
calib->tempSlope = ue9_fp64_to_double(buf + 32); | |||
calib->tempSlopeLow = ue9_fp64_to_double(buf + 48); | |||
calib->calTemp = ue9_fp64_to_double(buf + 64); | |||
calib->Vref = ue9_fp64_to_double(buf + 72); | |||
calib->VrefDiv2 = ue9_fp64_to_double(buf + 88); | |||
calib->VsSlope = ue9_fp64_to_double(buf + 96); | |||
/* Block 3 */ | |||
if (ue9_memory_read(fd, 3, buf, 128) < 0) return -1; | |||
calib->hiResUnipolarSlope = ue9_fp64_to_double(buf + 0); | |||
calib->hiResUnipolarOffset = ue9_fp64_to_double(buf + 8); | |||
/* Block 4 */ | |||
if (ue9_memory_read(fd, 4, buf, 128) < 0) return -1; | |||
calib->hiResBipolarSlope = ue9_fp64_to_double(buf + 0); | |||
calib->hiResBipolarOffset = ue9_fp64_to_double(buf + 8); | |||
/* All done */ | |||
return 1; | |||
} | |||
/* Retrieve comm config, returns -1 on error */ | |||
int ue9_get_comm_config(int fd, struct ue9CommConfig *config) | |||
{ | |||
uint8_t sendbuf[18]; | |||
uint8_t recvbuf[24]; | |||
memset(sendbuf, 0, sizeof(sendbuf)); | |||
memset(config, 0, sizeof(struct ue9CommConfig)); | |||
sendbuf[1] = 0xf8; | |||
sendbuf[2] = 0x09; | |||
sendbuf[3] = 0x08; | |||
if (ue9_command(fd, sendbuf, recvbuf, sizeof(recvbuf)) < 0) { | |||
verb("command failed\n"); | |||
return -1; | |||
} | |||
verb("todo\n"); | |||
return -1; | |||
} | |||
/* Retrieve control config, returns -1 on error */ | |||
int ue9_get_control_config(int fd, struct ue9ControlConfig *config) | |||
{ | |||
uint8_t sendbuf[18]; | |||
uint8_t recvbuf[24]; | |||
memset(sendbuf, 0, sizeof(sendbuf)); | |||
memset(config, 0, sizeof(struct ue9ControlConfig)); | |||
sendbuf[1] = 0xf8; | |||
sendbuf[2] = 0x06; | |||
sendbuf[3] = 0x08; | |||
if (ue9_command(fd, sendbuf, recvbuf, sizeof(recvbuf)) < 0) { | |||
verb("command failed\n"); | |||
return -1; | |||
} | |||
verb("todo\n"); | |||
return -1; | |||
} | |||
/* Open TCP/IP connection to the UE9 */ | |||
int ue9_open(const char *host, int port) | |||
{ | |||
int fd; | |||
struct sockaddr_in address; | |||
struct hostent *he; | |||
int window_size = 128 * 1024; | |||
net_init(); | |||
/* Create socket */ | |||
fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); | |||
if (fd < 0) { | |||
verb("socket returned %d\n", fd); | |||
return -1; | |||
} | |||
/* Set nonblocking */ | |||
if (soblock(fd, 0) < 0) { | |||
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, | |||
sizeof(window_size)); | |||
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *)&window_size, | |||
sizeof(window_size)); | |||
/* Resolve host */ | |||
address.sin_family = AF_INET; | |||
address.sin_port = htons(port); | |||
he = gethostbyname(host); | |||
if (he == NULL) { | |||
verb("gethostbyname(\"%s\") failed\n", host); | |||
return -1; | |||
} | |||
address.sin_addr = *((struct in_addr *) he->h_addr); | |||
debug("Resolved %s -> %s\n", host, inet_ntoa(address.sin_addr)); | |||
/* Connect */ | |||
if (connect_timeout(fd, (struct sockaddr *) &address, sizeof(address), | |||
& (struct timeval) { .tv_sec = UE9_TIMEOUT }) < 0) { | |||
verb("connection to %s:%d failed: %s\n", | |||
inet_ntoa(address.sin_addr), port, compat_strerror(errno)); | |||
return -1; | |||
} | |||
return fd; | |||
} | |||
/* Close connection to the UE9 */ | |||
void ue9_close(int fd) | |||
{ | |||
/* does anyone actually call shutdown these days? */ | |||
shutdown(fd, 2 /* SHUT_RDWR */); | |||
close(fd); | |||
} | |||
/* Compute scanrate based on the provided values. */ | |||
double ue9_compute_rate(uint8_t scanconfig, uint16_t scaninterval) | |||
{ | |||
double clock; | |||
/* A "scan" is across all channels. Each scan is triggered at | |||
a fixed rate, and not affected by the number of channels. | |||
Channels are scanned as quickly as possible. */ | |||
switch ((scanconfig >> 3) & 3) { | |||
case 0: clock = 4e6; break; | |||
case 1: clock = 48e6; break; | |||
case 2: clock = 750e3; break; | |||
case 3: clock = 24e6; break; | |||
} | |||
if (scanconfig & 0x2) | |||
clock /= 256; | |||
if (scaninterval == 0) | |||
return 0; | |||
return clock / scaninterval; | |||
} | |||
/* Choose the best ScanConfig and ScanInterval parameters for the | |||
desired scanrate. Returns -1 if no valid config found */ | |||
int ue9_choose_scan(double desired_rate, double *actual_rate, | |||
uint8_t *scanconfig, uint16_t *scaninterval) | |||
{ | |||
int i; | |||
struct { double clock; uint8_t config; } valid[] = { | |||
{ 48e6, 0x08 }, | |||
{ 24e6, 0x18 }, | |||
{ 4e6, 0x00 }, | |||
{ 750e3, 0x10 }, | |||
{ 48e6 / 256, 0x0a }, | |||
{ 24e6 / 256, 0x1a }, | |||
{ 4e6 / 256, 0x02 }, | |||
{ 750e3 / 256, 0x12 }, | |||
{ 0, 0 } }; |