Compare commits
28 Commits
ethstream-
...
master
Author | SHA1 | Date | |
---|---|---|---|
42d83649be | |||
df740e3e00 | |||
c2f43c474b | |||
893debb361 | |||
![]() |
0b1ad7415d | ||
![]() |
ba2565dc0e | ||
35238c57d0 | |||
e1cb6b6ee0 | |||
![]() |
7262486a36 | ||
![]() |
0f71b79b6f | ||
![]() |
d6f01a3497 | ||
![]() |
76143e7eb4 | ||
![]() |
3ba616841e | ||
![]() |
6bc683fb21 | ||
![]() |
640edf6aab | ||
![]() |
e2bf34ed0c | ||
![]() |
56a79c00f6 | ||
![]() |
bb4912a43c | ||
![]() |
16bf9f89ef | ||
![]() |
267e0e32a5 | ||
![]() |
588385bcb1 | ||
![]() |
ced565f372 | ||
![]() |
e6e9d32a54 | ||
![]() |
9cdb4ea77c | ||
![]() |
53ddf346d7 | ||
![]() |
b0be6473b8 | ||
![]() |
9353f2b3f2 | ||
![]() |
b697801e3e |
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
ethstream
|
||||
ethstream.1
|
||||
ethstream.txt
|
||||
ethstream.exe
|
||||
*.obj
|
||||
*.d
|
||||
*.dobj
|
||||
*.o
|
||||
|
||||
|
8
Makefile
8
Makefile
|
@ -14,9 +14,11 @@
|
|||
# Build options
|
||||
|
||||
CFLAGS += -Wall -g #-pg
|
||||
LDFLAGS += -lm #-pg
|
||||
LDFLAGS += #-pg
|
||||
LDLIBS += -lm
|
||||
|
||||
PREFIX = /usr/local
|
||||
MANPATH = ${PREFIX}/man/man1
|
||||
MANPATH = ${PREFIX}/man/man1/
|
||||
BINPATH = ${PREFIX}/bin
|
||||
|
||||
#WINCC = i386-mingw32-gcc
|
||||
|
@ -49,6 +51,7 @@ obj-common = opt.o ue9.o ue9error.o netutil.o debug.o nerdjack.o
|
|||
obj-ethstream = ethstream.o $(obj-common)
|
||||
|
||||
ethstream: $(obj-ethstream)
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
|
||||
|
||||
ethstream.exe: $(obj-ethstream:.o=.obj) compat-win32.obj
|
||||
|
||||
|
@ -65,6 +68,7 @@ ethstream.exe: $(obj-ethstream:.o=.obj) compat-win32.obj
|
|||
|
||||
.PHONY: install
|
||||
install: ethstream.1 ethstream
|
||||
mkdir -p ${BINPATH} ${MANPATH}
|
||||
install -m 0755 ethstream ${BINPATH}
|
||||
install -m 0644 ethstream.1 ${MANPATH}
|
||||
|
||||
|
|
4
README
4
README
|
@ -1,6 +1,8 @@
|
|||
Labjack/Nerdjack Tools
|
||||
by Jim Paris <jim@jtan.com>
|
||||
with modifications by Zach Clifford <zacharyc@mit.edu>
|
||||
with modifications by
|
||||
Zach Clifford <zacharyc@mit.edu>
|
||||
John Donnal <jdonnal@mit.edu>
|
||||
|
||||
These tools are for interacting with the LabJack UE9 or the NerdJack
|
||||
over the Ethernet interface. More information about the UE9 device:
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <stdio.h>
|
||||
#include "compat.h"
|
||||
#include <windows.h>
|
||||
#include "errno.h"
|
||||
|
||||
unsigned int sleep(unsigned int seconds)
|
||||
{
|
||||
|
|
3
compat.h
3
compat.h
|
@ -5,9 +5,6 @@
|
|||
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
|
||||
|
|
18
debug.c
18
debug.c
|
@ -1,6 +1,7 @@
|
|||
#include "debug.h"
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
int verb_count = 0;
|
||||
|
||||
|
@ -8,10 +9,27 @@ int func_fprintf(const char *func, FILE * stream, const char *format, ...)
|
|||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
fprintf(stream, "%ld.%06ld: ", (unsigned long)tv.tv_sec, (unsigned long)tv.tv_usec);
|
||||
fprintf(stream, "%s: ", func);
|
||||
va_start(ap, format);
|
||||
ret = vfprintf(stream, format, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int my_fprintf(FILE * stream, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
fprintf(stream, "%ld.%06ld: ", (unsigned long)tv.tv_sec, (unsigned long)tv.tv_usec);
|
||||
va_start(ap, format);
|
||||
ret = vfprintf(stream, format, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
|
7
debug.h
7
debug.h
|
@ -16,6 +16,8 @@ extern int verb_count;
|
|||
|
||||
int func_fprintf(const char *func, FILE * stream, const char *format,
|
||||
...) __attribute__ ((format(printf, 3, 4)));
|
||||
int my_fprintf(FILE * stream, const char *format,
|
||||
...) __attribute__ ((format(printf, 2, 3)));
|
||||
|
||||
#define debug(x...) ({ \
|
||||
if(verb_count >= 2) \
|
||||
|
@ -28,6 +30,11 @@ int func_fprintf(const char *func, FILE * stream, const char *format,
|
|||
})
|
||||
|
||||
#define info(x...) ({ \
|
||||
if(verb_count >= 0) \
|
||||
my_fprintf(stderr,x); \
|
||||
})
|
||||
|
||||
#define info_no_timestamp(x...) ({ \
|
||||
if(verb_count >= 0) \
|
||||
fprintf(stderr,x); \
|
||||
})
|
||||
|
|
178
ethstream.c
178
ethstream.c
|
@ -47,16 +47,17 @@ struct options opt[] = {
|
|||
{'r', "rate", "hz", "sample each channel at this rate (8000.0)"},
|
||||
|
||||
{'L', "labjack", NULL, "Force LabJack device"},
|
||||
{'t', "timers", "a,b,c", "set LabJack timer modes to a, b, and c"},
|
||||
{'t', "timers", "a[:A],b[:B]", "set LabJack timer modes a,b and optional values A,B"},
|
||||
{'T', "timerdivisor", "n", "set LabJack timer divisor to n"},
|
||||
|
||||
{'N', "nerdjack", NULL, "Force NerdJack device"},
|
||||
{'d', "detect", NULL, "Detect NerdJack IP address"},
|
||||
{'R', "range", "a,b",
|
||||
"Set range on NerdJack for channels 0-5,6-11 to either 5 or 10 (10,10)"},
|
||||
{'g', "gain", "a,b,c", "Set Labjack AIN channel gains: 0,1,2,4,8 in -C channel order"},
|
||||
{'o', "oneshot", NULL, "don't retry in case of errors"},
|
||||
{'f', "forceretry", NULL, "retry no matter what happens"},
|
||||
{'c', "convert", NULL, "convert output to volts"},
|
||||
{'c', "convert", NULL, "convert output to volts/temperature"},
|
||||
{'H', "converthex", NULL, "convert output to hex"},
|
||||
{'m', "showmem", NULL, "output memory stats with data (NJ only)"},
|
||||
{'l', "lines", "num", "if set, output this many lines and quit"},
|
||||
|
@ -70,19 +71,44 @@ struct options opt[] = {
|
|||
|
||||
int 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 *timer_mode_list, int *timer_value_list,
|
||||
int timer_mode_count, int timer_divisor,
|
||||
int *gain_list, int gain_count,
|
||||
int convert, int maxlines);
|
||||
int nerdDoStream(const char *address, int *channel_list, int channel_count,
|
||||
int precision, unsigned long period, int convert, int lines,
|
||||
int showmem);
|
||||
int data_callback(int channels, uint16_t * data, void *context);
|
||||
int data_callback(int channels, int *channel_list, int gain_count, int *gain_list,
|
||||
uint16_t * data, void *context);
|
||||
|
||||
int columns_left = 0;
|
||||
|
||||
////////EXTRA GLOBAL VARS///////////
|
||||
// for clean shutdown //
|
||||
// added by John Donnal 2015 //
|
||||
////////////////////////////////////
|
||||
int fd_cmd, fd_data;
|
||||
int ue9_running = 0; //flag if labjack is currently streaming data
|
||||
|
||||
void handle_sig(int sig)
|
||||
{
|
||||
while (columns_left--) {
|
||||
printf(" 0");
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* added by John Donnal 2015 *
|
||||
* Close out connection to LabJack, firmware glitches *
|
||||
* if the stream is not closed correctly *
|
||||
******************************************************/
|
||||
if(ue9_running==1){
|
||||
printf("Performing clean shutdown of LabJack\n");
|
||||
ue9_stream_stop(fd_cmd);
|
||||
ue9_buffer_flush(fd_cmd);
|
||||
ue9_close(fd_data);
|
||||
ue9_close(fd_cmd);
|
||||
}
|
||||
/******************************************************/
|
||||
fflush(stdout);
|
||||
exit(0);
|
||||
}
|
||||
|
@ -106,8 +132,11 @@ int main(int argc, char *argv[])
|
|||
uint8_t scanconfig;
|
||||
uint16_t scaninterval;
|
||||
int timer_mode_list[UE9_TIMERS];
|
||||
int timer_value_list[UE9_TIMERS];
|
||||
int timer_mode_count = 0;
|
||||
int timer_divisor = 1;
|
||||
int gain_list[MAX_CHANNELS];
|
||||
int gain_count = 0;
|
||||
int channel_list[MAX_CHANNELS];
|
||||
int channel_count = 0;
|
||||
int nerdjack = 0;
|
||||
|
@ -159,11 +188,35 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
while (*endp);
|
||||
break;
|
||||
case 't': /* labjack only */
|
||||
timer_mode_count = 0;
|
||||
case 'g': /* labjack only */
|
||||
gain_count = 0;
|
||||
do {
|
||||
tmp = strtol(optarg, &endp, 0);
|
||||
if (*endp != '\0' && *endp != ',') {
|
||||
info("bad gain number: %s\n",
|
||||
optarg);
|
||||
goto printhelp;
|
||||
}
|
||||
if (gain_count >= MAX_CHANNELS) {
|
||||
info("error: too many gains specified\n");
|
||||
goto printhelp;
|
||||
}
|
||||
if (!(tmp == 0 || tmp == 1 || tmp == 2 || tmp == 3 || tmp == 8)) {
|
||||
info("error: invalid gain specified\n");
|
||||
goto printhelp;
|
||||
}
|
||||
|
||||
gain_list[gain_count++] = tmp;
|
||||
optarg = endp + 1;
|
||||
}
|
||||
while (*endp);
|
||||
break;
|
||||
case 't': /* labjack only */
|
||||
timer_mode_count = 0;
|
||||
do {
|
||||
/* get mode */
|
||||
tmp = strtol(optarg, &endp, 0);
|
||||
if (*endp != '\0' && *endp != ',' && *endp != ':') {
|
||||
info("bad timer mode: %s\n", optarg);
|
||||
goto printhelp;
|
||||
}
|
||||
|
@ -171,7 +224,22 @@ int main(int argc, char *argv[])
|
|||
info("error: too many timers specified\n");
|
||||
goto printhelp;
|
||||
}
|
||||
timer_mode_list[timer_mode_count++] = tmp;
|
||||
timer_mode_list[timer_mode_count] = tmp;
|
||||
|
||||
/* get optional value */
|
||||
if (*endp == ':') {
|
||||
optarg = endp + 1;
|
||||
tmp = strtol(optarg, &endp, 0);
|
||||
if (*endp != '\0' && *endp != ',') {
|
||||
info("bad timer value: %s\n", optarg);
|
||||
goto printhelp;
|
||||
}
|
||||
timer_value_list[timer_mode_count] = tmp;
|
||||
} else {
|
||||
timer_value_list[timer_mode_count] = 0;
|
||||
}
|
||||
|
||||
timer_mode_count++;
|
||||
optarg = endp + 1;
|
||||
}
|
||||
while (*endp);
|
||||
|
@ -266,8 +334,9 @@ int main(int argc, char *argv[])
|
|||
return 0;
|
||||
break;
|
||||
case 'V':
|
||||
printf("etherstream " VERSION "\n");
|
||||
printf("ethstream " VERSION "\n");
|
||||
printf("Written by Jim Paris <jim@jtan.com>\n");
|
||||
printf("and John Donnal <jdonnal@mit.edu>\n");
|
||||
printf("and Zachary Clifford <zacharyc@mit.edu>.\n");
|
||||
printf("This program comes with no warranty and is "
|
||||
"provided under the GPLv2.\n");
|
||||
|
@ -375,6 +444,12 @@ int main(int argc, char *argv[])
|
|||
goto printhelp;
|
||||
}
|
||||
|
||||
/* Individual Analog Channel Gain Set requires Labjack*/
|
||||
if (gain_count && !labjack) {
|
||||
info("Can't use Individual Gain Set on NerdJack\n");
|
||||
goto printhelp;
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
info("error: too many arguments (%s)\n\n", argv[optind]);
|
||||
goto printhelp;
|
||||
|
@ -394,8 +469,8 @@ int main(int argc, char *argv[])
|
|||
if (verb_count) {
|
||||
info("Scanning channels:");
|
||||
for (i = 0; i < channel_count; i++)
|
||||
info(" AIN%d", channel_list[i]);
|
||||
info("\n");
|
||||
info_no_timestamp(" AIN%d", channel_list[i]);
|
||||
info_no_timestamp("\n");
|
||||
}
|
||||
|
||||
/* Figure out actual rate. */
|
||||
|
@ -423,6 +498,11 @@ int main(int argc, char *argv[])
|
|||
signal(SIGINT, handle_sig);
|
||||
signal(SIGTERM, handle_sig);
|
||||
|
||||
#ifdef SIGPIPE /* not on Windows */
|
||||
/* Ignore SIGPIPE so I/O errors to the network device won't kill the process */
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
if (detect) {
|
||||
info("Autodetecting NerdJack address\n");
|
||||
free(address);
|
||||
|
@ -446,7 +526,9 @@ int main(int argc, char *argv[])
|
|||
} else {
|
||||
ret = doStream(address, scanconfig, scaninterval,
|
||||
channel_list, channel_count,
|
||||
timer_mode_list, timer_mode_count, timer_divisor,
|
||||
timer_mode_list, timer_value_list,
|
||||
timer_mode_count, timer_divisor,
|
||||
gain_list, gain_count,
|
||||
convert, lines);
|
||||
verb("doStream returned %d\n", ret);
|
||||
}
|
||||
|
@ -595,11 +677,13 @@ nerdDoStream(const char *address, int *channel_list, int channel_count,
|
|||
int
|
||||
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 *timer_mode_list, int *timer_value_list,
|
||||
int timer_mode_count, int timer_divisor,
|
||||
int *gain_list, int gain_count,
|
||||
int convert, int lines)
|
||||
{
|
||||
int retval = -EAGAIN;
|
||||
int fd_cmd, fd_data;
|
||||
// int fd_cmd, fd_data; *these are now globals so sighandler can use them*
|
||||
int ret;
|
||||
static int first_call = 1;
|
||||
struct callbackInfo ci = {
|
||||
|
@ -638,18 +722,28 @@ doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval,
|
|||
|
||||
/* Set timer configuration */
|
||||
if (timer_mode_count &&
|
||||
ue9_timer_config(fd_cmd, timer_mode_list, timer_mode_count,
|
||||
timer_divisor) < 0) {
|
||||
ue9_timer_config(fd_cmd, timer_mode_list, timer_value_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;
|
||||
if (gain_count) {
|
||||
/* Set stream configuration */
|
||||
if (ue9_streamconfig(fd_cmd, channel_list, channel_count,
|
||||
scanconfig, scaninterval,
|
||||
gain_list, gain_count) < 0) {
|
||||
info("Failed to set stream configuration\n");
|
||||
goto out2;
|
||||
}
|
||||
} else {
|
||||
/* 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 */
|
||||
|
@ -659,8 +753,9 @@ doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval,
|
|||
}
|
||||
|
||||
/* Stream data */
|
||||
ue9_running = 1;
|
||||
ret =
|
||||
ue9_stream_data(fd_data, channel_count, data_callback, (void *)&ci);
|
||||
ue9_stream_data(fd_data, channel_count, channel_list, gain_count, gain_list, data_callback, (void *)&ci);
|
||||
if (ret < 0) {
|
||||
info("Data stream failed with error %d\n", ret);
|
||||
goto out3;
|
||||
|
@ -678,10 +773,11 @@ doStream(const char *address, uint8_t scanconfig, uint16_t scaninterval,
|
|||
out1:
|
||||
ue9_close(fd_cmd);
|
||||
out:
|
||||
ue9_running = 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
int data_callback(int channels, uint16_t * data, void *context)
|
||||
int data_callback(int channels, int *channel_list, int gain_count, int *gain_list, uint16_t * data, void *context)
|
||||
{
|
||||
int i;
|
||||
struct callbackInfo *ci = (struct callbackInfo *)context;
|
||||
|
@ -689,23 +785,35 @@ int data_callback(int channels, uint16_t * data, void *context)
|
|||
|
||||
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)
|
||||
if (ci->convert == CONVERT_VOLTS &&
|
||||
channel_list[i] <= UE9_MAX_ANALOG_CHANNEL) {
|
||||
/* CONVERT_VOLTS */
|
||||
if (i < gain_count)
|
||||
{
|
||||
if (printf("%lf", ue9_binary_to_analog(
|
||||
&ci->calib, gain_list[i],
|
||||
12, data[i])) < 0)
|
||||
goto bad;
|
||||
} else {
|
||||
if (printf("%lf", ue9_binary_to_analog(
|
||||
&ci->calib, 0,
|
||||
12, data[i])) < 0)
|
||||
goto bad;
|
||||
}
|
||||
} else if (ci->convert == CONVERT_VOLTS &&
|
||||
(channel_list[i] == 141 || channel_list[i] == 133)) {
|
||||
/* CONVERT_VOLTS but output temperature */
|
||||
if (printf("%lf", ue9_binary_to_temperature(
|
||||
&ci->calib, data[i])) < 0)
|
||||
goto bad;
|
||||
break;
|
||||
case CONVERT_HEX:
|
||||
} else if (ci->convert == CONVERT_HEX) {
|
||||
/* CONVERT_HEX */
|
||||
if (printf("%04X", data[i]) < 0)
|
||||
goto bad;
|
||||
break;
|
||||
default:
|
||||
case CONVERT_DEC:
|
||||
} else {
|
||||
/* CONVERT_DEC */
|
||||
if (printf("%d", data[i]) < 0)
|
||||
goto bad;
|
||||
break;
|
||||
}
|
||||
columns_left--;
|
||||
if (i < (channels - 1)) {
|
||||
|
|
|
@ -5,4 +5,6 @@
|
|||
#define CONVERT_VOLTS 1
|
||||
#define CONVERT_HEX 2
|
||||
|
||||
#define TIMEOUT 5 /* Timeout for connect/send/recv, in seconds */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
ETHERSTREAM(1) User Commands ETHERSTREAM(1)
|
||||
|
||||
|
||||
|
||||
NAME
|
||||
etherstream - manual page for etherstream 1.2 (2010-02-08)
|
||||
|
||||
SYNOPSIS
|
||||
ethstream [options]
|
||||
-------
|
||||
|
||||
DESCRIPTION
|
||||
-a, --address string
|
||||
host/address of device (192.168.1.209)
|
||||
|
||||
-n, --numchannels n
|
||||
sample the first N ADC channels (2)
|
||||
|
||||
-C, --channels a,b,c
|
||||
sample channels a, b, and c
|
||||
|
||||
-r, --rate hz
|
||||
sample each channel at this rate (8000.0)
|
||||
|
||||
-L, --labjack
|
||||
Force LabJack device
|
||||
|
||||
-t, --timers a,b,c
|
||||
set LabJack timer modes to a, b, and c
|
||||
|
||||
-T, --timerdivisor n
|
||||
set LabJack timer divisor to n
|
||||
|
||||
-N, --nerdjack
|
||||
Force NerdJack device
|
||||
|
||||
-d, --detect
|
||||
Detect NerdJack IP address
|
||||
|
||||
-R, --range a,b
|
||||
Set range on NerdJack for channels 0-5,6-11 to either 5 or 10
|
||||
(10,10)
|
||||
|
||||
-o, --oneshot
|
||||
don’t retry in case of errors
|
||||
|
||||
-f, --forceretry
|
||||
retry no matter what happens
|
||||
|
||||
-c, --convert
|
||||
convert output to volts
|
||||
|
||||
-H, --converthex
|
||||
convert output to hex
|
||||
|
||||
-m, --showmem
|
||||
output memory stats with data (NJ only)
|
||||
|
||||
-l, --lines num
|
||||
if set, output this many lines and quit
|
||||
|
||||
-h, --help
|
||||
this help
|
||||
|
||||
-v, --verbose
|
||||
be verbose
|
||||
|
||||
-V, --version
|
||||
show version number and exit
|
||||
|
||||
-i, --info
|
||||
get info from device (NJ only)
|
||||
|
||||
-X, --examples
|
||||
show ethstream examples and exit
|
||||
|
||||
Read data from the specified Labjack UE9 via Ethernet. See README for
|
||||
details.
|
||||
|
||||
AUTHOR
|
||||
Written by Jim Paris <jim@jtan.com> and Zachary Clifford
|
||||
<zacharyc@mit.edu>. This program comes with no warranty and is pro‐
|
||||
vided under the GPLv2.
|
||||
|
||||
|
||||
|
||||
etherstream 1.2 (2010-02-08) February 2010 ETHERSTREAM(1)
|
32
example.inc
32
example.inc
|
@ -71,4 +71,36 @@ specified, ethstream connects first to 192.168.1.209. It then tries\n\
|
|||
to autodetect the NerdJack. This should find the device if you are on\n\
|
||||
the same network, but it will get confused if there are multiple NerdJacks\n\
|
||||
on the network.\n\
|
||||
\n\
|
||||
Labjack only Timer modes are also avaliable. Read the Labjack UE9 Users Guide\n\
|
||||
for more information. Upto 6 timers of various modes can be specified,\n\
|
||||
they occur on FIO0-FIO5 which are on channels 200-205 respectively in order\n\
|
||||
of specification. For 32 bit timer modes, the MSW should be read from\n\
|
||||
channel 224 imeadiately after the LSW is read from one the timer channel.\n\
|
||||
A clock frequency divisor is specified on a per device basis. For example:\n\
|
||||
\n\
|
||||
ethstream -t 4,12 -T 1 -C 200,224,201\n\
|
||||
\n\
|
||||
This will enable two timers with the fastest system clock divisor (48 MhZ/1)\n\
|
||||
and read the two 16 bit words for timer mode 4 and the single 16 bit word of\n\
|
||||
timer mode 12. These three words will occupy their own columns in the output\n\
|
||||
stream. Digital timer mode channels can be interspersed with analog inouts.\n\
|
||||
\n\
|
||||
Labjack only individual analog input channel gain set is also avaliable.\n\
|
||||
Gain 0 is default on labjack and corresponds to -5.18v to +5.07v. Gain 1 is\n\
|
||||
is -0.01 to +5.07v. Gain 2 is -0.01 to +2.53v. Gain 4 is -0.01 to +1.26v.\n\
|
||||
Gain 8 is -0.01 to +0.62v. Gains on the -g flag should be put in the desired\n\
|
||||
order corresponding to the channels as specified by the -C flag. If there are\n\
|
||||
less gains specified than channels the remainder default to gain 0. Extra gains\n\
|
||||
are ignored. Gains can be specified for digital inputs or timer modes but they\n\
|
||||
are irrelevant. A case where one should do this is if there are dital input\n\
|
||||
channels intersperced within analog input channels; this keeps the order matched\n\
|
||||
up so later analog input channels have the expected gain.\n\
|
||||
\n\
|
||||
ethstream -t 4 -T 1 -C 0,1,200,224,2,3 -g 2,2,0,0,4,4 -c\n\
|
||||
\n\
|
||||
This will set channles 0,1 and 2,3 to gain 2,2 and 4,4, respectively and convert\n\
|
||||
the data to volts using the firmware stored factory calibrated data on the\n\
|
||||
labjack. The digital channels 200 and 224 will remain undisturbed as integers.\n\
|
||||
\n\
|
||||
";
|
||||
|
|
15
nerdjack.c
15
nerdjack.c
|
@ -25,8 +25,6 @@
|
|||
#include "netutil.h"
|
||||
#include "ethstream.h"
|
||||
|
||||
#define NERDJACK_TIMEOUT 5 /* Timeout for connect/send/recv, in seconds */
|
||||
|
||||
#define NERD_HEADER_SIZE 8
|
||||
#define MAX_SOCKETS 32
|
||||
|
||||
|
@ -99,6 +97,7 @@ static int discovered_sock_create(struct discover_t *ds, uint32_t local_ip,
|
|||
/* Set nonblocking */
|
||||
if (soblock(sock, 0) < 0) {
|
||||
verb("can't set nonblocking\n");
|
||||
close(sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -243,7 +242,7 @@ int nerdjack_detect(char *ipAddress)
|
|||
int buffer_length;
|
||||
char buffer[200];
|
||||
char incomingData[10];
|
||||
unsigned int lFromLen;
|
||||
socklen_t lFromLen;
|
||||
|
||||
sprintf(buffer, "TEST");
|
||||
buffer_length = strlen(buffer) + 1;
|
||||
|
@ -312,7 +311,7 @@ int nerdjack_detect(char *ipAddress)
|
|||
recvfrom_timeout(receivesock, incomingData, sizeof(incomingData), 0,
|
||||
(struct sockaddr *)&sFromAddr, &lFromLen,
|
||||
&(struct timeval) {
|
||||
.tv_sec = NERDJACK_TIMEOUT})) {
|
||||
.tv_sec = TIMEOUT})) {
|
||||
close(receivesock);
|
||||
return -1;
|
||||
}
|
||||
|
@ -341,14 +340,14 @@ int nerd_get_version(const char *address)
|
|||
|
||||
/* Send request */
|
||||
ret = send_all_timeout(fd_command, "VERS", 4, 0, &(struct timeval) {
|
||||
.tv_sec = NERDJACK_TIMEOUT});
|
||||
.tv_sec = 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});
|
||||
.tv_sec = TIMEOUT});
|
||||
|
||||
nerd_close_conn(fd_command);
|
||||
|
||||
|
@ -380,14 +379,14 @@ int nerd_send_command(const char *address, void *command, int length)
|
|||
|
||||
/* Send request */
|
||||
ret = send_all_timeout(fd_command, command, length, 0, &(struct timeval) {
|
||||
.tv_sec = NERDJACK_TIMEOUT});
|
||||
.tv_sec = TIMEOUT});
|
||||
if (ret < 0 || ret != length) {
|
||||
verb("short send %d\n", (int)ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = recv_all_timeout(fd_command, buf, 3, 0, &(struct timeval) {
|
||||
.tv_sec = NERDJACK_TIMEOUT});
|
||||
.tv_sec = TIMEOUT});
|
||||
|
||||
nerd_close_conn(fd_command);
|
||||
|
||||
|
|
|
@ -27,7 +27,8 @@
|
|||
/* Packet structure used in message to start sampling on NerdJack */
|
||||
typedef struct __attribute__ ((__packed__)) {
|
||||
char word[4];
|
||||
unsigned long period;
|
||||
unsigned int period; //CHANGED FROM TYPE LONG. With 64 bit compilers longs are 8 bytes and nerdjack expects a 4 byte value
|
||||
//since the int type is 4 bytes this works but should be changed to use defined datatypes rather than rely on compiler data types
|
||||
unsigned short channelbit;
|
||||
unsigned char precision;
|
||||
unsigned char prescaler;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#ifdef __WIN32__
|
||||
# include <winsock2.h>
|
||||
# include <ws2tcpip.h>
|
||||
# define socklen_t unsigned int
|
||||
# define socklen_t int
|
||||
# define in_addr_t uint32_t
|
||||
# define in_port_t uint16_t
|
||||
# include <windows.h>
|
||||
|
|
154
ue9.c
154
ue9.c
|
@ -24,8 +24,7 @@
|
|||
#include "ue9error.h"
|
||||
#include "util.h"
|
||||
#include "netutil.h"
|
||||
|
||||
#define UE9_TIMEOUT 5 /* Timeout for connect/send/recv, in seconds */
|
||||
#include "ethstream.h"
|
||||
|
||||
/* Fill checksums in data buffers, with "normal" checksum format */
|
||||
void ue9_checksum_normal(uint8_t * buffer, size_t len)
|
||||
|
@ -117,10 +116,24 @@ int ue9_verify_extended(uint8_t * buffer, size_t len)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Temperature conversion. If calib is NULL, use uncalibrated conversions. */
|
||||
double ue9_binary_to_temperature(struct ue9Calibration *calib, uint16_t data)
|
||||
{
|
||||
double slope;
|
||||
|
||||
if (calib == NULL) {
|
||||
slope = 0.012683;
|
||||
} else {
|
||||
slope = calib->tempSlope;
|
||||
}
|
||||
|
||||
return data * slope; /* output is in Kelvin */
|
||||
}
|
||||
|
||||
/* 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)
|
||||
int gain, uint8_t resolution, uint16_t data)
|
||||
{
|
||||
double slope = 0, offset;
|
||||
|
||||
|
@ -134,13 +147,27 @@ ue9_binary_to_analog(struct ue9Calibration *calib,
|
|||
}
|
||||
|
||||
if (resolution < 18) {
|
||||
if (gain <= 3) {
|
||||
slope = calib->unipolarSlope[gain];
|
||||
offset = calib->unipolarOffset[gain];
|
||||
} else if (gain == 8) {
|
||||
slope = calib->bipolarSlope;
|
||||
offset = calib->bipolarOffset;
|
||||
}
|
||||
switch (gain) {
|
||||
case 1:
|
||||
slope = calib->unipolarSlope[0];
|
||||
offset = calib->unipolarOffset[0];
|
||||
break;
|
||||
case 2:
|
||||
slope = calib->unipolarSlope[1];
|
||||
offset = calib->unipolarOffset[1];
|
||||
break;
|
||||
case 4:
|
||||
slope = calib->unipolarSlope[2];
|
||||
offset = calib->unipolarOffset[2];
|
||||
break;
|
||||
case 8:
|
||||
slope = calib->unipolarSlope[3];
|
||||
offset = calib->unipolarOffset[3];
|
||||
break;
|
||||
default:
|
||||
slope = calib->bipolarSlope;
|
||||
offset = calib->bipolarOffset;
|
||||
}
|
||||
} else {
|
||||
if (gain == 0) {
|
||||
slope = calib->hiResUnipolarSlope;
|
||||
|
@ -183,7 +210,7 @@ int ue9_command(int fd, uint8_t * out, uint8_t * in, int inlen)
|
|||
|
||||
/* Send request */
|
||||
ret = send_all_timeout(fd, out, outlen, 0, &(struct timeval) {
|
||||
.tv_sec = UE9_TIMEOUT});
|
||||
.tv_sec = TIMEOUT});
|
||||
if (ret < 0 || ret != outlen) {
|
||||
verb("short send %d\n", (int)ret);
|
||||
return -1;
|
||||
|
@ -197,7 +224,7 @@ int ue9_command(int fd, uint8_t * out, uint8_t * in, int inlen)
|
|||
|
||||
/* Receive result */
|
||||
ret = recv_all_timeout(fd, in, inlen, 0, &(struct timeval) {
|
||||
.tv_sec = UE9_TIMEOUT});
|
||||
.tv_sec = TIMEOUT});
|
||||
if (ret < 0 || ret != inlen) {
|
||||
verb("short recv %d\n", (int)ret);
|
||||
return -1;
|
||||
|
@ -375,6 +402,7 @@ int ue9_open(const char *host, int port)
|
|||
/* Set nonblocking */
|
||||
if (soblock(fd, 0) < 0) {
|
||||
verb("can't set nonblocking\n");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -390,6 +418,7 @@ int ue9_open(const char *host, int port)
|
|||
he = gethostbyname(host);
|
||||
if (he == NULL) {
|
||||
verb("gethostbyname(\"%s\") failed\n", host);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
address.sin_addr = *((struct in_addr *)he->h_addr);
|
||||
|
@ -399,11 +428,14 @@ int ue9_open(const char *host, int port)
|
|||
/* Connect */
|
||||
if (connect_timeout(fd, (struct sockaddr *)&address, sizeof(address),
|
||||
&(struct timeval) {
|
||||
.tv_sec = UE9_TIMEOUT}) < 0) {
|
||||
.tv_sec = TIMEOUT}) < 0) {
|
||||
verb("connection to %s:%d failed: %s\n",
|
||||
inet_ntoa(address.sin_addr), port, compat_strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
debug("Connected to port %d\n", port);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
@ -587,13 +619,81 @@ ue9_streamconfig_simple(int fd, int *channel_list, int channel_count,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Timer configuration */
|
||||
int ue9_timer_config(int fd, int *mode_list, int mode_count, int divisor)
|
||||
/* Stream configuration, each Analog Input channel can have its own gain. */
|
||||
int
|
||||
ue9_streamconfig(int fd, int *channel_list, int channel_count,
|
||||
uint8_t scanconfig, uint16_t scaninterval, int *gain_list, int gain_count)
|
||||
{
|
||||
int i;
|
||||
uint8_t buf[256];
|
||||
|
||||
if (mode_count < 0 || mode_count > 6) {
|
||||
/* Set up StreamConfig command with channels and scan options */
|
||||
buf[1] = 0xF8; /* Extended command */
|
||||
buf[2] = channel_count + 3; /* Command data words */
|
||||
buf[3] = 0x11; /* StreamConfig */
|
||||
buf[6] = channel_count; /* Number of channels */
|
||||
buf[7] = 12; /* Bit resolution */
|
||||
buf[8] = 0; /* Extra settling time */
|
||||
buf[9] = scanconfig;
|
||||
buf[10] = scaninterval & 0xff;
|
||||
buf[11] = scaninterval >> 8;
|
||||
|
||||
for (i = 0; i < channel_count; i++) {
|
||||
buf[12 + 2 * i] = channel_list[i]; /* Channel number */
|
||||
if (i < gain_count) {
|
||||
switch (gain_list[i]) {
|
||||
case 0:
|
||||
buf[13 + 2 * i] = UE9_BIPOLAR_GAIN1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
buf[13 + 2 * i] = UE9_UNIPOLAR_GAIN1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
buf[13 + 2 * i] = UE9_UNIPOLAR_GAIN2;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
buf[13 + 2 * i] = UE9_UNIPOLAR_GAIN4;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
buf[13 + 2 * i] = UE9_UNIPOLAR_GAIN8;
|
||||
break;
|
||||
|
||||
default:
|
||||
buf[13 + 2 * i] = UE9_BIPOLAR_GAIN1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buf[13 + 2 * i] = UE9_BIPOLAR_GAIN1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Send StreamConfig */
|
||||
if (ue9_command(fd, buf, buf, 8) < 0) {
|
||||
debug("command failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buf[6] != 0) {
|
||||
verb("returned error %s\n", ue9_error(buf[6]));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Timer configuration */
|
||||
int ue9_timer_config(int fd, int *mode_list, int *value_list, int count, int divisor)
|
||||
{
|
||||
int i;
|
||||
uint8_t buf[256];
|
||||
|
||||
if (count < 0 || count > 6) {
|
||||
verb("invalid count\n");
|
||||
return -1;
|
||||
}
|
||||
|
@ -603,17 +703,21 @@ int ue9_timer_config(int fd, int *mode_list, int mode_count, int divisor)
|
|||
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[7] = 0x80 | 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)
|
||||
if (i < count) {
|
||||
buf[10 + 3 * i] = mode_list[i];
|
||||
else
|
||||
buf[11 + 3 * i] = value_list[i] & 0xff;
|
||||
buf[12 + 3 * i] = value_list[i] >> 8;
|
||||
}
|
||||
else {
|
||||
buf[10 + 3 * i] = 0;
|
||||
buf[11 + 3 * i] = 0;
|
||||
buf[12 + 3 * i] = 0;
|
||||
buf[11 + 3 * i] = 0;
|
||||
buf[12 + 3 * i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
buf[28] = 0;
|
||||
|
@ -638,7 +742,7 @@ 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
|
||||
negative, stops reading and returns 0. Returns < 0 on error. */
|
||||
int
|
||||
ue9_stream_data(int fd, int channels, ue9_stream_cb_t callback, void *context)
|
||||
ue9_stream_data(int fd, int channels, int *channel_list, int gain_count, int *gain_list, ue9_stream_cb_t callback, void *context)
|
||||
{
|
||||
int ret;
|
||||
uint8_t buf[46];
|
||||
|
@ -650,7 +754,7 @@ ue9_stream_data(int fd, int channels, ue9_stream_cb_t callback, void *context)
|
|||
for (;;) {
|
||||
/* Receive data */
|
||||
ret = recv_all_timeout(fd, buf, 46, 0, &(struct timeval) {
|
||||
.tv_sec = UE9_TIMEOUT});
|
||||
.tv_sec = TIMEOUT});
|
||||
|
||||
/* Verify packet format */
|
||||
if (ret != 46) {
|
||||
|
@ -707,7 +811,7 @@ ue9_stream_data(int fd, int channels, ue9_stream_cb_t callback, void *context)
|
|||
|
||||
/* Received a full scan, send to callback */
|
||||
channel = 0;
|
||||
if ((*callback) (channels, data, context) < 0) {
|
||||
if ((*callback) (channels, channel_list, gain_count, gain_list, data, context) < 0) {
|
||||
/* We're done */
|
||||
return 0;
|
||||
}
|
||||
|
@ -717,6 +821,6 @@ ue9_stream_data(int fd, int channels, ue9_stream_cb_t callback, void *context)
|
|||
|
||||
/*
|
||||
Local variables:
|
||||
c-basic-offset: 2
|
||||
c-basic-offset: 8
|
||||
End:
|
||||
*/
|
||||
|
|
18
ue9.h
18
ue9.h
|
@ -68,6 +68,7 @@ struct ue9ControlConfig {
|
|||
uint16_t dac1;
|
||||
};
|
||||
|
||||
/* These are correct! 0, 1, 2, 3, 8 */
|
||||
#define UE9_UNIPOLAR_GAIN1 0x00
|
||||
#define UE9_UNIPOLAR_GAIN2 0x01
|
||||
#define UE9_UNIPOLAR_GAIN4 0x02
|
||||
|
@ -76,6 +77,7 @@ struct ue9ControlConfig {
|
|||
|
||||
#define UE9_MAX_CHANNEL_COUNT 128
|
||||
#define UE9_MAX_CHANNEL 255
|
||||
#define UE9_MAX_ANALOG_CHANNEL 13
|
||||
#define UE9_TIMERS 6
|
||||
|
||||
/* Fill checksums in data buffers */
|
||||
|
@ -103,7 +105,10 @@ int ue9_get_control_config(int fd, struct ue9ControlConfig *config);
|
|||
|
||||
/* 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);
|
||||
int gain, uint8_t resolution, uint16_t data);
|
||||
|
||||
/* Temperature conversion. If calib is NULL, use uncalibrated conversions. */
|
||||
double ue9_binary_to_temperature(struct ue9Calibration *calib, uint16_t data);
|
||||
|
||||
/* Compute scanrate based on the provided values. */
|
||||
double ue9_compute_rate(uint8_t scanconfig, uint16_t scaninterval);
|
||||
|
@ -133,14 +138,19 @@ int ue9_command(int fd, uint8_t * out, uint8_t * in, int inlen);
|
|||
int ue9_streamconfig_simple(int fd, int *channel_list, int channel_count,
|
||||
uint8_t scanconfig, uint16_t scaninterval,
|
||||
uint8_t gain);
|
||||
|
||||
/* Stream configuration, each Analog Input channel can have its own gain. */
|
||||
int ue9_streamconfig(int fd, int *channel_list, int channel_count,
|
||||
uint8_t scanconfig, uint16_t scaninterval,
|
||||
int *gain_list, int gain_count);
|
||||
|
||||
/* Timer configuration */
|
||||
int ue9_timer_config(int fd, int *mode_list, int mode_count, int divisor);
|
||||
int ue9_timer_config(int fd, int *mode_list, int *value_list, int count, int divisor);
|
||||
|
||||
/* Stream data and pass it to the data callback. If callback returns
|
||||
negative, stops reading and returns 0. Returns < 0 on error. */
|
||||
typedef int (*ue9_stream_cb_t) (int channels, uint16_t * data, void *context);
|
||||
int ue9_stream_data(int fd, int channels,
|
||||
typedef int (*ue9_stream_cb_t) (int channels, int *channel_list, int gain_count, int *gain_list, uint16_t * data, void *context);
|
||||
int ue9_stream_data(int fd, int channels, int *channel_list, int gain_count, int *gain_list,
|
||||
ue9_stream_cb_t callback, void *context);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue
Block a user